def trustees_generate_election_private_keys(self): # Each trustee (Tom and Taylor) will do the following process for idx, trustee_email_address in enumerate(settings.TRUSTEES_EMAIL_ADDRESSES): # Trustee opens link that has been sent to him by election administrator link_for_this_trustee = self.links_for_trustees[idx] # TODO: Decide either not send trustee email at all or read trustee link from email content self.browser = initialize_browser_for_scenario_2() browser = self.browser browser.get(link_for_this_trustee) # He checks that the page content shows the same election URL as the one the administrator saw election_url_css_selector = "#main ul li" election_url_element = wait_for_element_exists_and_has_non_empty_content(browser, election_url_css_selector) election_url_content = election_url_element.get_attribute('innerText').strip() assert election_url_content == self.election_page_url # He clicks on the "Generate a new keypair" button generate_button_css_selector = "#interactivity button" generate_button_expected_label = "Generate a new keypair" generate_button_element = wait_for_element_exists_and_contains_expected_text(browser, generate_button_css_selector, generate_button_expected_label) generate_button_element.click() # He clicks on the "private key" and "public key" links, to download the private key and the public key (files are respectively saved by default as `private_key.json` and `public_key.json`, but we decide to save them as a unique file name) link_css_ids = ["private_key", "public_key"] link_expected_labels = ["private key", "public key"] self.downloaded_files_paths_per_trustee[trustee_email_address] = dict() for idx2, link_css_id in enumerate(link_css_ids): link_target_filename = str(uuid4()) set_element_attribute(browser, link_css_id, 'download', link_target_filename) link_expected_label = link_expected_labels[idx2] link_element = wait_for_an_element_with_partial_link_text_exists(browser, link_expected_label) assert link_element.get_attribute('id') == link_css_id link_element.click() file_absolute_path = os.path.join(settings.BROWSER_DOWNLOAD_FOLDER, link_target_filename) # We save the filename in a class instance property, so that we can import the file afterwards (during partial decryption step) self.downloaded_files_paths_per_trustee[trustee_email_address][link_expected_labels[idx2]] = file_absolute_path self.remember_temporary_file_to_remove_after_test(file_absolute_path) # He clicks on the "Submit public key" button submit_button_expected_label = "Submit public key" submit_button_css_selector = "#main input[type=submit][value='" + submit_button_expected_label + "']" submit_button_element = wait_for_element_exists(browser, submit_button_css_selector) submit_button_element.click() # He checks that the next page shows the expected confirmation sentence expected_confirmation_label = "Your key has been received and checked!" expected_confirmation_css_selector = "#main" wait_for_element_exists_and_contains_expected_text(browser, expected_confirmation_css_selector, expected_confirmation_label) # He closes the window browser.quit()
def trustees_generate_election_private_keys(self): # Each trustee (Tom and Taylor) will do the following process for idx, trustee_email_address in enumerate( settings.TRUSTEES_EMAIL_ADDRESSES): # Trustee opens link that has been sent to him by election administrator link_for_this_trustee = self.links_for_trustees[ idx] # TODO: Decide either not send trustee email at all or read trustee link from email content self.browser = initialize_browser_for_scenario_2() browser = self.browser browser.get(link_for_this_trustee) # He checks that the page content shows the same election URL as the one the administrator saw election_url_css_selector = "#main ul li" election_url_element = wait_for_element_exists_and_has_non_empty_content( browser, election_url_css_selector) election_url_content = election_url_element.get_attribute( 'innerText').strip() assert election_url_content == self.election_page_url # He clicks on the "Generate a new keypair" button generate_button_css_selector = "#interactivity button" generate_button_expected_label = "Generate a new keypair" generate_button_element = wait_for_element_exists_and_contains_expected_text( browser, generate_button_css_selector, generate_button_expected_label) generate_button_element.click() # He clicks on the "private key" and "public key" links, to download the private key and the public key (files are respectively saved by default as `private_key.json` and `public_key.json`, but we decide to save them as a unique file name) link_css_ids = ["private_key"] link_expected_labels = ["private key"] self.downloaded_files_paths_per_trustee[ trustee_email_address] = dict() for idx2, link_css_id in enumerate(link_css_ids): link_target_filename = str(uuid4()) set_element_attribute(browser, link_css_id, 'download', link_target_filename) link_expected_label = link_expected_labels[idx2] link_element = wait_for_an_element_with_partial_link_text_exists( browser, link_expected_label) assert link_element.get_attribute('id') == link_css_id link_element.click() file_absolute_path = os.path.join( settings.BROWSER_DOWNLOAD_FOLDER, link_target_filename) # We save the filename in a class instance property, so that we can import the file afterwards (during partial decryption step) self.downloaded_files_paths_per_trustee[trustee_email_address][ link_expected_labels[idx2]] = file_absolute_path self.remember_temporary_file_to_remove_after_test( file_absolute_path) # He clicks on the "Submit public key" button submit_button_expected_label = "Submit public key" submit_button_css_selector = "#main input[type=submit][value='" + submit_button_expected_label + "']" submit_button_element = wait_for_element_exists( browser, submit_button_css_selector) submit_button_element.click() # He checks that the next page shows the expected confirmation sentence expected_confirmation_label = "Your key has been received and checked!" expected_confirmation_css_selector = "#main" wait_for_element_exists_and_contains_expected_text( browser, expected_confirmation_css_selector, expected_confirmation_label) # He closes the window browser.quit()
def credential_authority_sends_credentials_to_voters(self): # Cecily, the Credential Authority, receives the email sent by Alice, and opens the link in it self.browser = initialize_browser_for_scenario_2() browser = self.browser browser.get(self.credential_authority_link) wait_a_bit() # She remembers what the link to the election will be, so that she will be able to send it to voters by email with their private credential # TODO: use a better selector: edit Belenios page to use an ID in this DOM element future_election_link_css_selector = "#main ul li" future_election_link_element = wait_for_element_exists_and_has_non_empty_content( browser, future_election_link_css_selector) self.election_page_url = future_election_link_element.get_attribute( 'innerText').strip() # She clicks on the "Generate" button generate_button_css_selector = "#interactivity button" generate_button_element = wait_for_element_exists( browser, generate_button_css_selector) generate_button_element.click() wait_a_bit() # She clicks on the "private credentials" and "public credentials" links and downloads these files. Files are by default downloaded to /tmp using filenames `creds.txt` and `public_creds.txt` respectively, but we choose to name them using an unique identifier instead. link_css_ids = ["creds"] file_labels = ["private credentials"] link_css_selectors = ["#" + el for el in link_css_ids] for idx, link_css_id in enumerate(link_css_ids): link_element = wait_for_element_exists(browser, link_css_selectors[idx]) target_filename = str(uuid4()) set_element_attribute(browser, link_css_id, 'download', target_filename) link_element.click() file_absolute_path = os.path.join(settings.BROWSER_DOWNLOAD_FOLDER, target_filename) self.credential_authority_file_paths[file_labels[ idx]] = file_absolute_path # we save the filename in a class instance property, so that we can read the file afterwards (to extract trustee credentials and send them by email to trustees) self.remember_temporary_file_to_remove_after_test( file_absolute_path) wait_a_bit() # She clicks on the "Submit public credentials" button submit_button_css_selector = "#submit_form input[type=submit]" submit_button_element = wait_for_element_exists( browser, submit_button_css_selector) submit_button_element.click() wait_a_bit() # She checks that redirected page shows correct confirmation sentence expected_content_text = "Credentials have been received and checked!" expected_content_css_selector = "#main" wait_for_element_exists_and_contains_expected_text( browser, expected_content_css_selector, expected_content_text) wait_a_bit() # She closes the window browser.quit() # She reads the private credentials file (creds.txt) and sends credential emails to voters # TODO: Should we check that creds.txt contains the exact same voters email addresses as the ones that admin has added? private_credentials_file_path = self.credential_authority_file_paths[ "private credentials"] self.credential_authority_sends_credentials_to_voters_from_credentials_file( private_credentials_file_path)
def credential_authority_sends_credentials_to_voters(self): # Cecily, the Credential Authority, receives the email sent by Alice, and opens the link in it self.browser = initialize_browser_for_scenario_2() browser = self.browser browser.get(self.credential_authority_link) wait_a_bit() # She remembers what the link to the election will be, so that she will be able to send it to voters by email with their private credential # TODO: use a better selector: edit Belenios page to use an ID in this DOM element future_election_link_css_selector = "#main ul li" future_election_link_element = wait_for_element_exists_and_has_non_empty_content(browser, future_election_link_css_selector) self.election_page_url = future_election_link_element.get_attribute('innerText').strip() # She clicks on the "Generate" button generate_button_css_selector = "#interactivity button" generate_button_element = wait_for_element_exists(browser, generate_button_css_selector) generate_button_element.click() wait_a_bit() # She clicks on the "private credentials" and "public credentials" links and downloads these files. Files are by default downloaded to /tmp using filenames `creds.txt` and `public_creds.txt` respectively, but we choose to name them using an unique identifier instead. link_css_ids = ["creds", "public_creds"] file_labels = ["private credentials", "public credentials"] link_css_selectors = ["#" + el for el in link_css_ids] for idx, link_css_id in enumerate(link_css_ids): link_element = wait_for_element_exists(browser, link_css_selectors[idx]) target_filename = str(uuid4()) set_element_attribute(browser, link_css_id, 'download', target_filename) link_element.click() file_absolute_path = os.path.join(settings.BROWSER_DOWNLOAD_FOLDER, target_filename) self.credential_authority_file_paths[file_labels[idx]] = file_absolute_path # we save the filename in a class instance property, so that we can read the file afterwards (to extract trustee credentials and send them by email to trustees) self.remember_temporary_file_to_remove_after_test(file_absolute_path) wait_a_bit() # She clicks on the "Submit public credentials" button submit_button_css_selector = "#submit_form input[type=submit]" submit_button_element = wait_for_element_exists(browser, submit_button_css_selector) submit_button_element.click() wait_a_bit() # She checks that redirected page shows correct confirmation sentence expected_content_text = "Credentials have been received and checked!" expected_content_css_selector = "#main" wait_for_element_exists_and_contains_expected_text(browser, expected_content_css_selector, expected_content_text) wait_a_bit() # She closes the window browser.quit() # She reads the private credentials file (creds.txt) and sends credential emails to voters # TODO: Should we check that creds.txt contains the exact same voters email addresses as the ones that admin has added? private_credentials_file_path = self.credential_authority_file_paths["private credentials"] from_email_address = settings.CREDENTIAL_AUTHORITY_EMAIL_ADDRESS subject = "Your credential for election " + settings.ELECTION_TITLE content = """You are listed as a voter for the election {election_title} You will find below your credential. To cast a vote, you will also need a password, sent in a separate email. Be careful, passwords and credentials look similar but play different roles. You will be asked to enter your credential before entering the voting booth. Login and passwords are required once your ballot is ready to be cast. Credential: {credential} Page of the election: {election_url} Note that you are allowed to vote several times. Only the last vote counts.""" with open(private_credentials_file_path) as myfile: for line in myfile: match = re.search(r'^(\S+)\s(\S+)$', line) if match: voter_email_address = match.group(1) voter_private_credential = match.group(2) else: raise Exception("File creds.txt has wrong format") custom_content = content.format(election_title=settings.ELECTION_TITLE, credential=voter_private_credential, election_url=self.election_page_url) self.fake_sent_emails_manager.send_email(from_email_address, voter_email_address, subject, custom_content)
def trustees_do_initialization_step_3_of_3(self): # Trustees initialization step 3/3: Trustees do the final checks so that the election can be validated. Each of the `T` (aka `NUMBER_OF_TRUSTEES`) trustees will do the following process: for idx, trustee_email_address in enumerate( settings.TRUSTEES_EMAIL_ADDRESSES): # Trustee opens link that has been sent to him by election administrator link_for_this_trustee = self.links_for_trustees[ idx] # TODO: Decide either not send trustee email at all or read trustee link from email content self.browser = initialize_browser_for_scenario_2() browser = self.browser browser.get(link_for_this_trustee) wait_a_bit() # He checks that the page content shows the same election URL as the one the administrator saw election_url_css_selector = "#main ul li" election_url_element = wait_for_element_exists_and_has_non_empty_content( browser, election_url_css_selector) election_url_content = election_url_element.get_attribute( 'innerText').strip() assert election_url_content == self.election_page_url # He checks the presence of text "Step 3/3" expected_confirmation_label = "Step 3/3" expected_confirmation_css_selector = "#main" wait_for_element_exists_and_contains_expected_text( browser, expected_confirmation_css_selector, expected_confirmation_label) # In field next to "Enter your private key:", he types the content of the `private_key.txt` file he downloaded private_key_storage_label = "private key" private_key_file = self.downloaded_files_paths_per_trustee[ trustee_email_address][private_key_storage_label] private_key_css_selector = "#compute_private_key" private_key_element = wait_for_element_exists( browser, private_key_css_selector) private_key_element.clear() with open(private_key_file) as myfile: private_key_element.send_keys(myfile.read()) wait_a_bit() # He clicks on the "Proceed" button proceed_button_css_selector = "#compute_button" proceed_button_element = wait_for_element_exists( browser, proceed_button_css_selector) proceed_button_element.click() # He waits until the text field next to "Data:" contains text, and clicks on the "Submit" button data_field_css_selector = "#compute_data" data_field_expected_non_empty_attribute = "value" wait_for_element_exists_and_has_non_empty_attribute( browser, data_field_css_selector, data_field_expected_non_empty_attribute) submit_button_expected_label = "Submit" submit_button_css_selector = "#compute_form input[type=submit][value=" + submit_button_expected_label + "]" submit_button_element = wait_for_element_exists( browser, submit_button_css_selector) submit_button_element.click() wait_a_bit() # He checks that the next page contains text "Your job in the key establishment protocol is done!" expected_confirmation_label = "Your job in the key establishment protocol is done!" expected_confirmation_css_selector = "#main" wait_for_element_exists_and_contains_expected_text( browser, expected_confirmation_css_selector, expected_confirmation_label) wait_a_bit() # He clicks on the "public key" link and downloads the file (file is saved by default as `public_key.json`) link_css_ids = ["public_key"] link_expected_labels = ["public key"] if trustee_email_address not in self.downloaded_files_paths_per_trustee: self.downloaded_files_paths_per_trustee[ trustee_email_address] = dict() for idx2, link_css_id in enumerate(link_css_ids): link_target_filename = str(uuid4()) set_element_attribute(browser, link_css_id, 'download', link_target_filename) link_expected_label = link_expected_labels[idx2] link_element = wait_for_an_element_with_partial_link_text_exists( browser, link_expected_label) assert link_element.get_attribute('id') == link_css_id link_element.click() file_absolute_path = os.path.join( settings.BROWSER_DOWNLOAD_FOLDER, link_target_filename) # We save the filename in a class instance property, so that we can import the file afterwards (during partial decryption step) self.downloaded_files_paths_per_trustee[trustee_email_address][ link_expected_labels[idx2]] = file_absolute_path self.remember_temporary_file_to_remove_after_test( file_absolute_path) wait_a_bit() # He closes the window browser.quit() # Administrator logs in, and selects the election by clicking on its link self.browser = initialize_browser_for_scenario_2() browser = self.browser log_in_as_administrator(browser) browser.get(self.draft_election_administration_page_url) wait_a_bit() # In the trustees section, she clicks on the "here" link setup_election_key_link_label = "here" setup_election_key_link_element = wait_for_an_element_with_partial_link_text_exists( browser, setup_election_key_link_label) setup_election_key_link_element.click() wait_a_bit() # If current trustee is the last one, she checks that in the table, the "STATE" column is now "done" on every row. Else, she checks that in the table on the current trustee row, the "STATE" column is now "3b" (instead of "3a") if idx == settings.NUMBER_OF_TRUSTEES - 1: expected_value = "done" verify_all_trustee_states_in_table(browser, expected_value) else: expected_value = "3b" verify_trustee_state_in_table(browser, idx, expected_value) wait_a_bit() # She closes the window browser.quit()
def trustees_do_initialization_step_1_of_3(self): # Trustees initialization step 1/3: Trustees generate election private keys. Each of the `T` (aka `NUMBER_OF_TRUSTEES`) trustees will do the following process: for idx, trustee_email_address in enumerate( settings.TRUSTEES_EMAIL_ADDRESSES): # Trustee opens link that has been sent to him by election administrator link_for_this_trustee = self.links_for_trustees[ idx] # TODO: Decide either not send trustee email at all or read trustee link from email content self.browser = initialize_browser_for_scenario_2() browser = self.browser browser.get(link_for_this_trustee) wait_a_bit() # He checks that the page content shows the same election URL as the one the administrator saw election_url_css_selector = "#main ul li" election_url_element = wait_for_element_exists_and_has_non_empty_content( browser, election_url_css_selector) election_url_content = election_url_element.get_attribute( 'innerText').strip() assert election_url_content == self.election_page_url # He clicks on the "Generate private key" button generate_button_css_selector = "#interactivity button" generate_button_expected_label = "Generate private key" generate_button_element = wait_for_element_exists_and_contains_expected_text( browser, generate_button_css_selector, generate_button_expected_label) generate_button_element.click() wait_a_bit() # He clicks on the "private key" link, to download the private key (file is saved by default as `private_key.txt`) link_css_ids = ["private_key"] link_expected_labels = ["private key"] if trustee_email_address not in self.downloaded_files_paths_per_trustee: self.downloaded_files_paths_per_trustee[ trustee_email_address] = dict() for idx2, link_css_id in enumerate(link_css_ids): link_target_filename = str(uuid4()) set_element_attribute(browser, link_css_id, 'download', link_target_filename) link_expected_label = link_expected_labels[idx2] link_element = wait_for_an_element_with_partial_link_text_exists( browser, link_expected_label) assert link_element.get_attribute('id') == link_css_id link_element.click() file_absolute_path = os.path.join( settings.BROWSER_DOWNLOAD_FOLDER, link_target_filename) # We save the filename in a class instance property, so that we can import the file afterwards (during partial decryption step) self.downloaded_files_paths_per_trustee[trustee_email_address][ link_expected_labels[idx2]] = file_absolute_path self.remember_temporary_file_to_remove_after_test( file_absolute_path) wait_a_bit() # He clicks on the "Submit" button submit_button_expected_label = "Submit" submit_button_css_selector = "#main input[type=submit][value='" + submit_button_expected_label + "']" submit_button_element = wait_for_element_exists( browser, submit_button_css_selector) submit_button_element.click() wait_a_bit() # He checks that the next page shows the expected confirmation sentence (If trustee was the last one in the list, he checks that page contains text "Now, all the certificates of the trustees have been generated. Proceed to generate your share of the decryption key.", else he checks for sentence "Waiting for the other trustees... Reload the page to check progress.") if idx == settings.NUMBER_OF_TRUSTEES - 1: expected_confirmation_label = "Now, all the certificates of the trustees have been generated. Proceed to generate your share of the decryption key." else: expected_confirmation_label = "Waiting for the other trustees... Reload the page to check progress." expected_confirmation_css_selector = "#main" wait_for_element_exists_and_contains_expected_text( browser, expected_confirmation_css_selector, expected_confirmation_label) wait_a_bit() # He closes the window browser.quit() # Administrator logs in, and selects the election by clicking on its link self.browser = initialize_browser_for_scenario_2() browser = self.browser log_in_as_administrator(browser) browser.get(self.draft_election_administration_page_url) wait_a_bit() # In the trustees section, she clicks on the "here" link setup_election_key_link_label = "here" setup_election_key_link_element = wait_for_an_element_with_partial_link_text_exists( browser, setup_election_key_link_label) setup_election_key_link_element.click() wait_a_bit() # If current trustee is the last one, she checks that in the table, the "STATE" column is now "2a" on every row. Else, she checks that in the table on the current trustee row, the "STATE" column is now "1b" (instead of "1a") if idx == settings.NUMBER_OF_TRUSTEES - 1: expected_value = "2a" verify_all_trustee_states_in_table(browser, expected_value) else: expected_value = "1b" verify_trustee_state_in_table(browser, idx, expected_value) wait_a_bit() # She closes the window browser.quit()
def credential_authority_sends_credentials_to_voters(self): # Cecily, the Credential Authority, receives the email sent by Alice, and opens the link in it self.browser = initialize_browser_for_scenario_2() browser = self.browser browser.get(self.credential_authority_link) wait_a_bit() # She remembers what the link to the election will be, so that she will be able to send it to voters by email with their private credential # TODO: use a better selector: edit Belenios page to use an ID in this DOM element future_election_link_css_selector = "#main ul li" future_election_link_element = wait_for_element_exists_and_has_non_empty_content( browser, future_election_link_css_selector) self.election_page_url = future_election_link_element.get_attribute( 'innerText').strip() # She clicks on the "Generate" button generate_button_css_selector = "#interactivity button" generate_button_element = wait_for_element_exists( browser, generate_button_css_selector) generate_button_element.click() wait_a_bit() # She clicks on the "private credentials" and "public credentials" links and downloads these files. Files are by default downloaded to /tmp using filenames `creds.txt` and `public_creds.txt` respectively, but we choose to name them using an unique identifier instead. link_css_ids = ["creds", "public_creds"] file_labels = ["private credentials", "public credentials"] link_css_selectors = ["#" + el for el in link_css_ids] for idx, link_css_id in enumerate(link_css_ids): link_element = wait_for_element_exists(browser, link_css_selectors[idx]) target_filename = str(uuid4()) set_element_attribute(browser, link_css_id, 'download', target_filename) link_element.click() file_absolute_path = os.path.join(settings.BROWSER_DOWNLOAD_FOLDER, target_filename) self.credential_authority_file_paths[file_labels[ idx]] = file_absolute_path # we save the filename in a class instance property, so that we can read the file afterwards (to extract trustee credentials and send them by email to trustees) self.remember_temporary_file_to_remove_after_test( file_absolute_path) wait_a_bit() # She clicks on the "Submit public credentials" button submit_button_css_selector = "#submit_form input[type=submit]" submit_button_element = wait_for_element_exists( browser, submit_button_css_selector) submit_button_element.click() wait_a_bit() # She checks that redirected page shows correct confirmation sentence expected_content_text = "Credentials have been received and checked!" expected_content_css_selector = "#main" wait_for_element_exists_and_contains_expected_text( browser, expected_content_css_selector, expected_content_text) wait_a_bit() # She closes the window browser.quit() # She reads the private credentials file (creds.txt) and sends credential emails to voters # TODO: Should we check that creds.txt contains the exact same voters email addresses as the ones that admin has added? private_credentials_file_path = self.credential_authority_file_paths[ "private credentials"] from_email_address = settings.CREDENTIAL_AUTHORITY_EMAIL_ADDRESS subject = "Your credential for election " + settings.ELECTION_TITLE content = """You are listed as a voter for the election {election_title} You will find below your credential. To cast a vote, you will also need a password, sent in a separate email. Be careful, passwords and credentials look similar but play different roles. You will be asked to enter your credential before entering the voting booth. Login and passwords are required once your ballot is ready to be cast. Credential: {credential} Page of the election: {election_url} Note that you are allowed to vote several times. Only the last vote counts.""" with open(private_credentials_file_path) as myfile: for line in myfile: match = re.search(r'^(\S+)\s(\S+)$', line) if match: voter_email_address = match.group(1) voter_private_credential = match.group(2) else: raise Exception("File creds.txt has wrong format") custom_content = content.format( election_title=settings.ELECTION_TITLE, credential=voter_private_credential, election_url=self.election_page_url) self.fake_sent_emails_manager.send_email( from_email_address, voter_email_address, subject, custom_content)