From 73f48dc3e1ae86e5b7ed7565d59e35aa2caeecf4 Mon Sep 17 00:00:00 2001 From: Lee Richardson Date: Thu, 25 Aug 2022 16:22:40 -0400 Subject: [PATCH 1/8] validate the manifest_hash on ballot upload --- .../components/upload_ballots_component.py | 32 +++++++++++++++++-- .../services/ballot_upload_service.py | 8 +---- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/src/electionguard_gui/components/upload_ballots_component.py b/src/electionguard_gui/components/upload_ballots_component.py index d9cbeb0c..b2682585 100644 --- a/src/electionguard_gui/components/upload_ballots_component.py +++ b/src/electionguard_gui/components/upload_ballots_component.py @@ -1,5 +1,7 @@ from typing import Any from datetime import datetime +from electionguard.serialize import from_raw +from electionguard.ballot import SubmittedBallot import eel from electionguard_gui.components.component_base import ComponentBase from electionguard_gui.eel_utils import eel_fail, eel_success @@ -66,15 +68,41 @@ def upload_ballot( try: db = self._db_service.get_db() self._log.debug(f"adding ballot {file_name} to {ballot_upload_id}") + ballot = from_raw(SubmittedBallot, file_contents) + election = self._election_service.get(db, election_id) + context = election.get_context() + if context.manifest_hash != ballot.manifest_hash: + self._log.warn( + f"ballot '{ballot.object_id}' had a mismatched manifest hash. " + + f"Expected {context.manifest_hash}, got {ballot.manifest_hash}." + ) + return eel_fail( + "The uploaded ballot didn't match the encryption package for this election. " + + "Please try a different ballot." + ) + is_duplicate = self._ballot_upload_service.any_ballot_exists( + db, election_id, ballot.object_id + ) + if is_duplicate: + self._log.warn( + "ballot '{ballot.object_id}' already exists in election '{election_id}'" + ) + return eel_success({"is_duplicate": True}) + success = self._ballot_upload_service.add_ballot( - db, ballot_upload_id, election_id, file_name, file_contents + db, + ballot_upload_id, + election_id, + file_name, + file_contents, + ballot.object_id, ) if success: self._ballot_upload_service.increment_ballot_count(db, ballot_upload_id) self._election_service.increment_ballot_upload_ballot_count( db, election_id, ballot_upload_id ) - return eel_success({"is_duplicate": not success}) + return eel_success({"is_duplicate": False}) # pylint: disable=broad-except except Exception as e: return self.handle_error(e) diff --git a/src/electionguard_gui/services/ballot_upload_service.py b/src/electionguard_gui/services/ballot_upload_service.py index 6f7ba5ec..2d70e766 100644 --- a/src/electionguard_gui/services/ballot_upload_service.py +++ b/src/electionguard_gui/services/ballot_upload_service.py @@ -46,15 +46,9 @@ def add_ballot( election_id: str, file_name: str, file_contents: str, + ballot_object_id: str, ) -> bool: self._log.trace(f"adding ballot {file_name} to {ballot_upload_id}") - ballot = from_raw(SubmittedBallot, file_contents) - ballot_object_id = ballot.object_id - if self.any_ballot_exists(db, election_id, ballot_object_id): - self._log.warn( - "ballot '{ballot_object_id}' already exists in election '{election_id}'" - ) - return False db.ballot_uploads.insert_one( { "ballot_upload_id": ballot_upload_id, From 9cf4ac6544aa434fd5cf0b3956c5426f02967a95 Mon Sep 17 00:00:00 2001 From: Lee Richardson Date: Thu, 25 Aug 2022 16:23:11 -0400 Subject: [PATCH 2/8] Better error display on decryption --- .../admin/view-decryption-admin-component.js | 32 +++++++++++++------ 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/src/electionguard_gui/web/components/admin/view-decryption-admin-component.js b/src/electionguard_gui/web/components/admin/view-decryption-admin-component.js index 8772ff8d..d2381753 100644 --- a/src/electionguard_gui/web/components/admin/view-decryption-admin-component.js +++ b/src/electionguard_gui/web/components/admin/view-decryption-admin-component.js @@ -29,26 +29,37 @@ export default { spoiledBallotId: spoiledBallotId, }); }, - refresh_decryption: async function () { - await this.get_decryption(true); + refresh_decryption: async function (result) { + if (result.success) { + await this.get_decryption(true); + } else { + console.error(result.message); + this.error = true; + } }, get_decryption: async function (is_refresh) { console.log("getting decryption"); this.loading = true; - const result = await eel.get_decryption(this.decryptionId, is_refresh)(); - this.error = !result.success; - if (result.success) { - this.decryption = result.result; + try { + const result = await eel.get_decryption( + this.decryptionId, + is_refresh + )(); + this.error = !result.success; + if (result.success) { + this.decryption = result.result; + } + } finally { + this.loading = false; } - this.loading = false; }, }, async mounted() { - await this.get_decryption(false); eel.expose(this.refresh_decryption, "refresh_decryption"); + await this.get_decryption(false); console.log("watching decryption"); // only watch for changes if the decryption is in-progress - if (!this.decryption.completed_at_str) { + if (this.decryption && !this.decryption.completed_at_str) { eel.watch_decryption(this.decryptionId); } }, @@ -132,5 +143,8 @@ export default { +
+ +
`, }; From 14ee196583257e2a0b1eaeeffe677e411eb277ae Mon Sep 17 00:00:00 2001 From: Lee Richardson Date: Thu, 25 Aug 2022 16:37:18 -0400 Subject: [PATCH 3/8] Ballot uploading UI enhancements --- .../components/admin/upload-ballots-component.js | 2 +- .../components/admin/view-election-component.js | 15 +++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/electionguard_gui/web/components/admin/upload-ballots-component.js b/src/electionguard_gui/web/components/admin/upload-ballots-component.js index a3e99a36..8134d481 100644 --- a/src/electionguard_gui/web/components/admin/upload-ballots-component.js +++ b/src/electionguard_gui/web/components/admin/upload-ballots-component.js @@ -134,7 +134,7 @@ export default {
Please provide a ballot folder.
- + Cancel

{{ ballotsProcessed }} of {{ ballotsTotal }} files processed.

diff --git a/src/electionguard_gui/web/components/admin/view-election-component.js b/src/electionguard_gui/web/components/admin/view-election-component.js index 16e6bd81..8c7ae767 100644 --- a/src/electionguard_gui/web/components/admin/view-election-component.js +++ b/src/electionguard_gui/web/components/admin/view-election-component.js @@ -69,12 +69,6 @@ export default {
  • -
  • - - Upload ballots - -
  • -
  • Create tally @@ -104,9 +98,9 @@ export default {
    by {{election.created_by}} on {{election.created_at}}
  • -
    +
    From 855882036cd5920833da2dd78b9acc33b9db258c Mon Sep 17 00:00:00 2001 From: Lee Richardson Date: Thu, 25 Aug 2022 16:50:18 -0400 Subject: [PATCH 4/8] UI Enhancements to Election Page Move buttons on menu to appropriate sections where possible Display ballots and tallies even when there isn't information to display Move the title down and separate the sections more clearly --- .../admin/view-election-component.js | 44 ++++++++++--------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/src/electionguard_gui/web/components/admin/view-election-component.js b/src/electionguard_gui/web/components/admin/view-election-component.js index 8c7ae767..bd942192 100644 --- a/src/electionguard_gui/web/components/admin/view-election-component.js +++ b/src/electionguard_gui/web/components/admin/view-election-component.js @@ -53,13 +53,13 @@ export default { template: /*html*/ `
    -
    +
    -

    {{election.election_name}}

    +
    -
    -
    +

    {{election.election_name}}

    +
    +
    Guardians
    {{election.guardians}}
    -
    +
    Quorum
    {{election.quorum}}
    -
    +
    Election URL
    {{election.election_url}}
    -
    -
    Created
    +
    +
    Created
    by {{election.created_by}} on {{election.created_at}}
    -
    From acd14fec69e3e034a8a18fa0552109a39fc511ae Mon Sep 17 00:00:00 2001 From: Lee Richardson Date: Thu, 25 Aug 2022 17:12:00 -0400 Subject: [PATCH 5/8] Update decryption page to match style of election page Button color -> secondary Move title lower on page Decryption Results -> Tally Results Show a nice message if no spoiled ballots existed --- .../admin/view-decryption-admin-component.js | 72 ++++++++++++------- .../admin/view-election-component.js | 29 ++++---- 2 files changed, 57 insertions(+), 44 deletions(-) diff --git a/src/electionguard_gui/web/components/admin/view-decryption-admin-component.js b/src/electionguard_gui/web/components/admin/view-decryption-admin-component.js index d2381753..e160993d 100644 --- a/src/electionguard_gui/web/components/admin/view-decryption-admin-component.js +++ b/src/electionguard_gui/web/components/admin/view-decryption-admin-component.js @@ -72,13 +72,10 @@ export default {
    -
    -
    -

    {{decryption.decryption_name}}

    -
    -
    +
    +
    +
    +

    {{decryption.decryption_name}}

    +
    -
    -
    -
    Ballot Uploads
    -
    {{decryption.ballot_upload_count}}
    -
    -
    -
    Total Ballots
    -
    {{decryption.ballot_count}}
    +
    +
    +
    +
    Ballot Uploads
    +
    {{decryption.ballot_upload_count}}
    +
    +
    +
    Total Ballots
    +
    {{decryption.ballot_count}}
    +
    +
    +
    Created
    +
    by {{decryption.created_by}} on {{decryption.created_at}}
    +
    +
    +
    Completed
    +
    {{decryption.completed_at_str}}
    +
    -
    -
    Created
    -
    by {{decryption.created_by}} on {{decryption.created_at}}
    -
    -
    -
    Completed
    -
    {{decryption.completed_at_str}}
    -
    -
    +

    Joined Guardians

    • {{guardian}}
    • @@ -129,11 +131,27 @@ export default {

      No guardians have joined yet

    +
    +

    Tally Results

    + View Tally +
    -

    Decryption Results

    - View Tally -

    Spoiled Ballots

    - {{spoiled_ballot}} +

    Spoiled Ballots

    + + + + + + + + + + + +
    Ballot ID
    {{spoiledBallot}}
    +
    +

    No spoiled ballots existed at the time this tally was run.

    +
    diff --git a/src/electionguard_gui/web/components/admin/view-election-component.js b/src/electionguard_gui/web/components/admin/view-election-component.js index bd942192..fe416210 100644 --- a/src/electionguard_gui/web/components/admin/view-election-component.js +++ b/src/electionguard_gui/web/components/admin/view-election-component.js @@ -53,23 +53,18 @@ export default { template: /*html*/ `
    -
    -
    - -
    -
    - +
    From 449a5b4cccdaeefe84c274b3028f711739cc9366 Mon Sep 17 00:00:00 2001 From: Lee Richardson Date: Thu, 25 Aug 2022 17:49:13 -0400 Subject: [PATCH 6/8] Add tally date to tallies list on election page --- src/electionguard_gui/models/election_dto.py | 8 +++++++- src/electionguard_gui/services/election_service.py | 10 +++++++++- .../admin/view-decryption-admin-component.js | 2 +- .../web/components/admin/view-election-component.js | 10 ++++++---- 4 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/electionguard_gui/models/election_dto.py b/src/electionguard_gui/models/election_dto.py index 332e268d..bef2f343 100644 --- a/src/electionguard_gui/models/election_dto.py +++ b/src/electionguard_gui/models/election_dto.py @@ -74,7 +74,13 @@ def to_dict(self) -> dict[str, Any]: "ballot_styles": self._get_manifest_field("ballot_styles"), }, "ballot_uploads": self.ballot_uploads, - "decryptions": self.decryptions, + "decryptions": [ + { + "name": decryption["name"], + "created_at": utc_to_str(decryption.get("created_at")), + } + for decryption in self.decryptions + ], "created_by": self.created_by, "created_at": self.created_at_str, } diff --git a/src/electionguard_gui/services/election_service.py b/src/electionguard_gui/services/election_service.py index fe1a3f96..2aee03a8 100644 --- a/src/electionguard_gui/services/election_service.py +++ b/src/electionguard_gui/services/election_service.py @@ -121,7 +121,15 @@ def append_decryption( ) db.elections.update_one( {"_id": ObjectId(election_id)}, - {"$push": {"decryptions": {"decryption_id": decryption_id, "name": name}}}, + { + "$push": { + "decryptions": { + "decryption_id": decryption_id, + "name": name, + "created_at": datetime.utcnow(), + } + } + }, ) def increment_ballot_upload_ballot_count( diff --git a/src/electionguard_gui/web/components/admin/view-decryption-admin-component.js b/src/electionguard_gui/web/components/admin/view-decryption-admin-component.js index e160993d..b35738e3 100644 --- a/src/electionguard_gui/web/components/admin/view-decryption-admin-component.js +++ b/src/electionguard_gui/web/components/admin/view-decryption-admin-component.js @@ -143,7 +143,7 @@ export default { Ballot ID - + {{spoiledBallot}} diff --git a/src/electionguard_gui/web/components/admin/view-election-component.js b/src/electionguard_gui/web/components/admin/view-election-component.js index fe416210..da2af2fa 100644 --- a/src/electionguard_gui/web/components/admin/view-election-component.js +++ b/src/electionguard_gui/web/components/admin/view-election-component.js @@ -94,12 +94,12 @@ export default { - + - + @@ -124,19 +124,21 @@ export default { -
    +

    Tallies

    LocationLocation1 Ballot Count
    {{ballot_upload.location}} {{ballot_upload.ballot_count}}
    + - + + From eec76906244af7fdfa0c929e2e8e89c6f86d5793 Mon Sep 17 00:00:00 2001 From: Lee Richardson Date: Thu, 25 Aug 2022 18:03:57 -0400 Subject: [PATCH 7/8] Include ballot upload date in UI --- src/electionguard_gui/models/election_dto.py | 9 ++++++++- .../web/components/admin/view-election-component.js | 9 ++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/electionguard_gui/models/election_dto.py b/src/electionguard_gui/models/election_dto.py index bef2f343..f28efecb 100644 --- a/src/electionguard_gui/models/election_dto.py +++ b/src/electionguard_gui/models/election_dto.py @@ -73,7 +73,14 @@ def to_dict(self) -> dict[str, Any]: "contests": self._get_manifest_field("contests"), "ballot_styles": self._get_manifest_field("ballot_styles"), }, - "ballot_uploads": self.ballot_uploads, + "ballot_uploads": [ + { + "location": ballot_upload["location"], + "ballot_count": ballot_upload["ballot_count"], + "created_at": utc_to_str(ballot_upload.get("created_at")), + } + for ballot_upload in self.ballot_uploads + ], "decryptions": [ { "name": decryption["name"], diff --git a/src/electionguard_gui/web/components/admin/view-election-component.js b/src/electionguard_gui/web/components/admin/view-election-component.js index da2af2fa..fc1bfa5c 100644 --- a/src/electionguard_gui/web/components/admin/view-election-component.js +++ b/src/electionguard_gui/web/components/admin/view-election-component.js @@ -94,13 +94,15 @@ export default {
    NameDate
    {{decryption.name}}{{decryption.created_at}}
    - + + + @@ -109,6 +111,7 @@ export default { + @@ -130,15 +133,15 @@ export default {
    Location1UploadedLocation Ballot Count
    {{ballot_upload.created_at}} {{ballot_upload.location}} {{ballot_upload.ballot_count}}
    Total  {{ballotSum}}
    + - - + From 718b4f74088e136713bd7860e625108923841c82 Mon Sep 17 00:00:00 2001 From: Lee Richardson Date: Thu, 25 Aug 2022 18:06:01 -0400 Subject: [PATCH 8/8] fix linting issues --- src/electionguard_gui/components/upload_ballots_component.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/electionguard_gui/components/upload_ballots_component.py b/src/electionguard_gui/components/upload_ballots_component.py index b2682585..913dd593 100644 --- a/src/electionguard_gui/components/upload_ballots_component.py +++ b/src/electionguard_gui/components/upload_ballots_component.py @@ -1,8 +1,8 @@ from typing import Any from datetime import datetime +import eel from electionguard.serialize import from_raw from electionguard.ballot import SubmittedBallot -import eel from electionguard_gui.components.component_base import ComponentBase from electionguard_gui.eel_utils import eel_fail, eel_success from electionguard_gui.services import ElectionService, BallotUploadService
    Created NameDate
    {{decryption.name}} {{decryption.created_at}}{{decryption.name}}