From 5cb044ef941ae173e5e5ec7d57f692850b7f2c67 Mon Sep 17 00:00:00 2001 From: Eduardo Robles Elvira Date: Mon, 3 Jan 2022 11:35:17 +0100 Subject: [PATCH 01/14] initial work to support suspend and resume features --- .../0048_authevent_allowed_statuses.py | 27 ++++++ authapi/api/models.py | 19 +++- authapi/api/tasks.py | 16 ++-- authapi/api/urls.py | 2 +- authapi/api/views.py | 91 +++++++++++++------ authapi/authapi/celery.py | 7 +- 6 files changed, 122 insertions(+), 40 deletions(-) create mode 100644 authapi/api/migrations/0048_authevent_allowed_statuses.py diff --git a/authapi/api/migrations/0048_authevent_allowed_statuses.py b/authapi/api/migrations/0048_authevent_allowed_statuses.py new file mode 100644 index 00000000..4ec997ea --- /dev/null +++ b/authapi/api/migrations/0048_authevent_allowed_statuses.py @@ -0,0 +1,27 @@ +# Generated by Edulix on 2021-12-30 11:00 + +from django.db import migrations, models + +class Migration(migrations.Migration): + dependencies = [ + ('api', '0047_userdata_use_generated_auth_code'), + ] + + operations = [ + migrations.AlterField( + model_name='authevent', + name='status', + field=models.CharField( + default='notstarted', + max_length=15, + choices=[ + ('notstarted', 'not-started'), + ('started', 'started'), + ('stopped', 'stopped'), + ('resumed', 'resumed'), + ('suspended', 'suspended') + ] + ), + preserve_default=True + ) + ] diff --git a/authapi/api/models.py b/authapi/api/models.py index 4f867f50..064b567f 100644 --- a/authapi/api/models.py +++ b/authapi/api/models.py @@ -46,6 +46,8 @@ ('notstarted', 'not-started'), ('started', 'started'), ('stopped', 'stopped'), + ('resumed', 'resumed'), + ('suspended', 'suspended') ) AE_TALLY_STATUSES = ( @@ -262,19 +264,30 @@ class AuthEvent(models.Model): use case is that each AuthEvent corresponds with an Election, in principle it could be used for any kind of authentication and authorization event. ''' + NOT_STARTED = "notstarted" + STARTED = "started" + STOPPED = "stopped" + SUSPENDED = "suspended" + RESUMED = "resumed" + PENDING = "pending" + SUCCESS = "success" auth_method = models.CharField(max_length=255) census = models.CharField(max_length=5, choices=CENSUS, default="close") auth_method_config = JSONField() extra_fields = JSONField(blank=True, null=True) - status = models.CharField(max_length=15, choices=AE_STATUSES, default="notstarted") - + status = models.CharField( + max_length=15, + choices=AE_STATUSES, + default=NOT_STARTED + ) + # used by authapi_celery to know what tallies to launch, and to serialize # those launches one by one. set/get with (s|g)et_tally_status api calls tally_status = models.CharField( max_length=15, choices=AE_TALLY_STATUSES, - default="notstarted" + default=NOT_STARTED ) created = models.DateTimeField(auto_now_add=True) diff --git a/authapi/api/tasks.py b/authapi/api/tasks.py index 6e83f30f..2a8d52b8 100644 --- a/authapi/api/tasks.py +++ b/authapi/api/tasks.py @@ -132,7 +132,7 @@ def launch_tally(auth_event): agora_elections_request.status_code, agora_elections_request.text ) - auth_event.tally_status = 'notstarted' + auth_event.tally_status = AuthEvent.STARTED auth_event.save() # log the action @@ -165,7 +165,7 @@ def launch_tally(auth_event): agora_elections_request.status_code, agora_elections_request.text ) - auth_event.tally_status = 'started' + auth_event.tally_status = AuthEvent.STARTED auth_event.save() # log the action @@ -218,7 +218,7 @@ def launch_virtual_tally(auth_event): agora_elections_request.status_code, agora_elections_request.text ) - auth_event.tally_status = 'notstarted' + auth_event.tally_status = AuthEvent.NOT_STARTED auth_event.save() # log the action @@ -246,7 +246,7 @@ def launch_virtual_tally(auth_event): agora_elections_request.status_code, agora_elections_request.text ) - auth_event.tally_status = 'success' + auth_event.tally_status = AuthEvent.SUCCESS auth_event.save() # log the action @@ -319,7 +319,7 @@ def update_tally_status(auth_event): election_state = updated_election['payload']['state'] if election_state in ['tally_error', 'stopped', 'started']: - auth_event.tally_status = 'notstarted' + auth_event.tally_status = AuthEvent.NOT_STARTED auth_event.save() # log the action @@ -334,7 +334,7 @@ def update_tally_status(auth_event): ) action.save() elif election_state in ['tally_ok', 'results_ok', 'results_pub']: - auth_event.tally_status = 'success' + auth_event.tally_status = AuthEvent.SUCCESS auth_event.save() # log the action @@ -383,7 +383,7 @@ def process_tallies(): ''' logger.info('\n\ntasks.process_tallies') tallying_events = AuthEvent.objects\ - .filter(tally_status='started')\ + .filter(tally_status=AuthEvent.STARTED)\ .order_by('id') # Review which tallies have succeeded and update corresponding AuthEvents @@ -391,7 +391,7 @@ def process_tallies(): update_tally_status(auth_event) pending_events = AuthEvent.objects\ - .filter(tally_status='pending')\ + .filter(tally_status=AuthEvent.AuthEvent.STARTED .order_by('id') logger.info( diff --git a/authapi/api/urls.py b/authapi/api/urls.py index b6833b5a..d36a6496 100644 --- a/authapi/api/urls.py +++ b/authapi/api/urls.py @@ -54,7 +54,7 @@ url(r'^auth-event/(?P\d+)/resend_auth_code/$', views.resend_auth_code, name='resend_auth_code'), url(r'^auth-event/(?P\d+)/census/send_auth/$', views.census_send_auth, name='census_send_auth'), url(r'^auth-event/(?P\d+)/census/reset-voter/$', views.census_reset_voter, name='census_reset_voter'), - url(r'^auth-event/(?P\d+)/(?P(notstarted|started|stopped))/$', views.ae_status, name='ae_status'), + url(r'^auth-event/(?P\d+)/(?P(notstarted|started|stopped|suspended|resumed))/$', views.ae_status, name='ae_status'), url(r'^auth-event/module/$', views.authevent_module, name='authevent_module'), url(r'^auth-event/module/(?P[-\w]+)/$', views.authevent_module, name='authevent_module'), diff --git a/authapi/api/views.py b/authapi/api/views.py index 17068bae..45349321 100644 --- a/authapi/api/views.py +++ b/authapi/api/views.py @@ -599,7 +599,7 @@ class Authenticate(View): def post(self, request, pk): try: - e = get_object_or_404(AuthEvent, pk=pk, status="started") + e = get_object_or_404(AuthEvent, pk=pk, status=AuthEvent.STARTED) except: return json_response(status=400, error_codename=ErrorCodes.BAD_REQUEST) @@ -674,7 +674,10 @@ def post(self, request, pk): e = get_object_or_404( AuthEvent, pk=pk, - status__in=['notstarted', 'started'], + status__in=[ + AuthEvent.NOT_STARTED, + AuthEvent.STARTED + ], allow_public_census_query=True) try: @@ -940,12 +943,18 @@ def post(self, request, pk): if "match_census_on_registration" in f and f['match_census_on_registration'] ] - if (e.census == 'close') and (len(match_census_on_registration) == 0 or e.status != 'started'): + if ( + (e.census == 'close') and + ( + len(match_census_on_registration) == 0 or + e.status != AuthEvent.STARTED + ) + ): return json_response( status=400, error_codename="REGISTER_IS_DISABLED") # registration is closed - if e.census == 'open' and e.status != 'started': + if e.census == 'open' and e.status != AuthEvent.STARTED: return json_response( status=400, error_codename="AUTH_EVENT_NOT_STARTED") @@ -981,7 +990,7 @@ def post(self, request, pk): status=400, error_codename="AUTH_EVENT_NOT_STARTED") # registration is closed - if (auth_event.census == 'open' or auth_event.check_allow_user_resend()) and auth_event.status != 'started': + if (auth_event.census == 'open' or auth_event.check_allow_user_resend()) and auth_event.status != AuthEvent.STARTED: return json_response( status=400, error_codename="AUTH_EVENT_NOT_STARTED") @@ -1017,7 +1026,9 @@ def post(self, request, pk, status): alt = dict( notstarted="notstarted", started='start', - stopped='stop' + stopped='stop', + suspended='suspend', + resumed='resume' )[status] permission_required(request.user, 'AuthEvent', ['edit', alt], pk) @@ -1056,7 +1067,7 @@ def post(self, request, pk, status): action.save() # update in agora-elections - if alt in ['start', 'stop']: + if alt in ['start', 'stop', 'suspend', 'resume']: for callback_base in settings.AGORA_ELECTIONS_BASE: callback_url = "%s/api/election/%s/%s" % ( callback_base, @@ -1368,7 +1379,11 @@ def post(request, pk): ''' from authmethods.utils import verify_children_election_info permission_required(request.user, 'AuthEvent', 'edit', pk) - auth_event = get_object_or_404(AuthEvent, pk=pk, status='notstarted') + auth_event = get_object_or_404( + AuthEvent, + pk=pk, + status=AuthEvent.NOT_STARTED + ) try: req = parse_json_request(request) except: @@ -1448,10 +1463,18 @@ def post(request, pk=None): requested_id = req.get('id', None) election_exists = False if requested_id and isinstance(requested_id, int): - count_existing_elections = AuthEvent.objects.filter(pk=requested_id).count() - if count_existing_elections != 0: - permission_required(request.user, 'AuthEvent', 'edit', requested_id) - election_exists = True + count_existing_elections = AuthEvent\ + .objects\ + .filter(pk=requested_id)\ + .count() + if count_existing_elections != 0: + permission_required( + request.user, + 'AuthEvent', + 'edit', + requested_id + ) + election_exists = True else: requested_id = None @@ -1700,8 +1723,13 @@ def post(request, pk=None): ae.save() # TODO: Problem if object_id is None, change None by 0 - acl = get_object_or_404(ACL, user=request.user.userdata, - perm='edit', object_type='AuthEvent', object_id=ae.pk) + acl = get_object_or_404( + ACL, + user=request.user.userdata, + perm='edit', + object_type='AuthEvent', + object_id=ae.pk + ) action = Action( executer=request.user, @@ -1996,8 +2024,11 @@ def post(self, request): class UserAuthEvent(View): def get(self, request): ''' Get ids auth-event of request user. ''' - acls = ACL.objects.filter(user=request.user.pk, object_type='AuthEvent', - perm='edit') + acls = ACL.objects.filter( + user=request.user.pk, + object_type='AuthEvent', + perm='edit' + ) ae_ids = [] for acl in acls: ae_ids.append(acl.object_id) @@ -2278,7 +2309,12 @@ class BallotBoxView(View): ''' def post(self, request, pk): - permission_required(request.user, 'AuthEvent', ['edit', 'add-ballot-boxes'], pk) + permission_required( + request.user, + 'AuthEvent', + ['edit', 'add-ballot-boxes'], + pk + ) # parse input try: @@ -2296,8 +2332,8 @@ def post(self, request, pk): if not new_ballot_box.is_valid() or not auth_event.has_ballot_boxes: return json_response( status=400, - error_codename=ErrorCodes.BAD_REQUEST) - + error_codename=ErrorCodes.BAD_REQUEST + ) # try to create new object in the db. might fail if bb already exists try: @@ -2462,7 +2498,7 @@ def post(self, request, pk, ballot_box_pk): BallotBox, pk=ballot_box_pk, auth_event__pk=pk, - auth_event__status="stopped" + auth_event__status=AuthEvent.STOPPED ) # require extra permissions to override tally sheet @@ -2631,7 +2667,7 @@ def post(self, request, pk): # cannot launch tally on an election whose voting period is still open # or has not even started. - if auth_event.status != 'stopped': + if auth_event.status != AuthEvent.STOPPED: return json_response( status=400, error_codename=ErrorCodes.BAD_REQUEST @@ -2693,18 +2729,21 @@ def post(self, request, pk): # set the pending status accordingly for auth_event_to_tally in auth_events: if ( - auth_event_to_tally.tally_status == 'notstarted' or + auth_event_to_tally.tally_status == AuthEvent.NOT_STARTED or ( - auth_event_to_tally.tally_status == 'pending' and + auth_event_to_tally.tally_status == AuthEvent.PENDING and force_tally in ['force-untallied', 'force-all'] ) or ( - auth_event_to_tally.tally_status in ['started', 'success'] and + auth_event_to_tally.tally_status in [ + AuthEvent.STARTED, + AuthEvent.SUCCESS + ] and force_tally in ['force-all'] ) ): # set tally status to pending previous_tally_status = auth_event_to_tally.tally_status - auth_event_to_tally.tally_status = 'pending' + auth_event_to_tally.tally_status = AuthEvent.PENDING auth_event_to_tally.save() # log the action @@ -2717,7 +2756,7 @@ def post(self, request, pk): auth_event=auth_event_to_tally.pk, previous_tally_status=previous_tally_status, force_tally=force_tally, - forced=(previous_tally_status != 'notstarted') + forced=(previous_tally_status != AuthEvent.NOT_STARTED) ) ) action.save() diff --git a/authapi/authapi/celery.py b/authapi/authapi/celery.py index 9c7414b5..e0fbca3d 100644 --- a/authapi/authapi/celery.py +++ b/authapi/authapi/celery.py @@ -21,8 +21,11 @@ def reset_tallies_task(sender=None, conf=None, **kwargs): from api.models import AuthEvent AuthEvent\ .objects\ - .filter(tally_status__in=['pending','started'])\ - .update(tally_status='notstarted') + .filter(tally_status__in=[ + AuthEvent.PENDING, + AuthEvent.STARTED + ])\ + .update(tally_status=AuthEvent.NOT_STARTED) # set the default Django settings module for the 'celery' program. os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'authapi.settings') From 238632fa9e4720fd8d76d16c4ee286d148cd8db2 Mon Sep 17 00:00:00 2001 From: Eduardo Robles Elvira Date: Mon, 3 Jan 2022 16:38:01 +0100 Subject: [PATCH 02/14] fixing typos --- authapi/api/tasks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/authapi/api/tasks.py b/authapi/api/tasks.py index 2a8d52b8..bb429a7f 100644 --- a/authapi/api/tasks.py +++ b/authapi/api/tasks.py @@ -391,7 +391,7 @@ def process_tallies(): update_tally_status(auth_event) pending_events = AuthEvent.objects\ - .filter(tally_status=AuthEvent.AuthEvent.STARTED + .filter(tally_status=AuthEvent.AuthEvent.STARTED)\ .order_by('id') logger.info( From 5ebcb3c28fafe9d8b67e7acd711beb6190a46b9c Mon Sep 17 00:00:00 2001 From: Eduardo Robles Elvira Date: Mon, 3 Jan 2022 19:01:34 +0100 Subject: [PATCH 03/14] adding enforce_state_controls support to authapi --- authapi/api/views.py | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/authapi/api/views.py b/authapi/api/views.py index 45349321..58206990 100644 --- a/authapi/api/views.py +++ b/authapi/api/views.py @@ -1038,17 +1038,41 @@ def post(self, request, pk, status): children_ids = main_auth_event.children_election_info['natural_order'] else: children_ids = [] - + auth_events = AuthEvent.objects.filter( Q(pk=pk) | Q(parent_id=pk) | Q(parent_id__in=children_ids) ) - + for auth_event in auth_events: # update AuthEvent - if auth_event.status != status: + # enforce state transitions make sense + if settings.ENFORCE_STATE_CONTROLS: + if ( + status == AuthEvent.STARTED and + auth_event.status != AuthEvent.NOT_STARTED + ) or ( + status != AuthEvent.SUSPENDED and + auth_event.status != AuthEvent.STARTED + ) or ( + status != AuthEvent.RESUMED and + auth_event.status != AuthEvent.SUSPENDED + ) or ( + status != AuthEvent.PENDING and + auth_event.status != AuthEvent.STOPPED + ) or ( + status != AuthEvent.SUCCESS and + auth_event.status != AuthEvent.PENDING + ): + return json_response( + status=400, + next_status=status, + current_status=auth_event.status, + error_codename="INVALID_STATUS_TRANSITION" + ) + auth_event.status = status auth_event.save() From e2188ecbd9a32de7425d3770522d2bd6b8911091 Mon Sep 17 00:00:00 2001 From: Eduardo Robles Elvira Date: Mon, 3 Jan 2022 19:08:35 +0100 Subject: [PATCH 04/14] adding missing ENFORCE_STATE_CONTROLS in test_settings --- authapi/authapi/test_settings.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/authapi/authapi/test_settings.py b/authapi/authapi/test_settings.py index 686806bd..485a3400 100644 --- a/authapi/authapi/test_settings.py +++ b/authapi/authapi/test_settings.py @@ -164,6 +164,8 @@ class CeleryConfig: USE_TZ = True +ENFORCE_STATE_CONTROLS = False + # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/1.7/howto/static-files/ From 48846767b1b8a3af61361967a0f7c076b8e5abf4 Mon Sep 17 00:00:00 2001 From: Eduardo Robles Elvira Date: Wed, 5 Jan 2022 12:28:23 +0100 Subject: [PATCH 05/14] invalid status transition --- authapi/api/views.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/authapi/api/views.py b/authapi/api/views.py index 58206990..b6f82b94 100644 --- a/authapi/api/views.py +++ b/authapi/api/views.py @@ -1068,8 +1068,10 @@ def post(self, request, pk, status): ): return json_response( status=400, - next_status=status, - current_status=auth_event.status, + data=dict( + next_status=status, + current_status=auth_event.status + ), error_codename="INVALID_STATUS_TRANSITION" ) From bc35f9d82193129beea3563161458921dd141922 Mon Sep 17 00:00:00 2001 From: Eduardo Robles Elvira Date: Wed, 5 Jan 2022 12:34:17 +0100 Subject: [PATCH 06/14] allowing setting data in error output --- authapi/utils.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/authapi/utils.py b/authapi/utils.py index 1365a18e..88ca76c6 100644 --- a/authapi/utils.py +++ b/authapi/utils.py @@ -86,8 +86,16 @@ def json_response(data=None, status=200, message="", field=None, error_codename= error_codename = ErrorCodes.GENERAL_ERROR if isinstance(error_codename, ErrorCodes): error_codename = error_codename.value - data = dict(message=message, field=field, error_codename=error_codename) - jsondata = json.dumps(data) + error_data = dict( + message=message, + field=field, + error_codename=error_codename + ) + if data is not None: + error_data['data'] = data + jsondata = json.dumps(error_data) + else: + jsondata = json.dumps(data) return HttpResponse(jsondata, status=status, content_type='application/json') From c6e7acad6b60022716b744404a35053d4072a5dc Mon Sep 17 00:00:00 2001 From: Eduardo Robles Elvira Date: Sun, 9 Jan 2022 08:34:43 +0100 Subject: [PATCH 07/14] fixing time_limit --- authapi/authapi/settings.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/authapi/authapi/settings.py b/authapi/authapi/settings.py index 5efe7b52..43403ef6 100644 --- a/authapi/authapi/settings.py +++ b/authapi/authapi/settings.py @@ -39,14 +39,19 @@ class CeleryConfig: 'args': [], 'options': { 'expires': 10 - }, - 'time_limit': 10 - }, + } + } } result_backend = 'django-db' CELERY_CONFIG = CeleryConfig +CELERY_ANNOTATIONS = { + 'tasks.process_tallies': { + 'time_limit': 10 + } +} + # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/1.7/howto/deployment/checklist/ From af4e2679237d05437990abfdd23c374c35ddaf22 Mon Sep 17 00:00:00 2001 From: Eduardo Robles Elvira Date: Sun, 9 Jan 2022 08:37:22 +0100 Subject: [PATCH 08/14] fixing process_tallies --- authapi/api/tasks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/authapi/api/tasks.py b/authapi/api/tasks.py index bb429a7f..e6d23ed8 100644 --- a/authapi/api/tasks.py +++ b/authapi/api/tasks.py @@ -391,7 +391,7 @@ def process_tallies(): update_tally_status(auth_event) pending_events = AuthEvent.objects\ - .filter(tally_status=AuthEvent.AuthEvent.STARTED)\ + .filter(tally_status=AuthEvent.STARTED)\ .order_by('id') logger.info( From fbecb17f4c62b3509cace22100a07c7b387aaaa6 Mon Sep 17 00:00:00 2001 From: Eduardo Robles Elvira Date: Sun, 9 Jan 2022 08:59:22 +0100 Subject: [PATCH 09/14] stopping tally_status if stopping auth_event --- authapi/api/views.py | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/authapi/api/views.py b/authapi/api/views.py index b6f82b94..f6ddc87c 100644 --- a/authapi/api/views.py +++ b/authapi/api/views.py @@ -1054,17 +1054,23 @@ def post(self, request, pk, status): status == AuthEvent.STARTED and auth_event.status != AuthEvent.NOT_STARTED ) or ( - status != AuthEvent.SUSPENDED and + status == AuthEvent.SUSPENDED and auth_event.status != AuthEvent.STARTED ) or ( - status != AuthEvent.RESUMED and + status == AuthEvent.RESUMED and auth_event.status != AuthEvent.SUSPENDED ) or ( - status != AuthEvent.PENDING and - auth_event.status != AuthEvent.STOPPED + status == AuthEvent.PENDING and + auth_event.status != AuthEvent.STOPPED and + auth_event.status != AuthEvent.SUSPENDED ) or ( - status != AuthEvent.SUCCESS and + status == AuthEvent.SUCCESS and auth_event.status != AuthEvent.PENDING + ) or ( + status == AuthEvent.STOPPED and + auth_event.status != AuthEvent.STARTED and + auth_event.status != AuthEvent.RESUMED and + auth_event.status != AuthEvent.SUSPENDED ): return json_response( status=400, @@ -1131,6 +1137,15 @@ def post(self, request, pk, status): error_codename=ErrorCodes.GENERAL_ERROR ) + # if new status is stop and tally_status is pending, move it to + if ( + alt == 'stop' and + auth_event.tally_status != AuthEvent.STOPPED + ): + auth_event.tally_status = AuthEvent.STOPPED + auth_event.save() + + LOGGER.info(\ "AuthEventStatus.post\n"\ "agora_elections.callback_url '%r'\n"\ From a7741737c46f75add3202cd330e36e850dbf9e11 Mon Sep 17 00:00:00 2001 From: Eduardo Robles Elvira Date: Sun, 9 Jan 2022 09:00:38 +0100 Subject: [PATCH 10/14] do not allow to pending from suspended, first election needs to stop --- authapi/api/views.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/authapi/api/views.py b/authapi/api/views.py index f6ddc87c..7d486e7a 100644 --- a/authapi/api/views.py +++ b/authapi/api/views.py @@ -1061,8 +1061,7 @@ def post(self, request, pk, status): auth_event.status != AuthEvent.SUSPENDED ) or ( status == AuthEvent.PENDING and - auth_event.status != AuthEvent.STOPPED and - auth_event.status != AuthEvent.SUSPENDED + auth_event.status != AuthEvent.STOPPED ) or ( status == AuthEvent.SUCCESS and auth_event.status != AuthEvent.PENDING From 30ebf1fc8afdc72a71290a1f20a3bf277f1a2cb4 Mon Sep 17 00:00:00 2001 From: Eduardo Robles Elvira Date: Sun, 9 Jan 2022 09:03:53 +0100 Subject: [PATCH 11/14] fixing status name --- authapi/api/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/authapi/api/views.py b/authapi/api/views.py index 7d486e7a..ea8792ff 100644 --- a/authapi/api/views.py +++ b/authapi/api/views.py @@ -1139,9 +1139,9 @@ def post(self, request, pk, status): # if new status is stop and tally_status is pending, move it to if ( alt == 'stop' and - auth_event.tally_status != AuthEvent.STOPPED + auth_event.tally_status != AuthEvent.NOT_STARTED ): - auth_event.tally_status = AuthEvent.STOPPED + auth_event.tally_status = AuthEvent.NOT_STARTED auth_event.save() From 6b0f756983b3dd08d8ea02532a37d167b3d5d8a0 Mon Sep 17 00:00:00 2001 From: Eduardo Robles Elvira Date: Sun, 9 Jan 2022 09:07:03 +0100 Subject: [PATCH 12/14] fixing status ref --- authapi/api/tasks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/authapi/api/tasks.py b/authapi/api/tasks.py index e6d23ed8..af7d34da 100644 --- a/authapi/api/tasks.py +++ b/authapi/api/tasks.py @@ -391,7 +391,7 @@ def process_tallies(): update_tally_status(auth_event) pending_events = AuthEvent.objects\ - .filter(tally_status=AuthEvent.STARTED)\ + .filter(tally_status=AuthEvent.PENDING)\ .order_by('id') logger.info( From 4aeb253662aa26af3d5c2a8d0d53e6e6dafd0cc0 Mon Sep 17 00:00:00 2001 From: Eduardo Robles Elvira Date: Sun, 9 Jan 2022 10:04:57 +0100 Subject: [PATCH 13/14] adding some missing support for AuthEvent.RESUMED as it was AuthEvent.STARTED --- authapi/api/views.py | 38 ++++++++++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/authapi/api/views.py b/authapi/api/views.py index ea8792ff..cf3dadd7 100644 --- a/authapi/api/views.py +++ b/authapi/api/views.py @@ -599,7 +599,14 @@ class Authenticate(View): def post(self, request, pk): try: - e = get_object_or_404(AuthEvent, pk=pk, status=AuthEvent.STARTED) + e = get_object_or_404( + AuthEvent, + pk=pk, + status__in=[ + AuthEvent.STARTED, + AuthEvent.RESUMED + ] + ) except: return json_response(status=400, error_codename=ErrorCodes.BAD_REQUEST) @@ -676,7 +683,9 @@ def post(self, request, pk): pk=pk, status__in=[ AuthEvent.NOT_STARTED, - AuthEvent.STARTED + AuthEvent.STARTED, + AuthEvent.SUSPENDED, + AuthEvent.RESUMED ], allow_public_census_query=True) @@ -947,14 +956,21 @@ def post(self, request, pk): (e.census == 'close') and ( len(match_census_on_registration) == 0 or - e.status != AuthEvent.STARTED + ( + e.status != AuthEvent.STARTED and + e.status != AuthEvent.RESUMED + ) ) ): return json_response( status=400, error_codename="REGISTER_IS_DISABLED") # registration is closed - if e.census == 'open' and e.status != AuthEvent.STARTED: + if ( + e.census == 'open' and + e.status != AuthEvent.STARTED and + e.status != AuthEvent.RESUMED + ): return json_response( status=400, error_codename="AUTH_EVENT_NOT_STARTED") @@ -985,12 +1001,22 @@ class ResendAuthCode(View): def post(self, request, pk): auth_event = get_object_or_404(AuthEvent, pk=pk) - if (auth_event.census == 'close' and not auth_event.check_allow_user_resend()): + if ( + auth_event.census == 'close' and + not auth_event.check_allow_user_resend() + ): return json_response( status=400, error_codename="AUTH_EVENT_NOT_STARTED") # registration is closed - if (auth_event.census == 'open' or auth_event.check_allow_user_resend()) and auth_event.status != AuthEvent.STARTED: + if ( + ( + auth_event.census == 'open' or + auth_event.check_allow_user_resend() + ) and + auth_event.status != AuthEvent.STARTED and + auth_event.status != AuthEvent.RESUMED + ): return json_response( status=400, error_codename="AUTH_EVENT_NOT_STARTED") From 5ba6797f4e48c2b9ae2e749f801ccc8f8c23a965 Mon Sep 17 00:00:00 2001 From: Eduardo Robles Elvira Date: Sun, 9 Jan 2022 10:19:45 +0100 Subject: [PATCH 14/14] allowing to suspend from resumed --- authapi/api/views.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/authapi/api/views.py b/authapi/api/views.py index cf3dadd7..b6df3ea7 100644 --- a/authapi/api/views.py +++ b/authapi/api/views.py @@ -1081,7 +1081,8 @@ def post(self, request, pk, status): auth_event.status != AuthEvent.NOT_STARTED ) or ( status == AuthEvent.SUSPENDED and - auth_event.status != AuthEvent.STARTED + auth_event.status != AuthEvent.STARTED and + auth_event.status != AuthEvent.RESUMED ) or ( status == AuthEvent.RESUMED and auth_event.status != AuthEvent.SUSPENDED