예제 #1
0
def administrator_validates_creation_of_election(browser):
    """
    :return: election page URL

    Initial browser (required) state: on the "Preparation of election" page, with election not yet completely created
    Final browser state: on the "Preparation of election" page (with election completely created)

    Alice, as an administrator who has recently started creating an election (election status is draft), finalizes the creation of the election:
    - In "Validate creation" section, she clicks on the "Create election" link
    - (She arrives on the "Checklist" page, that lists all main parameters of the election for review, and that flags incoherent or misconfigured parameters. For example, in this test scenario, it displays 2 warnings: "Warning: No trustees were set. This means that the server will manage the election key by itself.", and "Warning: No contact was set!")
    - In the "Validate creation" section, she clicks on the "Create election" button
    - (She arrives back on the "My test election for Scenario 1 — Administration" page. Its contents have changed. There is now a text saying "The election is open. Voters can vote.", and there are now buttons "Close election", "Archive election", "Delete election")
    - She remembers the URL of the voting page, that is where the "Election home" link points to
    - She checks that a "Close election" button is present (but she does not click on it)
    """

    # In "Validate creation" section, she clicks on the "Create election" link
    create_election_link_label = "Create election"
    create_election_link_element = wait_for_an_element_with_partial_link_text_exists(
        browser, create_election_link_label, settings.EXPLICIT_WAIT_TIMEOUT)
    create_election_link_element.click()

    wait_a_bit()

    # She arrives on the "Checklist" page, that lists all main parameters of the election for review, and that flags incoherent or misconfigured parameters. For example, in this test scenario, it displays 2 warnings: "Warning: No trustees were set. This means that the server will manage the election key by itself.", and "Warning: No contact was set!"

    # She checks the presence of text "election ready"
    expected_confirmation_label = "election ready"
    expected_confirmation_css_selector = "#main"
    wait_for_element_exists_and_contains_expected_text(
        browser, expected_confirmation_css_selector,
        expected_confirmation_label)

    # In the "Validate creation" section, she clicks on the "Create election" button
    create_election_button_label = "Create election"
    create_election_button_css_selector = build_css_selector_to_find_buttons_in_page_content_by_value(
        create_election_button_label)
    create_election_button_element = wait_for_element_exists(
        browser, create_election_button_css_selector,
        settings.EXPLICIT_WAIT_TIMEOUT)
    create_election_button_element.click()

    wait_a_bit()

    # She arrives back on the "My test election for Scenario 1 — Administration" page. Its contents have changed. There is now a text saying "The election is open. Voters can vote.", and there are now buttons "Close election", "Archive election", "Delete election"

    # She remembers the URL of the voting page, that is where the "Election home" link points to
    election_page_link_label = "Election home"
    election_page_link_element = wait_for_an_element_with_partial_link_text_exists(
        browser, election_page_link_label, settings.EXPLICIT_WAIT_TIMEOUT)
    election_page_url = election_page_link_element.get_attribute('href')

    # She checks that a "Close election" button is present (but she does not click on it)
    close_election_button_label = "Close election"
    close_election_button_css_selector = build_css_selector_to_find_buttons_in_page_content_by_value(
        close_election_button_label)
    wait_for_element_exists(browser, close_election_button_css_selector,
                            settings.EXPLICIT_WAIT_TIMEOUT)

    return election_page_url
예제 #2
0
        def verify_page_title(browser):
            console_log(
                "#### Analyzing contents of page returned after having clicked on Submit button, expecting title to be either 'Ill-formed ballot' or 'Password login'"
            )
            page_title_css_selector = "#header h1"
            page_title_element = wait_for_element_exists(
                browser, page_title_css_selector)
            page_title_label = page_title_element.get_attribute(
                'innerText'
            )  # Here we sometimes get a stale element. This is why we run this inside a loop with several attempts

            page_content_css_selector = "#main"
            page_content_element = wait_for_element_exists(
                browser, page_content_css_selector)
            page_content_label = page_content_element.get_attribute(
                'innerText')

            console_log("#### Page title was", page_title_label,
                        "and page content was", page_content_label)
            if page_title_label == "Error":
                assert page_content_label == "Ill-formed ballot"
                return page_title_label
            elif page_title_label == "Log in with password":
                return page_title_label
            else:
                # This case happens sometimes, because Selenium has not yet replaced its DOM with the new DOM of the page (maybe because server has not responded yet or page loading is not yet complete).
                # In this situation, data is still: ('Unexpected page content', 'USERNAME:\t\nPASSWORD:\t')
                # Or: ('Unexpected page content', 'My test election for Scenario 1', 'Username:Password:')
                raise Exception("Unexpected page content", page_title_label,
                                page_content_label)
예제 #3
0
    def administrator_finishes_tallying_of_election(self, max_trustees=None):
        self.browser = initialize_browser_for_scenario_2()
        browser = self.browser

        # Alice goes to the election page
        election_url = self.election_page_url
        browser.get(election_url)

        wait_a_bit()

        # She clicks on "en" language
        select = Select(
            wait_for_element_exists(browser, ".lang_box select",
                                    settings.EXPLICIT_WAIT_TIMEOUT))
        select.select_by_visible_text("en")
        submit = wait_for_element_exists(browser,
                                         ".lang_box input[type=submit]",
                                         settings.EXPLICIT_WAIT_TIMEOUT)
        submit.click()

        wait_a_bit()

        # She clicks on the "Administer this election" link
        administration_link_label = "Administer this election"
        administration_link_element = wait_for_an_element_with_partial_link_text_exists(
            browser, administration_link_label, settings.EXPLICIT_WAIT_TIMEOUT)
        administration_link_element.click()

        # She logs in as administrator
        log_in_as_administrator(browser, from_a_login_page=True)

        wait_a_bit()

        # She checks that the "DONE?" column of each trustee is to "Yes" (or if there is a threshold on trustees, she checks that at least `max_trustees` have a "Yes")
        expected_label = "Yes"
        yes_cells_selector = "#main table tr td:nth-last-child(1)"
        attribute_name = "innerText"
        if max_trustees is None:
            verify_all_elements_have_attribute_value(browser,
                                                     yes_cells_selector,
                                                     attribute_name,
                                                     expected_label)
        else:
            verify_some_elements_have_attribute_value(browser,
                                                      yes_cells_selector,
                                                      attribute_name,
                                                      expected_label,
                                                      max_trustees)

        # She clicks on the "Compute the result" button
        compute_result_button_expected_label = "Compute the result"
        compute_result_button_css_selector = "#main input[type=submit][value='" + compute_result_button_expected_label + "']"
        compute_result_button_element = wait_for_element_exists(
            browser, compute_result_button_css_selector)
        compute_result_button_element.click()

        wait_a_bit()

        self.administrator_verifies_vote_results()
예제 #4
0
def log_in_as_administrator(browser, from_a_login_page=False):
    if from_a_login_page:
        local_login_link_label = "local"
        local_login_link_element = wait_for_an_element_with_partial_link_text_exists(browser, local_login_link_label, settings.EXPLICIT_WAIT_TIMEOUT)
        local_login_link_element.click()
    else:
        # Alice has been given administrator rights on an online voting app called Belenios. She goes
        # to check out its homepage

        browser.get(settings.SERVER_URL)

        wait_a_bit()

        # She notices the page title mentions an election
        # TODO: Should we wait for the page to load here? It looks like we don't need to.
        assert 'Election Server' in browser.title, "Browser title was: " + browser.title

        # If a personal data policy modal appears (it does not appear after it has been accepted), she clicks on the "Accept" button
        accept_button_label = "Accept"
        button_elements = find_buttons_in_page_content_by_value(browser, accept_button_label)
        if len(button_elements) > 0:
            assert len(button_elements) is 1
            button_elements[0].click()

        # She clicks on "local" to go to the login page
        login_link_css_selector = "#login_local"
        login_element = wait_for_element_exists(browser, login_link_css_selector, settings.EXPLICIT_WAIT_TIMEOUT)
        login_element.click()

    wait_a_bit()

    # She enters her identifier and password and submits the form to log in
    login_form_username_value = settings.ADMINISTRATOR_USERNAME
    login_form_password_value = settings.ADMINISTRATOR_PASSWORD

    login_form_username_css_selector = '#main form input[name=username]'
    login_form_password_css_selector = '#main form input[name=password]'

    login_form_username_element = wait_for_element_exists(browser, login_form_username_css_selector, settings.EXPLICIT_WAIT_TIMEOUT)
    login_form_password_element = wait_for_element_exists(browser, login_form_password_css_selector, settings.EXPLICIT_WAIT_TIMEOUT)

    login_form_username_element.send_keys(login_form_username_value)
    login_form_password_element.send_keys(login_form_password_value)

    wait_a_bit()

    login_form_password_element.submit()

    # She verifies that she arrived on the administration page (instead of any login error page)

    # Here we use Selenium's Explicit Wait to wait for the h1 element of the page to contain expected text, meaning browser will have changed from login page to administration page. If we had used an Implicit Wait (with a defined duration) instead of an Explicit one, we risk to have some errors sometimes (we experienced them before doing this refactoring):
    # - Sometimes we get an error like `selenium.common.exceptions.StaleElementReferenceException: Message: The element reference of <h1> is stale; either the element is no longer attached to the DOM, it is not in the current frame context, or the document has been refreshed` or `selenium.common.exceptions.NoSuchElementException: Message: Unable to locate element: #header h1`. This is because page content changed in between two of our instructions.
    # - Value read from the page is still the value contained in previous page, because page content has not changed yet.

    page_title_css_selector = "#header h1"
    page_title_expected_content = "Administration"
    wait_for_element_exists_and_contains_expected_text(browser, page_title_css_selector, page_title_expected_content, settings.EXPLICIT_WAIT_TIMEOUT)
예제 #5
0
def administrator_sets_election_voters(browser, voters_email_addresses):
    """
    Initial browser (required) state: on the "Preparation of election" page, with voters not set yet
    Final browser state: on the "Preparation of election" page (with voters set)

    :param voters_email_addresses: an array of voters' email addresses, for example generated using `random_email_addresses_generator()`

    Alice, as an administrator who has recently started creating an election (election status is draft), sets its voters:
    - She clicks on the "Edit voters" link, to then type the list of voters
    - She types N e-mail addresses (the list of invited voters)
    - She clicks on the "Add" button to submit changes
    - She clicks on "Go back to election draft" link
    """

    # She clicks on the "Edit voters" link, to then type the list of voters
    edit_voters_link_css_selector = "#edit_voters"
    edit_voters_link_element = wait_for_element_exists(browser, edit_voters_link_css_selector, settings.EXPLICIT_WAIT_TIMEOUT)
    edit_voters_link_element.click()

    wait_a_bit()

    # Split voters_email_addresses into batches of maximum 1000 elements (this is a limit imposed by Belenios UI)
    splitted_voters_email_addresses = list(divide_chunks(voters_email_addresses, 1000))

    for batch_of_email_addresses in splitted_voters_email_addresses:
        # She types N e-mail addresses (the list of invited voters)
        voters_list_field_css_selector = "#main form textarea"
        voters_list_field_element = wait_for_element_exists(browser, voters_list_field_css_selector, settings.EXPLICIT_WAIT_TIMEOUT)
        voters_list_field_element.clear()
        last_email_address_typed = None
        is_first = True
        for email_address in batch_of_email_addresses:
            if is_first:
                is_first = False
            else:
                voters_list_field_element.send_keys(Keys.ENTER)
            voters_list_field_element.send_keys(email_address)
            last_email_address_typed = email_address

        wait_a_bit()

        # She clicks on the "Add" button to submit changes
        voters_list_field_element.submit()

        wait_a_bit()

        # She waits until the returned page displays the last email address typed
        if last_email_address_typed:
            expected_email_address_css_selector = "tr:last-child td:first-child"
            wait_for_element_exists_and_contains_expected_text(browser, expected_email_address_css_selector, last_email_address_typed)

    # She clicks on "Go back to election draft" link
    return_link_label = "Go back to election draft"
    return_link_element = wait_for_an_element_with_partial_link_text_exists(browser, return_link_label, settings.EXPLICIT_WAIT_TIMEOUT)
    return_link_element.click()

    wait_a_bit()
예제 #6
0
def log_in_as_administrator(browser, from_a_login_page=False):
    if from_a_login_page:
        local_login_link_label = settings.LOGIN_MODE
        local_login_link_element = wait_for_an_element_with_partial_link_text_exists(browser, local_login_link_label, settings.EXPLICIT_WAIT_TIMEOUT)
        local_login_link_element.click()
    else:
        # Alice has been given administrator rights on an online voting app called Belenios. She goes
        # to check out its homepage

        browser.get(settings.SERVER_URL)

        wait_a_bit()

        # She notices the page title mentions an election
        # TODO: Should we wait for the page to load here? It looks like we don't need to.
        assert 'Election server' in browser.title, "Browser title was: " + browser.title

        # If a personal data policy modal appears (it does not appear after it has been accepted), she clicks on the "Accept" button
        accept_button_label = "Accept"
        button_elements = find_buttons_in_page_content_by_value(browser, accept_button_label)
        if len(button_elements) > 0:
            assert len(button_elements) is 1
            button_elements[0].click()

        # She clicks on "local" to go to the login page
        login_link_css_selector = "#login_" + settings.LOGIN_MODE
        login_element = wait_for_element_exists(browser, login_link_css_selector, settings.EXPLICIT_WAIT_TIMEOUT)
        login_element.click()

    wait_a_bit()

    # She enters her identifier and password and submits the form to log in
    login_form_username_value = settings.ADMINISTRATOR_USERNAME
    login_form_password_value = settings.ADMINISTRATOR_PASSWORD

    login_form_username_css_selector = '#main form input[name=username]'
    login_form_password_css_selector = '#main form input[name=password]'

    login_form_username_element = wait_for_element_exists(browser, login_form_username_css_selector, settings.EXPLICIT_WAIT_TIMEOUT)
    login_form_password_element = wait_for_element_exists(browser, login_form_password_css_selector, settings.EXPLICIT_WAIT_TIMEOUT)

    login_form_username_element.send_keys(login_form_username_value)
    login_form_password_element.send_keys(login_form_password_value)

    wait_a_bit()

    login_form_password_element.submit()

    # She verifies that she arrived on the administration page (instead of any login error page)

    # Here we use Selenium's Explicit Wait to wait for the h1 element of the page to contain expected text, meaning browser will have changed from login page to administration page. If we had used an Implicit Wait (with a defined duration) instead of an Explicit one, we risk to have some errors sometimes (we experienced them before doing this refactoring):
    # - Sometimes we get an error like `selenium.common.exceptions.StaleElementReferenceException: Message: The element reference of <h1> is stale; either the element is no longer attached to the DOM, it is not in the current frame context, or the document has been refreshed` or `selenium.common.exceptions.NoSuchElementException: Message: Unable to locate element: #header h1`. This is because page content changed in between two of our instructions.
    # - Value read from the page is still the value contained in previous page, because page content has not changed yet.

    page_title_css_selector = "#header h1"
    page_title_expected_content = "Administration"
    wait_for_element_exists_and_contains_expected_text(browser, page_title_css_selector, page_title_expected_content, settings.EXPLICIT_WAIT_TIMEOUT)
예제 #7
0
 def click_on_language_link(self, language_link_label):
     select = Select(
         wait_for_element_exists(self.browser, ".lang_box select",
                                 self.timeout))
     select.select_by_visible_text(language_link_label)
     submit = wait_for_element_exists(self.browser,
                                      ".lang_box input[type=submit]",
                                      self.timeout)
     submit.click()
예제 #8
0
    def credential_authority_sends_locally_generated_credentials_to_server(
            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 executes local (not server's) CLI belenios-tool to generate a number of credentials corresponding to the number of voters. This creates some local files.
        console_log("#### Starting step: belenios_tool_generate_credentials")
        self.credential_file_id = belenios_tool_generate_credentials(
            self.election_id)
        console_log("#### Step complete: belenios_tool_generate_credentials")
        console_log("#### Credential file id:", self.credential_file_id)

        # She uploads the file that corresponds to the public part of the genereated credentials. For this, she clicks on the 'Browse' button and selects the file with `.pubcreds` extension
        browse_button_css_selector = "form input[name=public_creds][type=file]"
        browse_button_element = wait_for_element_exists(
            browser, browse_button_css_selector)
        path_of_file_to_upload = self.credential_file_id + ".pubcreds"
        browse_button_element.clear()
        browse_button_element.send_keys(path_of_file_to_upload)

        # She clicks on 'Submit' button
        submit_button_css_selector = "form input[type=submit][value=Submit]"
        submit_button_element = wait_for_element_exists(
            browser, submit_button_css_selector)
        submit_button_element.click()

        wait_a_bit()

        # She checks that page contains text "Credentials have been received and checked!"
        page_content_css_selector = "#main div"
        page_content_expected_content = "Credentials have been received and checked!"
        wait_for_element_exists_and_contains_expected_text(
            browser, page_content_css_selector, page_content_expected_content,
            settings.EXPLICIT_WAIT_TIMEOUT)

        # We act like if credential authority had also sent each private credential to a different voter. For ease of parsing, we write these emails into the same text file as Belenios server's.
        self.credential_authority_sends_credentials_to_voters_from_credentials_file(
            self.credential_file_id + ".privcreds",
            self.voters_email_addresses)

        # She closes the browser window
        browser.quit()
예제 #9
0
    def fill_form(self, username, password):
        login_form_username_value = username # correct value: settings.ADMINISTRATOR_USERNAME
        login_form_password_value = password # correct value: settings.ADMINISTRATOR_PASSWORD

        login_form_username_element = wait_for_element_exists(self.browser, self.login_form_username_css_selector, self.timeout)
        login_form_password_element = wait_for_element_exists(self.browser, self.login_form_password_css_selector, self.timeout)

        login_form_username_element.clear()
        login_form_username_element.send_keys(login_form_username_value)
        login_form_password_element.clear()
        login_form_password_element.send_keys(login_form_password_value)
예제 #10
0
    def administrator_does_tallying_of_election(self):
        browser = self.browser

        # Alice goes to the election page
        election_url = self.election_page_url  # Could also be obtained with self.voters_data[self.voters_email_addresses[0]]["election_page_url"]
        browser.get(election_url)

        wait_a_bit()

        # She clicks on "en" language
        english_language_link_expected_label = "en"
        english_language_link_element = wait_for_an_element_with_link_text_exists(
            browser, english_language_link_expected_label,
            settings.EXPLICIT_WAIT_TIMEOUT)
        english_language_link_element.click()

        wait_a_bit()

        # She clicks on the "Administer this election" link
        administration_link_label = "Administer this election"
        administration_link_element = wait_for_an_element_with_partial_link_text_exists(
            browser, administration_link_label, settings.EXPLICIT_WAIT_TIMEOUT)
        administration_link_element.click()

        # She logs in as administrator
        log_in_as_administrator(browser, from_a_login_page=True)

        wait_a_bit()

        # She clicks on the "Close election" button
        close_election_button_label = "Close election"
        close_election_button_css_selector = build_css_selector_to_find_buttons_in_page_content_by_value(
            close_election_button_label)
        close_election_button_element = wait_for_element_exists(
            browser, close_election_button_css_selector,
            settings.EXPLICIT_WAIT_TIMEOUT)
        close_election_button_element.click()

        wait_a_bit()

        # She clicks on the "Proceed to vote counting" button
        proceed_button_label = "Proceed to vote counting"
        proceed_button_css_selector = build_css_selector_to_find_buttons_in_page_content_by_value(
            proceed_button_label)
        proceed_button_element = wait_for_element_exists(
            browser, proceed_button_css_selector,
            settings.EXPLICIT_WAIT_TIMEOUT)
        proceed_button_element.click()

        wait_a_bit()

        # FIXME: If no voter has cast their vote, it shows a "Internal Server Error" "Error 500" page

        self.administrator_verifies_vote_results()
예제 #11
0
def administrator_sets_election_voters(browser, voters_email_addresses):
    """
    Initial browser (required) state: on the "Preparation of election" page, with voters not set yet
    Final browser state: on the "Preparation of election" page (with voters set)

    :param voters_email_addresses: an array of voters' email addresses, for example generated using `random_email_addresses_generator()`

    Alice, as an administrator who has recently started creating an election (election status is draft), sets its voters:
    - She clicks on the "Edit voters" link, to then type the list of voters
    - She types N e-mail addresses (the list of invited voters)
    - She clicks on the "Add" button to submit changes
    - She clicks on "Return to draft page" link
    """

    # She clicks on the "Edit voters" link, to then type the list of voters
    edit_voters_link_css_selector = "#edit_voters"
    edit_voters_link_element = wait_for_element_exists(
        browser, edit_voters_link_css_selector, settings.EXPLICIT_WAIT_TIMEOUT)
    edit_voters_link_element.click()

    wait_a_bit()

    # She types N e-mail addresses (the list of invited voters)
    voters_list_field_css_selector = "#main form textarea"
    voters_list_field_element = wait_for_element_exists(
        browser, voters_list_field_css_selector,
        settings.EXPLICIT_WAIT_TIMEOUT)
    voters_list_field_element.clear()
    is_first = True
    for email_address in voters_email_addresses:
        if is_first:
            is_first = False
        else:
            voters_list_field_element.send_keys(Keys.ENTER)
        voters_list_field_element.send_keys(email_address)

    wait_a_bit()

    # She clicks on the "Add" button to submit changes
    add_button_css_selector = "#main form input[type=submit]"
    add_button_element = browser.find_element_by_css_selector(
        add_button_css_selector)
    add_button_element.click()

    wait_a_bit()

    # She clicks on "Return to draft page" link
    return_link_label = "Return to draft page"
    return_link_element = wait_for_an_element_with_partial_link_text_exists(
        browser, return_link_label, settings.EXPLICIT_WAIT_TIMEOUT)
    return_link_element.click()

    wait_a_bit()
예제 #12
0
def administrator_sets_election_voters(browser, voters_email_addresses):
    """
    Initial browser (required) state: on the "Preparation of election" page, with voters not set yet
    Final browser state: on the "Preparation of election" page (with voters set)

    :param voters_email_addresses: an array of voters' email addresses, for example generated using `random_email_addresses_generator()`

    Alice, as an administrator who has recently started creating an election (election status is draft), sets its voters:
    - She clicks on the "Edit voters" link, to then type the list of voters
    - She types N e-mail addresses (the list of invited voters)
    - She clicks on the "Add" button to submit changes
    - She clicks on "Return to draft page" link
    """

    # She clicks on the "Edit voters" link, to then type the list of voters
    edit_voters_link_css_selector = "#edit_voters"
    edit_voters_link_element = wait_for_element_exists(browser, edit_voters_link_css_selector, settings.EXPLICIT_WAIT_TIMEOUT)
    edit_voters_link_element.click()

    wait_a_bit()

    # She types N e-mail addresses (the list of invited voters)
    voters_list_field_css_selector = "#main form textarea"
    voters_list_field_element = wait_for_element_exists(browser, voters_list_field_css_selector, settings.EXPLICIT_WAIT_TIMEOUT)
    voters_list_field_element.clear()
    is_first = True
    for email_address in voters_email_addresses:
        if is_first:
            is_first = False
        else:
            voters_list_field_element.send_keys(Keys.ENTER)
        voters_list_field_element.send_keys(email_address)

    wait_a_bit()

    # She clicks on the "Add" button to submit changes
    add_button_css_selector = "#main form input[type=submit]"
    add_button_element = browser.find_element_by_css_selector(add_button_css_selector)
    add_button_element.click()

    wait_a_bit()

    # She clicks on "Return to draft page" link
    return_link_label = "Return to draft page"
    return_link_element = wait_for_an_element_with_partial_link_text_exists(browser, return_link_label, settings.EXPLICIT_WAIT_TIMEOUT)
    return_link_element.click()

    wait_a_bit()
예제 #13
0
def log_out(browser):
    # In the header of the page, she clicks on the "Log out" link
    logout_link_css_selector = "#logout"
    logout_element = wait_for_element_exists(browser, logout_link_css_selector, settings.EXPLICIT_WAIT_TIMEOUT)
    logout_element.click()

    # She arrives on the election home page. She checks that the "Start" button is present
    wait_for_element_exists_and_contains_expected_text(browser, "#main button", "Start", settings.EXPLICIT_WAIT_TIMEOUT)
예제 #14
0
def administrator_validates_creation_of_election(browser):
    """
    :return: election page URL

    Initial browser (required) state: on the "Preparation of election" page, with election not yet completely created
    Final browser state: on the "Preparation of election" page (with election completely created)

    Alice, as an administrator who has recently started creating an election (election status is draft), finalizes the creation of the election:
    - In "Validate creation" section, she clicks on the "Create election" link
    - (She arrives on the "Checklist" page, that lists all main parameters of the election for review, and that flags incoherent or misconfigured parameters. For example, in this test scenario, it displays 2 warnings: "Warning: No trustees were set. This means that the server will manage the election key by itself.", and "Warning: No contact was set!")
    - In the "Validate creation" section, she clicks on the "Create election" button
    - (She arrives back on the "My test election for Scenario 1 — Administration" page. Its contents have changed. There is now a text saying "The election is open. Voters can vote.", and there are now buttons "Close election", "Archive election", "Delete election")
    - She remembers the URL of the voting page, that is where the "Election home" link points to
    - She checks that a "Close election" button is present (but she does not click on it)
    """

    # In "Validate creation" section, she clicks on the "Create election" link
    create_election_link_label = "Create election"
    create_election_link_element = wait_for_an_element_with_partial_link_text_exists(browser, create_election_link_label, settings.EXPLICIT_WAIT_TIMEOUT)
    create_election_link_element.click()

    wait_a_bit()

    # She arrives on the "Checklist" page, that lists all main parameters of the election for review, and that flags incoherent or misconfigured parameters. For example, in this test scenario, it displays 2 warnings: "Warning: No trustees were set. This means that the server will manage the election key by itself.", and "Warning: No contact was set!"

    # In the "Validate creation" section, she clicks on the "Create election" button
    create_election_button_label = "Create election"
    create_election_button_css_selector = build_css_selector_to_find_buttons_in_page_content_by_value(create_election_button_label)
    create_election_button_element = wait_for_element_exists(browser, create_election_button_css_selector, settings.EXPLICIT_WAIT_TIMEOUT)
    create_election_button_element.click()

    wait_a_bit()

    # She arrives back on the "My test election for Scenario 1 — Administration" page. Its contents have changed. There is now a text saying "The election is open. Voters can vote.", and there are now buttons "Close election", "Archive election", "Delete election"

    # She remembers the URL of the voting page, that is where the "Election home" link points to
    election_page_link_label = "Election home"
    election_page_link_element = wait_for_an_element_with_partial_link_text_exists(browser, election_page_link_label, settings.EXPLICIT_WAIT_TIMEOUT)
    election_page_url = election_page_link_element.get_attribute('href')

    # She checks that a "Close election" button is present (but she does not click on it)
    close_election_button_label = "Close election"
    close_election_button_css_selector = build_css_selector_to_find_buttons_in_page_content_by_value(close_election_button_label)
    wait_for_element_exists(browser, close_election_button_css_selector, settings.EXPLICIT_WAIT_TIMEOUT)

    return election_page_url
예제 #15
0
    def test_submit_prepared_ballot_by_smart_monkey_v2(self, ballot):
        try:
            browser = self.browser
            timeout = settings.EXPLICIT_WAIT_TIMEOUT
            ballot['election_uuid'] = self.election_id
            printable_ballot = json.dumps(ballot)
            console_log("### Starting a new Monkey navigation that will try to submit ballot:", printable_ballot)
            result = self.submit_prepared_ballot(printable_ballot)
            if result:
                console_log("#### Page title was 'Password login', so we log in")
                # Our ballot is not detected as ill-formed, so we arrive on the log in screen
                console_log("#### Verify that we are on login page")
                login_page = VoterLoginPage(browser, timeout)
                login_page.verify_page()

                console_log("#### Filling log in form and submitting it")
                login_page.log_in(settings.VOTER_USERNAME, settings.VOTER_PASSWORD)

                console_log("#### Analyzing page title, expecting it not to be 'Unauthorized'")
                # If user provided a wrong username/password combination, the resulting page is a browser error page like `<h1>Unauthorized</h1><p>Error 401</p>`
                page_title_css_selector = "h1"
                expected_text_1 = "Authenticate with password"
                expected_text_2 = "Unauthorized"
                page_title_element = wait_for_element_exists_and_does_not_contain_expected_text(browser, page_title_css_selector, expected_text_1, timeout)
                page_title_element = wait_for_element_exists_and_does_not_contain_expected_text(browser, page_title_css_selector, expected_text_2, timeout)
                page_title_label = page_title_element.get_attribute('innerText')
                assert page_title_label != expected_text_1
                assert page_title_label != expected_text_2
                console_log("#### Page title was not 'Unauthorized', so we click on confirm ballot submission button")
                # We arrive on the next screen, which asks us to confirm ballot submission
                submit_button_css_selector = "input[type=submit]"
                submit_button_element = wait_for_element_exists(browser, submit_button_css_selector, timeout)
                submit_button_element.click()

                # We check wether the ballot has been received and parsed without errors
                @try_several_times(5)
                def verify_step_label(browser, current_attempt=0):
                    console_log("#### Analyzing (attempt", current_attempt, ") whether result page (after click on confirm ballot submission button) has as step title 'Step 6/6: FAIL!' because ballot content is invalid")
                    current_step_css_selector = "#main .current_step"
                    current_step_element = wait_for_element_exists(browser, current_step_css_selector, timeout)
                    current_step_label = current_step_element.get_attribute('innerText')
                    console_log("#### Step title is:", current_step_label)
                    if current_step_label == "Step 6/6: FAIL!":
                        console_log("#### Page step title was 'Step 6/6: FAIL!', which is what we expected. So the full test is correct.")
                        return current_step_label
                    else:
                        # Possibility of improvement: Handle very improbable case where Belenios server accepts the ballot because by chance this ballot generated by Hypothesis would be correct. For now we consider that if Belenios accepts the generated ballot (correct or incorrect), it is a test failure. Maybe also it could be necessary to better detect any case that falls outside these 2 situations.
                        console_log("#### Step title is unexpected. So the full test is incorrect.")
                        raise Exception("Step title is unexpected:", current_step_label)

                final_label = verify_step_label(browser)
                assert final_label == "Step 6/6: FAIL!"
        except Exception as e:
            console_log("Step title is unexpected. Exception received:", e)
            browser.quit()
            self.browser = initialize_browser()
            raise e
예제 #16
0
def administrator_edits_election_questions(browser, nh_question=False):
    """
    Initial browser (required) state: on the "Preparation of election" page, with questions not edited yet
    Final browser state: on the "Preparation of election" page (with questions edited)

    Alice, as an administrator who has recently started creating an election (election status is draft), configures its questions:
    - She clicks on the "Edit questions" link, to write her own questions
    - She arrives on the Questions page. She checks that the page title is correct
    - She removes answer 3
    - She clicks on the "Save changes" button (this redirects to the "Preparation of election" page)
    """

    # She clicks on the "Edit questions" link, to write her own questions
    edit_questions_link_css_selector = "#edit_questions"
    edit_questions_link_element = wait_for_element_exists(
        browser, edit_questions_link_css_selector,
        settings.EXPLICIT_WAIT_TIMEOUT)
    edit_questions_link_element.click()

    wait_a_bit()

    # She arrives on the Questions page. She checks that the page title is correct
    page_title_css_selector = "#header h1"
    page_title_expected_content = "Questions for"
    wait_for_element_exists_and_contains_expected_text(
        browser, page_title_css_selector, page_title_expected_content,
        settings.EXPLICIT_WAIT_TIMEOUT)

    if nh_question:
        # She ticks "Tick the box to activate this mode."
        browser.find_element_by_css_selector("#hybrid_mode").click()
        # She ticks "Alternative"
        nhtally_css_selector = ".nonhomomorphic_tally"
        nhtally_checkbox_element = browser.find_element_by_css_selector(
            nhtally_css_selector)
        nhtally_checkbox_element.click()

    # She removes answer 3
    question_to_remove = 3
    remove_button_css_selector = ".question_answer_item:nth-child(" + str(
        question_to_remove) + ") .btn_remove"
    remove_button_element = browser.find_element_by_css_selector(
        remove_button_css_selector)
    remove_button_element.click()

    wait_a_bit()

    # She clicks on the "Save changes" button (this redirects to the "Preparation of election" page)
    save_changes_button_expected_label = "Save changes"
    button_elements = browser.find_elements_by_css_selector("button")
    assert len(button_elements)
    save_changes_button_element = button_elements[-1]
    verify_element_label(save_changes_button_element,
                         save_changes_button_expected_label)
    save_changes_button_element.click()

    wait_a_bit()
예제 #17
0
def log_out(browser):
    # In the header of the page, she clicks on the "Log out" link
    logout_link_css_selector = "#logout"
    logout_element = wait_for_element_exists(browser, logout_link_css_selector,
                                             settings.EXPLICIT_WAIT_TIMEOUT)
    logout_element.click()

    # She arrives on the election home page. She checks that the "Start" button is present
    wait_for_element_exists_and_contains_expected_text(
        browser, "#main button", "Start", settings.EXPLICIT_WAIT_TIMEOUT)
예제 #18
0
    def submit_prepared_ballot(self, ballot):
        browser = self.browser
        timeout = settings.EXPLICIT_WAIT_TIMEOUT
        console_log("#### Going to election page")
        election_page_url = election_id_to_election_home_page_url(self.election_id)
        browser.get(election_page_url)

        console_log("#### Clicking on 'en' language link")
        election_home_page = ElectionHomePage(browser, timeout)
        election_home_page.click_on_language_link("en")

        console_log("#### Clicking on 'Advanced mode' link")
        election_home_page.click_on_advanced_mode_link()

        console_log("#### Filling ballot field with text representation of ballot and clicking on Submit button")
        field_css_selector = "form[action=submit-ballot] textarea[name=encrypted_vote]"
        field_element = wait_for_element_exists(browser, field_css_selector, timeout)
        field_element.clear()
        field_element.send_keys(ballot)
        field_element.submit()

        wait_a_bit()

        @try_several_times(5)
        def verify_page_title(browser):
            console_log("#### Analyzing contents of page returned after having clicked on Submit button, expecting title to be either 'Ill-formed ballot' or 'Password login'")
            page_title_css_selector = "#header h1"
            page_title_element = wait_for_element_exists(browser, page_title_css_selector)
            page_title_label = page_title_element.get_attribute('innerText') # Here we sometimes get a stale element. This is why we run this inside a loop with several attempts

            page_content_css_selector = "#main"
            page_content_element = wait_for_element_exists(browser, page_content_css_selector)
            page_content_label = page_content_element.get_attribute('innerText')

            console_log("#### Page title was", page_title_label, "and page content was", page_content_label)
            if page_title_label == "Error":
                assert page_content_label == "Ill-formed ballot"
                return page_title_label
            elif page_title_label == "Authenticate with password":
                return page_title_label
            else:
                # This case happens sometimes, because Selenium has not yet replaced its DOM with the new DOM of the page (maybe because server has not responded yet or page loading is not yet complete).
                # In this situation, data is still: ('Unexpected page content', 'USERNAME:\t\nPASSWORD:\t')
                # Or: ('Unexpected page content', 'My test election for Scenario 1', 'Username:Password:')
                raise Exception("Unexpected page content", page_title_label, page_content_label)

        try:
            page_title = verify_page_title(browser)
            assert page_title in ["Error", "Authenticate with password"]
            return page_title
        except Exception as e:
            console_log("Could not locate expected element. Exception received:", e)
            # browser.quit()
            # self.browser = initialize_browser()
            raise e
예제 #19
0
    def administrator_does_tallying_of_election(self):
        browser = self.browser

        # Alice goes to the election page
        election_url = self.election_page_url # Could also be obtained with self.voters_data[self.voters_email_addresses[0]]["election_page_url"]
        browser.get(election_url)

        wait_a_bit()

        # She clicks on the "Administer this election" link
        administration_link_label = "Administer this election"
        administration_link_element = wait_for_an_element_with_partial_link_text_exists(browser, administration_link_label, settings.EXPLICIT_WAIT_TIMEOUT)
        administration_link_element.click()

        # She logs in as administrator
        log_in_as_administrator(browser, from_a_login_page=True)

        wait_a_bit()

        # She clicks on the "Close election" button
        close_election_button_label = "Close election"
        close_election_button_css_selector = build_css_selector_to_find_buttons_in_page_content_by_value(close_election_button_label)
        close_election_button_element = wait_for_element_exists(browser, close_election_button_css_selector, settings.EXPLICIT_WAIT_TIMEOUT)
        close_election_button_element.click()

        wait_a_bit()

        # She clicks on the "Proceed to vote counting" button
        proceed_button_label = "Proceed to vote counting"
        proceed_button_css_selector = build_css_selector_to_find_buttons_in_page_content_by_value(proceed_button_label)
        proceed_button_element = wait_for_element_exists(browser, proceed_button_css_selector, settings.EXPLICIT_WAIT_TIMEOUT)
        proceed_button_element.click()

        wait_a_bit()

        # She clicks on the "Proceed to decryption" button
        decrypt_button_label = "Proceed to decryption"
        decrypt_button_css_selector = build_css_selector_to_find_buttons_in_page_content_by_value(decrypt_button_label)
        decrypt_button_element = wait_for_element_exists(browser, decrypt_button_css_selector, settings.EXPLICIT_WAIT_TIMEOUT)
        decrypt_button_element.click()

        wait_a_bit()
예제 #20
0
                    def verify_step_5_and_6(browser, timeout):
                        # Verify that page contains a ballot tracker
                        smart_ballot_tracker_css_selector = "#ballot_tracker"
                        smart_ballot_tracker_element = wait_for_element_exists_and_has_non_empty_content(
                            browser, smart_ballot_tracker_css_selector,
                            timeout)
                        my_smart_ballot_tracker_value = smart_ballot_tracker_element.get_attribute(
                            'innerText')
                        assert len(my_smart_ballot_tracker_value) > 5

                        # Click "I cast my vote" button
                        submit_button_css_selector = "form input[type=submit]"
                        submit_button_expected_content = "I cast my vote"
                        verify_all_elements_have_attribute_value(
                            browser, submit_button_css_selector, "value",
                            submit_button_expected_content, timeout)
                        submit_button_element = wait_for_element_exists(
                            browser, submit_button_css_selector, timeout)
                        submit_button_element.click()

                        wait_a_bit()

                        # Verify that vote has been accepted by the server

                        @try_several_times(max_attempts=3)
                        def verify_that_we_are_on_step_6(browser, timeout):
                            all_ballots_link_expected_label = "ballot box"
                            all_ballots_element = wait_for_an_element_with_link_text_exists(
                                browser, all_ballots_link_expected_label,
                                timeout)

                            current_step_css_selector = ".current_step"
                            current_step_expected_content = "Step 6/6: Thank you for voting!"
                            wait_for_element_exists_and_contains_expected_text(
                                browser, current_step_css_selector,
                                current_step_expected_content, timeout)

                            return all_ballots_element

                        all_ballots_element = verify_that_we_are_on_step_6(
                            browser, timeout)

                        # Go to all ballots page
                        all_ballots_element.click()

                        wait_a_bit()

                        # Verify presence of my ballot
                        my_smart_ballot_tracker_link_element = wait_for_an_element_with_link_text_exists(
                            browser, my_smart_ballot_tracker_value,
                            settings.EXPLICIT_WAIT_TIMEOUT)
                        my_smart_ballot_tracker_link_element.click()
예제 #21
0
    def administrator_finishes_tallying_of_election(self):
        self.browser = initialize_browser_for_scenario_2()
        browser = self.browser

        # Alice goes to the election page
        election_url = self.election_page_url
        browser.get(election_url)

        wait_a_bit()

        # She clicks on the "Administer this election" link
        administration_link_label = "Administer this election"
        administration_link_element = wait_for_an_element_with_partial_link_text_exists(
            browser, administration_link_label, settings.EXPLICIT_WAIT_TIMEOUT)
        administration_link_element.click()

        # She logs in as administrator
        log_in_as_administrator(browser, from_a_login_page=True)

        wait_a_bit()

        # She checks that encrypted tally hash is still the same as the first time it has been displayed to her
        encrypted_tally_hash_css_selector = "#encrypted_tally_hash"
        encrypted_tally_hash_element = wait_for_element_exists_and_has_non_empty_content(
            browser, encrypted_tally_hash_css_selector)
        encrypted_tally_hash = encrypted_tally_hash_element.get_attribute(
            'innerText')
        assert encrypted_tally_hash == self.encrypted_tally_hash, "Error: Encrypted tally hash displayed to trustee (" + encrypted_tally_hash + ") is not the same as the one displayed to election administrator (" + self.encrypted_tally_hash + ")."

        # She checks that the "DONE?" column of each trustee is to "Yes"
        expected_label = "Yes"
        yes_cells_selector = "#main table tr td:nth-last-child(1)"
        yes_cells_elements = wait_for_elements_exist(browser,
                                                     yes_cells_selector)
        assert len(yes_cells_elements
                   ) > 0, "Error: could not find last cell in table in page"
        for element in yes_cells_elements:
            assert element.get_attribute(
                'innerText'
            ) == expected_label, "Error: The last cell of a row in table that is displayed in page has a value that is '" + element.get_attribute(
                'innerText') + "' instead of expected '" + expected_label + "'"

        # She clicks on the "Compute the result" button
        compute_result_button_expected_label = "Compute the result"
        compute_result_button_css_selector = "#main input[type=submit][value='" + compute_result_button_expected_label + "']"
        compute_result_button_element = wait_for_element_exists(
            browser, compute_result_button_css_selector)
        compute_result_button_element.click()

        wait_a_bit()

        self.administrator_verifies_vote_results()
예제 #22
0
 def verify_step_label(browser, current_attempt=0):
     console_log("#### Analyzing (attempt", current_attempt, ") whether result page (after click on confirm ballot submission button) has as step title 'Step 6/6: FAIL!' because ballot content is invalid")
     current_step_css_selector = "#main .current_step"
     current_step_element = wait_for_element_exists(browser, current_step_css_selector, timeout)
     current_step_label = current_step_element.get_attribute('innerText')
     console_log("#### Step title is:", current_step_label)
     if current_step_label == "Step 6/6: FAIL!":
         console_log("#### Page step title was 'Step 6/6: FAIL!', which is what we expected. So the full test is correct.")
         return current_step_label
     else:
         # Possibility of improvement: Handle very improbable case where Belenios server accepts the ballot because by chance this ballot generated by Hypothesis would be correct. For now we consider that if Belenios accepts the generated ballot (correct or incorrect), it is a test failure. Maybe also it could be necessary to better detect any case that falls outside these 2 situations.
         console_log("#### Step title is unexpected. So the full test is incorrect.")
         raise Exception("Step title is unexpected:", current_step_label)
예제 #23
0
    def administrator_does_tallying_of_election(self):
        browser = self.browser

        # Alice goes to the election page
        election_url = self.election_page_url # Could also be obtained with self.voters_data[self.voters_email_addresses[0]]["election_page_url"]
        browser.get(election_url)

        wait_a_bit()

        # She clicks on the "Administer this election" link
        administration_link_label = "Administer this election"
        administration_link_element = wait_for_an_element_with_partial_link_text_exists(browser, administration_link_label, settings.EXPLICIT_WAIT_TIMEOUT)
        administration_link_element.click()

        # She logs in as administrator
        log_in_as_administrator(browser, from_a_login_page=True)

        wait_a_bit()

        # She clicks on the "Close election" button
        close_election_button_label = "Close election"
        close_election_button_css_selector = build_css_selector_to_find_buttons_in_page_content_by_value(close_election_button_label)
        close_election_button_element = wait_for_element_exists(browser, close_election_button_css_selector, settings.EXPLICIT_WAIT_TIMEOUT)
        close_election_button_element.click()

        wait_a_bit()

        # She clicks on the "Proceed to vote counting" button
        proceed_button_label = "Proceed to vote counting"
        proceed_button_css_selector = build_css_selector_to_find_buttons_in_page_content_by_value(proceed_button_label)
        proceed_button_element = wait_for_element_exists(browser, proceed_button_css_selector, settings.EXPLICIT_WAIT_TIMEOUT)
        proceed_button_element.click()

        wait_a_bit()

        # FIXME: If no voter has cast their vote, it shows a "Internal Server Error" "Error 500" page

        self.administrator_verifies_vote_results()
예제 #24
0
    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()
예제 #25
0
def log_out(browser, election_id=None):
    # In the header of the page, she clicks on the "Log out" link
    logout_link_css_selector = "#logout"
    logout_element = wait_for_element_exists(browser, logout_link_css_selector, settings.EXPLICIT_WAIT_TIMEOUT)
    logout_element.click()

    # She arrives on the election home page. She checks that the "Start" button is present

    # v1:
    # if election_id:
    #     verify_all_elements_have_attribute_value(browser, "#main button", "onclick", "location.href='../../vote.html#uuid=" + election_id + "';")
    # else:
    #     wait_for_element_exists_and_contains_expected_text(browser, "#main button", "Start", settings.EXPLICIT_WAIT_TIMEOUT) # This solution is less robust to variations in browser language settings

    # v2:
    election_home_find_start_button(browser)
예제 #26
0
    def administrator_finishes_tallying_of_election(self):
        self.browser = initialize_browser_for_scenario_2()
        browser = self.browser

        # Alice goes to the election page
        election_url = self.election_page_url
        browser.get(election_url)

        wait_a_bit()

        # She clicks on the "Administer this election" link
        administration_link_label = "Administer this election"
        administration_link_element = wait_for_an_element_with_partial_link_text_exists(browser, administration_link_label, settings.EXPLICIT_WAIT_TIMEOUT)
        administration_link_element.click()

        # She logs in as administrator
        log_in_as_administrator(browser, from_a_login_page=True)

        wait_a_bit()

        # She checks that encrypted tally hash is still the same as the first time it has been displayed to her
        encrypted_tally_hash_css_selector = "#encrypted_tally_hash"
        encrypted_tally_hash_element = wait_for_element_exists_and_has_non_empty_content(browser, encrypted_tally_hash_css_selector)
        encrypted_tally_hash = encrypted_tally_hash_element.get_attribute('innerText')
        assert encrypted_tally_hash == self.encrypted_tally_hash, "Error: Encrypted tally hash displayed to trustee (" + encrypted_tally_hash + ") is not the same as the one displayed to election administrator (" + self.encrypted_tally_hash + ")."

        # She checks that the "DONE?" column of each trustee is to "Yes"
        expected_label = "Yes"
        yes_cells_selector = "#main table tr td:nth-last-child(1)"
        yes_cells_elements = wait_for_elements_exist(browser, yes_cells_selector)
        assert len(yes_cells_elements) > 0, "Error: could not find last cell in table in page"
        for element in yes_cells_elements:
            assert element.get_attribute('innerText') == expected_label, "Error: The last cell of a row in table that is displayed in page has a value that is '" + element.get_attribute('innerText') + "' instead of expected '" + expected_label + "'"

        # She clicks on the "Compute the result" button
        compute_result_button_expected_label = "Compute the result"
        compute_result_button_css_selector = "#main input[type=submit][value='" + compute_result_button_expected_label + "']"
        compute_result_button_element = wait_for_element_exists(browser, compute_result_button_css_selector)
        compute_result_button_element.click()

        wait_a_bit()

        self.administrator_verifies_vote_results()
예제 #27
0
def administrator_edits_election_questions(browser):
    """
    Initial browser (required) state: on the "Preparation of election" page, with questions not edited yet
    Final browser state: on the "Preparation of election" page (with questions edited)

    Alice, as an administrator who has recently started creating an election (election status is draft), configures its questions:
    - She clicks on the "Edit questions" link, to write her own questions
    - She arrives on the Questions page. She checks that the page title is correct
    - She removes answer 3
    - She clicks on the "Save changes" button (this redirects to the "Preparation of election" page)
    """

    # She clicks on the "Edit questions" link, to write her own questions
    edit_questions_link_css_selector = "#edit_questions"
    edit_questions_link_element = wait_for_element_exists(browser, edit_questions_link_css_selector, settings.EXPLICIT_WAIT_TIMEOUT)
    edit_questions_link_element.click()

    wait_a_bit()

    # She arrives on the Questions page. She checks that the page title is correct
    page_title_css_selector = "#header h1"
    page_title_expected_content = "Questions for"
    wait_for_element_exists_and_contains_expected_text(browser, page_title_css_selector, page_title_expected_content, settings.EXPLICIT_WAIT_TIMEOUT)

    # She removes answer 3
    question_to_remove = 3
    remove_button_css_selector = ".question_answer_item:nth-child(" + str(question_to_remove) + ") .btn_remove"
    remove_button_element = browser.find_element_by_css_selector(remove_button_css_selector)
    remove_button_element.click()

    wait_a_bit()

    # She clicks on the "Save changes" button (this redirects to the "Preparation of election" page)
    save_changes_button_expected_label = "Save changes"
    button_elements = browser.find_elements_by_css_selector("button")
    assert len(button_elements)
    save_changes_button_element = button_elements[-1]
    verify_element_label(save_changes_button_element, save_changes_button_expected_label)
    save_changes_button_element.click()

    wait_a_bit()
예제 #28
0
def administrator_starts_creation_of_election(browser, manual_credential_management=False, election_title=None, election_description=None):
    """
    Initial browser (required) state: administrator has just logged in
    Final browser state: on the "Preparation of election" page

    Alice, as an administrator, starts creation of the election:
    - She clicks on the "Prepare a new election" link
    - She picks the Credential management method she wants (function paramenter `manual_credential_management`)
    (- She keeps default value for Authentication method: it is Password, not CAS)
    - She clicks on the "Proceed" button (this redirects to the "Preparation of election" page)
    - She changes values of fields name and description of the election
    - She clicks on the "Save changes button" (the one that is next to the election description field)
    """

    if election_title is None:
        election_title = settings.ELECTION_TITLE

    if election_description is None:
        election_description = settings.ELECTION_DESCRIPTION

    # She clicks on the "Prepare a new election" link
    create_election_link_expected_content = "Prepare a new election"
    links_css_selector = "#main a"
    create_election_link_element = wait_for_element_exists_and_contains_expected_text(browser, links_css_selector, create_election_link_expected_content, settings.EXPLICIT_WAIT_TIMEOUT)
    create_election_link_element.click()

    wait_a_bit()

    if manual_credential_management:
        # She selects the "Manual" radio button, under section "Credential management"
        manual_mode_radio_button_css_selector = "#main input[type=radio][value=manual]"
        manual_mode_radio_button_element = wait_for_element_exists(browser, manual_mode_radio_button_css_selector)
        manual_mode_radio_button_element.click()

    wait_a_bit()

    # She clicks on the "Proceed" button (this redirects to the "Preparation of election" page)
    proceed_button_css_selector = "#main form input[type=submit]"
    proceed_button_element = wait_for_element_exists(browser, proceed_button_css_selector, settings.EXPLICIT_WAIT_TIMEOUT)
    proceed_button_element.click()

    wait_a_bit()

    # She changes values of fields name and description of the election
    election_name_field_css_selector = "#main form input[name=__co_eliom_name]"
    election_name_field_element = wait_for_element_exists(browser, election_name_field_css_selector, settings.EXPLICIT_WAIT_TIMEOUT)
    election_name_field_value = election_title
    election_name_field_element.clear()
    election_name_field_element.send_keys(election_name_field_value)

    wait_a_bit()

    election_description_field_css_selector = "#main form textarea[name=__co_eliom_description]"
    election_description_field_element = browser.find_element_by_css_selector(election_description_field_css_selector)
    election_description_field_value = election_description
    election_description_field_element.clear()
    election_description_field_element.send_keys(election_description_field_value)

    wait_a_bit()

    # She clicks on the "Save changes button" (the one that is next to the election description field)
    save_changes_button_css_selector = "#main > div:nth-child(1) form input[type=submit]" # Warning: form:nth-child(1) selects another form
    save_changes_button_element = browser.find_element_by_css_selector(save_changes_button_css_selector)
    save_changes_button_element.click()

    wait_a_bit()
예제 #29
0
    def administrator_regenerates_passwords_for_some_voters(self):
        # Alice has been contacted by some voters who say they lost their password. She wants to re-generate their passwords and have the platform send them by email. For this, she logs in as administrator.
        browser = self.browser
        log_in_as_administrator(browser)


        # She remembers the list of voters who contacted her and said they lost their password. For this, we pick randomly NUMBER_OF_REGENERATED_PASSWORD_VOTERS voters from all the voters.
        self.voters_email_addresses_who_have_lost_their_password = random.sample(self.voters_email_addresses, settings.NUMBER_OF_REGENERATED_PASSWORD_VOTERS)

        # She selects the election that she wants to edit
        browser = self.browser
        election_to_edit_css_selector = "#election_admin_" + str(self.election_id)
        election_to_edit_elements = wait_for_elements_exist(browser, election_to_edit_css_selector, settings.EXPLICIT_WAIT_TIMEOUT)
        assert len(election_to_edit_elements) > 0
        election_to_edit_elements[0].click()

        wait_a_bit()

        # She arrives to the election administration page. For each voter of the NUMBER_OF_REGENERATED_PASSWORD_VOTERS selected voters:
        for email_address in self.voters_email_addresses_who_have_lost_their_password:
            # She clicks on the "Regenerate and mail a password" link
            regenerate_and_mail_a_password_link_css_selector = "#election_regenpwd"
            regenerate_and_mail_a_password_link_element = wait_for_element_exists(browser, regenerate_and_mail_a_password_link_css_selector, settings.EXPLICIT_WAIT_TIMEOUT)
            regenerate_and_mail_a_password_link_element.click()

            wait_a_bit()

            # She types the e-mail address of the voter in the "Username" field
            username_field_css_selector = "#main input[type=text]"
            username_field_element = wait_for_element_exists(browser, username_field_css_selector, settings.EXPLICIT_WAIT_TIMEOUT)
            username_field_element.send_keys(email_address)

            wait_a_bit()

            # She clicks on the "Submit" button
            submit_button_label = "Submit"
            submit_button_element = find_button_in_page_content_by_value(browser, submit_button_label)
            submit_button_element.click()

            wait_a_bit()

            # She checks that the page shows a confirmation message similar to "A new password has been mailed to [email protected]"
            confirmation_sentence_expected_text = "A new password has been mailed to"
            confirmation_sentence_css_selector = "#main p"
            wait_for_element_exists_and_contains_expected_text(browser, confirmation_sentence_css_selector, confirmation_sentence_expected_text, settings.EXPLICIT_WAIT_TIMEOUT)

            # She clicks on the "Proceed" link
            proceed_link_css_selector = "#generic_proceed_link"
            proceed_link_element = wait_for_element_exists(browser, proceed_link_css_selector, settings.EXPLICIT_WAIT_TIMEOUT)
            proceed_link_element.click()

            wait_a_bit()

            # She arrives back to the election administration page

        """
        Now we do a sanity check that server has really tried to send these emails. For this, we look for email addresses in the temporary file where our fake sendmail executable redirects its inputs to. There should be 3 occurences of "To : xxx@xxx" for users who have lost their password, with respective subjects:
        - "Your credential for election My test election for Scenario 1"
        - "Your password for election My test election for Scenario 1"
        - "Your password for election My test election for Scenario 1"

        And there should be only 2 occurences for other users, with respective subjects:
        - "Your credential for election My test election for Scenario 1"
        - "Your password for election My test election for Scenario 1"
        """

        for email_address in self.voters_email_addresses_who_have_lost_their_password:
            text_to_look_for = 'To: "' + email_address + '"'
            assert self.fake_sent_emails_manager.count_occurences_in_sent_emails(text_to_look_for) is 3

        voters_email_addresses_who_have_not_lost_their_password = set(self.voters_email_addresses) - set(self.voters_email_addresses_who_have_lost_their_password)

        for email_address in voters_email_addresses_who_have_not_lost_their_password:
            text_to_look_for = 'To: "' + email_address + '"'
            assert self.fake_sent_emails_manager.count_occurences_in_sent_emails(text_to_look_for) is 2

        log_out(browser)
예제 #30
0
    def one_voter_votes(self, voter, direct=False):
        browser = self.browser

        if direct:
            url = voter["election_page_url"]
            browser.get(settings.SERVER_URL + "/vote.html#" + urlencode({"url": url}))

        else:
            # Bob has received 2 emails containing an invitation to vote and all necessary credentials (election page URL, username, password). He goes to the election page URL.
            browser.get(voter["election_page_url"])

            wait_a_bit()

            # He clicks on the "Start" button
            start_button_expected_label = "Start"
            start_button_css_selector = "#main button"
            start_button_element = wait_for_element_exists_and_contains_expected_text(browser, start_button_css_selector, start_button_expected_label, settings.EXPLICIT_WAIT_TIMEOUT)
            start_button_element.click()

        wait_a_bit()

        # A loading screen appears, then another screen appears. He clicks on the "Here" button
        here_button_expected_label = "here"
        here_button_css_selector = "#input_code button"
        here_button_element = wait_for_element_exists_and_contains_expected_text(browser, here_button_css_selector, here_button_expected_label, settings.EXPLICIT_WAIT_TIMEOUT)
        here_button_element.click()

        wait_a_bit()

        # A modal opens (it is an HTML modal created using Window.prompt()), with an input field. He types his credential.
        credential_prompt = Alert(browser)
        credential_prompt.send_keys(voter["credential"])
        credential_prompt.accept()

        wait_a_bit()

        # A new screen appears, which has a title "Step 2/6: Answer to questions", and a content:
        # "Question 1?"
        # "Question #1 of 1 — select between 1 and 2 answer(s)"
        # [ ] "Answer 1"
        # [ ] "Answer 2"
        # [Next]
        # (where "[ ]" is a checkbox, and [Next] is a button)

        # He fills his votes to each answer of the question
        answers_css_selector = "#question_div input[type=checkbox]"
        answers_elements = wait_for_elements_exist(browser, answers_css_selector, settings.EXPLICIT_WAIT_TIMEOUT) # or we could use find_element_by_xpath("//div[@id='question_div']/input[@type='checkbox'][2]")

        assert len(answers_elements) is 2
        question1_answer1_element = answers_elements[0]
        question1_answer2_element = answers_elements[1]
        voter_vote_to_question_1_answer_1 = voter["votes"]["question1"]["answer1"]
        voter_vote_to_question_1_answer_2 = voter["votes"]["question1"]["answer2"]
        voter_vote_to_question_1_answer_1_is_checked = question1_answer1_element.get_attribute('checked')
        voter_vote_to_question_1_answer_2_is_checked = question1_answer2_element.get_attribute('checked')
        assert voter_vote_to_question_1_answer_1_is_checked is None
        assert voter_vote_to_question_1_answer_2_is_checked is None
        if voter_vote_to_question_1_answer_1:
            question1_answer1_element.click()
        if voter_vote_to_question_1_answer_2:
            question1_answer2_element.click()

        wait_a_bit()

        # He clicks on the "Next" button
        next_button_expected_label = "Next"
        next_button_css_selector = "#question_div button"
        next_button_element = wait_for_element_exists_and_contains_expected_text(browser, next_button_css_selector, next_button_expected_label, settings.EXPLICIT_WAIT_TIMEOUT)
        next_button_element.click()

        wait_a_bit()

        """
        A new screen appears, showing:

        Step 3/6: Review and encrypt
        Question 1?
        - Answer 1

        Your ballot has been successfully encrypted, but has not been cast yet!

        Your smart ballot tracker is sLRilXoAYcodIrjWrOqPrVXLNlRyCJAqFeeHZ4WCajU

        We invite you to save it in order to check later that it is taken into account.

        [Continue]
        [Restart]
        """

        # He remembers the smart ballot tracker that is displayed.
        smart_ballot_tracker_css_selector = "#ballot_tracker"
        smart_ballot_tracker_element = wait_for_element_exists_and_has_non_empty_content(browser, smart_ballot_tracker_css_selector, settings.EXPLICIT_WAIT_TIMEOUT)
        smart_ballot_tracker_value = smart_ballot_tracker_element.get_attribute('innerText')
        assert len(smart_ballot_tracker_value) > 5

        voter["smart_ballot_tracker"] = smart_ballot_tracker_value

        # He clicks on the "Continue" button
        next_button_expected_label = "Continue"
        next_button_css_selector = "#div_submit input[type=submit][value='" + next_button_expected_label + "']"
        next_button_element = browser.find_element_by_css_selector(next_button_css_selector)
        next_button_element.click()

        wait_a_bit()

        # He types his voter username and password, and submits the form
        username_field_css_selector = "#main input[name=username]"
        username_field_element = wait_for_element_exists(browser, username_field_css_selector, settings.EXPLICIT_WAIT_TIMEOUT)
        username_field_element.send_keys(voter["username"])

        password_field_css_selector = "#main input[name=password]"
        password_field_element = browser.find_element_by_css_selector(password_field_css_selector)
        password_field_element.send_keys(voter["password"])

        wait_a_bit()

        password_field_element.submit()

        wait_a_bit()

        # He checks that the smart ballot tracker value that appears on screen is the same as the one he noted
        smart_ballot_tracker_verification_css_selector = "#ballot_tracker"
        smart_ballot_tracker_verification_element = wait_for_element_exists_and_has_non_empty_content(browser, smart_ballot_tracker_verification_css_selector, settings.EXPLICIT_WAIT_TIMEOUT)
        smart_ballot_tracker_verification_value = smart_ballot_tracker_verification_element.get_attribute('innerText')
        assert len(smart_ballot_tracker_verification_value) > 5

        assert smart_ballot_tracker_verification_value == voter["smart_ballot_tracker"]

        # He clicks on the "I cast my vote" button
        submit_button_css_selector = "#main input[type=submit]"
        submit_button_element = browser.find_element_by_css_selector(submit_button_css_selector)
        submit_button_element.click()
예제 #31
0
    def administrator_starts_creation_of_manual_election(self):
        # # Setting up a new election (action of the administrator)

        browser = self.browser

        # Alice has been given administrator rights on an online voting app called Belenios. She goes
        # to check out its homepage and logs in
        log_in_as_administrator(browser)

        # She starts creation of the election:
        # - She clicks on the "Prepare a new election" link
        # - She picks the Credential management method: manual
        # (- She keeps default value for Authentication method: it is Password, not CAS)
        # - She clicks on the "Proceed" button (this redirects to the "Preparation of election" page)
        # - She changes values of fields name and description of the election
        # - She clicks on the "Save changes button" (the one that is next to the election description field)
        administrator_starts_creation_of_election(browser, True)

        # She remembers the URL of the draft election administration page
        self.draft_election_administration_page_url = browser.current_url

        # She edits election's questions:
        # - She clicks on the "Edit questions" link, to write her own questions
        # - She arrives on the Questions page. She checks that the page title is correct
        # - She removes answer 3
        # - She clicks on the "Save changes" button (this redirects to the "Preparation of election" page)
        administrator_edits_election_questions(browser)

        # She sets election's voters:
        # - She clicks on the "Edit voters" link, to then type the list of voters
        # - She types N e-mail addresses (the list of invited voters)
        # - She clicks on the "Add" button to submit changes
        # - She clicks on "Return to draft page" link
        self.voters_email_addresses = random_email_addresses_generator(settings.NUMBER_OF_INVITED_VOTERS)
        administrator_sets_election_voters(browser, self.voters_email_addresses)

        # In "Authentication" section, she clicks on the "Generate and mail missing passwords" button
        generate_and_mail_missing_passwords_button_label = "Generate and mail missing passwords"
        generate_and_mail_missing_passwords_button_css_selector = "#main input[type=submit][value='" + generate_and_mail_missing_passwords_button_label + "']"
        generate_and_mail_missing_passwords_button_element = wait_for_element_exists(browser, generate_and_mail_missing_passwords_button_css_selector, settings.EXPLICIT_WAIT_TIMEOUT)
        generate_and_mail_missing_passwords_button_element.click() # FIXME: This click does not get triggered when we have maximized the browser window

        wait_a_bit()

        # She checks that the page contains expected confirmation text, instead of an error (TODO: explain in which case an error can happen, and check that it does not show)
        confirmation_sentence_expected_text = "Passwords have been generated and mailed!"
        confirmation_sentence_css_selector = "#main p"
        wait_for_element_exists_and_contains_expected_text(browser, confirmation_sentence_css_selector, confirmation_sentence_expected_text, settings.EXPLICIT_WAIT_TIMEOUT)

        # She clicks on the "Proceed" link (this redirects to the "Preparation of election" page)
        proceed_link_expected_label = "Proceed"
        proceed_link_css_selector = "#main a"
        proceed_link_element = wait_for_element_exists_and_contains_expected_text(browser, proceed_link_css_selector, proceed_link_expected_label, settings.EXPLICIT_WAIT_TIMEOUT)
        proceed_link_element.click()

        wait_a_bit()

        # In "Credentials" section, she clicks on "Credential management" link
        credential_management_expected_label = "Credential management"
        credential_management_link_element = wait_for_an_element_with_partial_link_text_exists(browser, credential_management_expected_label)
        credential_management_link_element.click()

        wait_a_bit()

        # She remembers the link displayed
        link_for_credential_authority_css_selector = "#main a"
        link_for_credential_authority_element = wait_for_element_exists_and_has_non_empty_content(browser, link_for_credential_authority_css_selector)
        link_label = link_for_credential_authority_element.get_attribute('innerText').strip()
        self.credential_authority_link = link_label

        # She sends the remembered link to the credential authority by email (actually we don't need to send anything because we will act as the credential authority)

        # Optionnaly, she logs out
        # log_out(browser)

        # She closes the browser window
        browser.quit()
예제 #32
0
def administrator_starts_creation_of_election(
        browser,
        manual_credential_management=False,
        election_title=None,
        election_description=None,
        initiator_contact=None):
    """
    Initial browser (required) state: administrator has just logged in
    Final browser state: on the "Preparation of election" page

    Alice, as an administrator, starts creation of the election:
    - She clicks on the "Prepare a new election" link
    - She picks the Credential management method she wants (function paramenter `manual_credential_management`)
    (- She keeps default value for Authentication method: it is Password, not CAS)
    - She clicks on the "Proceed" button (this redirects to the "Preparation of election" page)
    - In the "Name and description of the election" section, she changes values of fields name and description of the election
    - She clicks on the "Save changes button" (the one that is next to the election description field)
    - In "Contact" section, she changes the value of "contact" field
    - She clicks on the "Save changes" button (the one that is in the "Contact" section)
    """

    if election_title is None:
        election_title = settings.ELECTION_TITLE

    if election_description is None:
        election_description = settings.ELECTION_DESCRIPTION

    if initiator_contact is None:
        initiator_contact = settings.INITIATOR_CONTACT

    # She clicks on the "Prepare a new election" link
    create_election_link_expected_content = "Prepare a new election"
    links_css_selector = "#main a"
    create_election_link_element = wait_for_element_exists_and_contains_expected_text(
        browser, links_css_selector, create_election_link_expected_content,
        settings.EXPLICIT_WAIT_TIMEOUT)
    create_election_link_element.click()

    wait_a_bit()

    if manual_credential_management:
        # She selects the "Manual" radio button, under section "Credential management"
        manual_mode_radio_button_css_selector = "#main input[type=radio][value=manual]"
        manual_mode_radio_button_element = wait_for_element_exists(
            browser, manual_mode_radio_button_css_selector)
        manual_mode_radio_button_element.click()

    wait_a_bit()

    # She clicks on the "Proceed" button (this redirects to the "Preparation of election" page)
    proceed_button_css_selector = "#main form input[type=submit]"
    proceed_button_element = wait_for_element_exists(
        browser, proceed_button_css_selector, settings.EXPLICIT_WAIT_TIMEOUT)
    proceed_button_element.click()

    wait_a_bit()

    # In the "Name and description of the election" section, she changes values of fields name and description of the election
    election_name_field_css_selector = "#name_and_description_form input[name=__co_eliom_name]"
    election_name_field_element = wait_for_element_exists(
        browser, election_name_field_css_selector,
        settings.EXPLICIT_WAIT_TIMEOUT)
    election_name_field_value = election_title
    election_name_field_element.clear()
    election_name_field_element.send_keys(election_name_field_value)

    wait_a_bit()

    election_description_field_css_selector = "#name_and_description_form textarea[name=__co_eliom_description]"
    election_description_field_element = browser.find_element_by_css_selector(
        election_description_field_css_selector)
    election_description_field_value = election_description
    election_description_field_element.clear()
    election_description_field_element.send_keys(
        election_description_field_value)

    wait_a_bit()

    # She clicks on the "Save changes" button (the one that is next to the election description field)
    save_changes_button_css_selector = "#name_and_description_form input[type=submit]"
    save_changes_button_element = browser.find_element_by_css_selector(
        save_changes_button_css_selector)
    save_changes_button_element.click()

    wait_a_bit()

    # In "Contact" section, she changes the value of "contact" field
    election_contact_field_css_selector = "#form_contact input[name=__co_eliom_contact]"
    election_contact_field_element = browser.find_element_by_css_selector(
        election_contact_field_css_selector)
    election_contact_field_value = initiator_contact
    election_contact_field_element.clear()
    election_contact_field_element.send_keys(election_contact_field_value)

    wait_a_bit()

    # She clicks on the "Save changes" button (the one that is in the "Contact" section)
    contact_section_save_changes_button_css_selector = "#form_contact input[type=submit]"
    contact_section_save_changes_button_element = browser.find_element_by_css_selector(
        contact_section_save_changes_button_css_selector)
    contact_section_save_changes_button_element.click()

    wait_a_bit()

    # In "Public name of the administrator" section, she changes the value of "name" field
    admin_name_field_css_selector = "#form_admin_name input[name=__co_eliom_name]"
    admin_name_field_element = browser.find_element_by_css_selector(
        admin_name_field_css_selector)
    admin_name_field_value = "Election initiator"
    admin_name_field_element.clear()
    admin_name_field_element.send_keys(admin_name_field_value)

    wait_a_bit()

    # She clicks on the "Save changes" button (the one that is in the "Contact" section)
    admin_name_save_changes_button_css_selector = "#form_admin_name input[type=submit]"
    admin_name_save_changes_button_element = browser.find_element_by_css_selector(
        admin_name_save_changes_button_css_selector)
    admin_name_save_changes_button_element.click()

    wait_a_bit()
예제 #33
0
    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)
예제 #34
0
    def trustees_do_partial_decryption(self, max_trustees=None):
        # Each of the `T` trustees (limited to `max_trustees`) will do the following process:
        for idx, trustee_email_address in enumerate(
                settings.TRUSTEES_EMAIL_ADDRESSES):
            if max_trustees is not None and idx >= max_trustees:  # TODO: Maybe we should pick trustees randomly in the list of trustees instead of always the first few ones?
                break
            # He opens the link that Alice (the election administrator) has sent to him
            self.browser = initialize_browser_for_scenario_2()
            browser = self.browser
            link_for_trustee = self.closed_election_tally_links_for_trustees[
                idx]
            browser.get(link_for_trustee)

            wait_a_bit()

            # He verifies that the "private key" input field is empty (at the beginning)
            private_key_field_css_selector = "#private_key"
            private_key_field_element = wait_for_element_exists(
                browser, private_key_field_css_selector)
            assert private_key_field_element.get_attribute('value') == ""

            # One trustee uploads his private key file, the other copy-pastes its contents into the form field
            private_key_file = self.downloaded_files_paths_per_trustee[
                trustee_email_address]["private key"]
            if idx % 2 == 0:
                # He clicks on the "Browse..." button and selects his private key file (initially downloaded as `private_key.json` by default)
                browse_button_css_selector = "input[id=private_key_file][type=file]"
                browse_button_element = wait_for_element_exists(
                    browser, browse_button_css_selector)
                path_of_file_to_upload = private_key_file
                browse_button_element.clear()
                browse_button_element.send_keys(path_of_file_to_upload)

                # He waits until the "private key" input field (that has id "#private_key") becomes not empty anymore. This is because once the user has selected the file to upload, the Javascript code in the page detects that a file has been selected, reads it, and fills "private key" input field with file's contents. The computation triggered by click on the "Compute decryption factors" button will use the value of this field, not directly the uploaded file contents.
                private_key_field_expected_non_empty_attribute = "value"
                wait_for_element_exists_and_has_non_empty_attribute(
                    browser, private_key_field_css_selector,
                    private_key_field_expected_non_empty_attribute)
            else:
                with open(private_key_file) as myfile:
                    private_key_field_element.send_keys(myfile.read())
                wait_a_bit()

            # He clicks on the "Compute decryption factors" button
            compute_button_css_selector = "button[id=compute]"
            compute_button_element = wait_for_element_exists(
                browser, compute_button_css_selector)
            compute_button_element.click()

            # He checks that the text field below (used as visual feedback) now contains text
            visual_feedback_css_selector = "#pd"
            visual_feedback_expected_non_empty_attribute = "value"
            try:
                wait_for_element_exists_and_has_non_empty_attribute(
                    browser, visual_feedback_css_selector,
                    visual_feedback_expected_non_empty_attribute, 60 * 2)
            except UnexpectedAlertPresentException as e:
                raise Exception(
                    "An alert was displayed at a moment when no alert should be displayed. Alert displayed probably contains error information about uploaded file contents."
                ) from e

            # He clicks on the "Submit" button
            submit_button_css_selector = "#pd_done input[type=submit]"
            submit_button_element = wait_for_element_exists(
                browser, submit_button_css_selector)
            submit_button_element.click()

            wait_a_bit()

            # He checks that next screen contains a confirmation sentence
            confirmation_sentence_expected_text = "Your partial decryption has been received and checked!"
            confirmation_sentence_css_selector = "#main p"
            wait_for_element_exists_and_contains_expected_text(
                browser, confirmation_sentence_css_selector,
                confirmation_sentence_expected_text,
                settings.EXPLICIT_WAIT_TIMEOUT)

            # He closes the window
            browser.quit()
예제 #35
0
    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()
예제 #36
0
    def administrator_invites_trustees(self):
        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
        # TODO: use a better selector: edit Belenios page to use an ID in this DOM element
        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()

        # She adds two trustees (their email address), and remembers the link she will send to each trustee
        self.links_for_trustees = []
        email_address_field_css_selector = "#main form input[type=text]"
        submit_button_css_selector = "#main form input[type=submit][value=Add]"

        for idx, email_address in enumerate(settings.TRUSTEES_EMAIL_ADDRESSES):
            email_address_field_element = wait_for_element_exists(browser, email_address_field_css_selector)
            email_address_field_element.clear()
            email_address_field_element.send_keys(email_address)

            submit_button_element = wait_for_element_exists(browser, submit_button_css_selector)
            submit_button_element.click()

            trustee_link_css_selector = "#main table tr:nth-child(" + str(idx + 3) + ") td:nth-child(3) a"
            trustee_link_element = wait_for_element_exists_and_has_non_empty_content(browser, trustee_link_css_selector)
            self.links_for_trustees.append(trustee_link_element.get_attribute('href'))

            wait_a_bit()

        # She sends to each trustee an email containing their own link
        subject = "Link to generate the decryption key"
        content_format = """\
Dear trustee,

You will find below the link to generate your private decryption key, used to tally the election.

{link_for_trustee}

Here's the instructions:
1. click on the link
2. click on "generate a new key pair"
3. your private key will appear in another window or tab. Make sure
you SAVE IT properly otherwise it will not possible to tally and the
election will be canceled.
4. in the first window, click on "submit" to send the public part of
your key, used encrypt the votes. For verification purposes, you
should save this part (that starts with "pok" "challenge"), for
example sending yourself an email.

Regarding your private key, it is crucial you save it (otherwise the
election will be canceled) and store it securely (if your private key
is known together with the private keys of the other trustees, then
vote privacy is no longer guaranteed). We suggest two options:
1. you may store the key on a USB stick and store it in a safe.
2. Or you may simply print it and store it in a safe.
Of course, more cryptographic solutions are welcome as well.

Thank you for your help,

--
The election administrator.\
"""
        for idx, trustee_email_address in enumerate(settings.TRUSTEES_EMAIL_ADDRESSES):
            custom_content = content_format.format(link_for_trustee=self.links_for_trustees[idx])
            self.fake_sent_emails_manager.send_email(settings.ADMINISTRATOR_EMAIL_ADDRESS, trustee_email_address, subject, custom_content)

        # Optionnaly, she logs out
        # log_out(browser)

        # She closes the window
        browser.quit()
예제 #37
0
    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)
예제 #38
0
    def administrator_creates_election(self):
        # # Setting up a new election (action of the administrator)

        browser = self.browser

        # Alice has been given administrator rights on an online voting app called Belenios. She goes
        # to check out its homepage and logs in
        log_in_as_administrator(browser)

        # She starts creation of the election:
        # - She clicks on the "Prepare a new election" link
        # (- She keeps default values on the form: Credential management is automatic (not manual), and Authentication method is Password, not CAS)
        # - She clicks on the "Proceed" button (this redirects to the "Preparation of election" page)
        # - She changes values of fields name and description of the election
        # - She clicks on the "Save changes button" (the one that is next to the election description field)
        administrator_starts_creation_of_election(browser)

        # She edits election's questions:
        # - She clicks on the "Edit questions" link, to write her own questions
        # - She arrives on the Questions page. She checks that the page title is correct
        # - She removes answer 3
        # - She clicks on the "Save changes" button (this redirects to the "Preparation of election" page)
        administrator_edits_election_questions(browser)

        # She sets election's voters:
        # - She clicks on the "Edit voters" link, to then type the list of voters
        # - She types N e-mail addresses (the list of invited voters)
        # - She clicks on the "Add" button to submit changes
        # - She clicks on "Return to draft page" link
        self.voters_email_addresses = random_email_addresses_generator(settings.NUMBER_OF_INVITED_VOTERS)
        administrator_sets_election_voters(browser, self.voters_email_addresses)

        # She clicks on button "Generate on server"
        generate_on_server_button_label = "Generate on server"
        generate_on_server_button_css_selector = build_css_selector_to_find_buttons_in_page_content_by_value(generate_on_server_button_label)
        generate_on_server_button_element = wait_for_element_exists(browser, generate_on_server_button_css_selector, settings.EXPLICIT_WAIT_TIMEOUT)
        generate_on_server_button_element.click()

        wait_a_bit()

        # (Server sends emails to voters.) She checks that server does not show any error that would happen when trying to send these emails (this can happen if sendmail is not configured)
        confirmation_sentence_expected_text = "Credentials have been generated and mailed!"
        confirmation_sentence_css_selector = "#main p"
        wait_for_element_exists_and_contains_expected_text(browser, confirmation_sentence_css_selector, confirmation_sentence_expected_text, settings.EXPLICIT_WAIT_TIMEOUT)

        # Now we do a sanity check that server has really tried to send emails. For this, we look for email addresses in the temporary file where our fake sendmail executable redirects its inputs to.

        """
        An email sent by Belenios (using sendmail or using the fake sendmail) to a voter looks like this:

Content-type: text/plain; charset="UTF-8"
Content-transfer-encoding: quoted-printable
From: Belenios public server <*****@*****.**>
To: "*****@*****.**"
 <*****@*****.**>
Subject: Your credential for election My test election for Scenario 1
MIME-Version: 1.0
X-Mailer: OcamlNet (ocamlnet.sourceforge.net)
Date: Wed, 31 Oct 2018 15:22:27 +0100

You are listed as a voter for the election

  My test election for Scenario 1

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: yQVDQaKSAQVjdZq
Page of the election: http://localhost:8001/elections/AFFNDEPnpy21bw/

Note that you are allowed to vote several times.  Only the last vote
counts.

----------

Vous =C3=AAtes enregistr=C3=A9(e) en tant qu=27=C3=A9lecteur(trice) pour=20=
l=27=C3=A9lection

  My test election for Scenario 1

Veuillez trouver ci-dessous votre code de vote. Pour soumettre un
bulletin, vous aurez =C3=A9galement besoin d=27un mot de passe, envoy=C3=A9=
 dans
un e-mail s=C3=A9par=C3=A9. Soyez attentif(ve), le mot de passe et le cod=
e de
vote se ressemblent mais jouent des r=C3=B4les diff=C3=A9rents. Le syst=C3=
=A8me vous
demandera votre code de vote d=C3=A8s l=27entr=C3=A9e dans l=27isoloir=20=
virtuel. Le
nom d=27utilisateur et le mot de passe sont n=C3=A9cessaires lorsque votr=
e
bulletin est pr=C3=AAt =C3=A0 =C3=AAtre soumis.

Code de vote=C2=A0: yQVDQaKSAQVjdZq
Page de l=27=C3=A9lection=C2=A0: http://localhost:8001/elections/AFFNDEPn=
py21bw/

Notez que vous pouvez voter plusieurs fois. Seul le dernier vote est
pris en compte.

--=20
        """

        email_address_to_look_for = self.voters_email_addresses[0]
        text_to_look_for = 'To: "' + email_address_to_look_for + '"'
        email_address_found = self.fake_sent_emails_manager.find_in_sent_emails(text_to_look_for)
        assert email_address_found, "Text '" + email_address_to_look_for + "'' not found in fake sendmail log file"


        # She clicks on the "Proceed" link
        proceed_link_css_selector = "#generic_proceed_link"
        proceed_link_element = wait_for_element_exists(browser, proceed_link_css_selector, settings.EXPLICIT_WAIT_TIMEOUT)
        proceed_link_element.click()

        wait_a_bit()

        # In "Authentication" section, she clicks on the "Generate and mail missing passwords" button
        generate_and_mail_missing_passwords_button_label = "Generate and mail missing passwords"
        generate_and_mail_missing_passwords_button_element = wait_for_element_exists(browser, build_css_selector_to_find_buttons_in_page_content_by_value(generate_and_mail_missing_passwords_button_label), settings.EXPLICIT_WAIT_TIMEOUT)
        generate_and_mail_missing_passwords_button_element.click()

        wait_a_bit()

        # She checks that the page contains expected confirmation text, instead of an error (TODO: explain in which case an error can happen, and check that it does not show)
        confirmation_sentence_expected_text = "Passwords have been generated and mailed!"
        confirmation_sentence_css_selector = "#main p"
        wait_for_element_exists_and_contains_expected_text(browser, confirmation_sentence_css_selector, confirmation_sentence_expected_text, settings.EXPLICIT_WAIT_TIMEOUT)

        # She clicks on the "Proceed" link (this redirects to the "Preparation of election" page)
        proceed_link_css_selector = "#generic_proceed_link"
        proceed_link_element = wait_for_element_exists(browser, proceed_link_css_selector, settings.EXPLICIT_WAIT_TIMEOUT)
        proceed_link_element.click()

        wait_a_bit()

        self.election_page_url = administrator_validates_creation_of_election(browser)
        console_log("election_page_url:", self.election_page_url)
        self.election_id = election_page_url_to_election_id(self.election_page_url)
        console_log("election_id:", self.election_id)

        log_out(browser)
예제 #39
0
    def administrator_regenerates_passwords_for_some_voters(self):
        # Alice has been contacted by some voters who say they lost their password. She wants to re-generate their passwords and have the platform send them by email. For this, she logs in as administrator.
        browser = self.browser
        log_in_as_administrator(browser)

        # She remembers the list of voters who contacted her and said they lost their password. For this, we pick randomly NUMBER_OF_REGENERATED_PASSWORD_VOTERS voters from all the voters.
        self.voters_email_addresses_who_have_lost_their_password = random.sample(
            self.voters_email_addresses,
            settings.NUMBER_OF_REGENERATED_PASSWORD_VOTERS)

        # She selects the election that she wants to edit
        browser = self.browser
        election_to_edit_css_selector = "#election_admin_" + str(
            self.election_id)
        election_to_edit_elements = wait_for_elements_exist(
            browser, election_to_edit_css_selector,
            settings.EXPLICIT_WAIT_TIMEOUT)
        assert len(election_to_edit_elements) > 0
        election_to_edit_elements[0].click()

        wait_a_bit()

        # She arrives to the election administration page. For each voter of the NUMBER_OF_REGENERATED_PASSWORD_VOTERS selected voters:
        for email_address in self.voters_email_addresses_who_have_lost_their_password:
            # She clicks on the "Regenerate and mail a password" link
            regenerate_and_mail_a_password_link_css_selector = "#election_regenpwd"
            regenerate_and_mail_a_password_link_element = wait_for_element_exists(
                browser, regenerate_and_mail_a_password_link_css_selector,
                settings.EXPLICIT_WAIT_TIMEOUT)
            regenerate_and_mail_a_password_link_element.click()

            wait_a_bit()

            # She types the e-mail address of the voter in the "Username" field
            username_field_css_selector = "#main input[type=text]"
            username_field_element = wait_for_element_exists(
                browser, username_field_css_selector,
                settings.EXPLICIT_WAIT_TIMEOUT)
            username_field_element.send_keys(email_address)

            wait_a_bit()

            # She clicks on the "Submit" button
            submit_button_label = "Submit"
            submit_button_element = find_button_in_page_content_by_value(
                browser, submit_button_label)
            submit_button_element.click()

            wait_a_bit()

            # She checks that the page shows a confirmation message similar to "A new password has been mailed to [email protected]"
            confirmation_sentence_expected_text = "A new password has been mailed to"
            confirmation_sentence_css_selector = "#main p"
            wait_for_element_exists_and_contains_expected_text(
                browser, confirmation_sentence_css_selector,
                confirmation_sentence_expected_text,
                settings.EXPLICIT_WAIT_TIMEOUT)

            # She clicks on the "Proceed" link
            proceed_link_css_selector = "#generic_proceed_link"
            proceed_link_element = wait_for_element_exists(
                browser, proceed_link_css_selector,
                settings.EXPLICIT_WAIT_TIMEOUT)
            proceed_link_element.click()

            wait_a_bit()

            # She arrives back to the election administration page
        """
        Now we do a sanity check that server has really tried to send these emails. For this, we look for email addresses in the temporary file where our fake sendmail executable redirects its inputs to. There should be 3 occurences of "To : xxx@xxx" for users who have lost their password, with respective subjects:
        - "Your credential for election My test election for Scenario 1"
        - "Your password for election My test election for Scenario 1"
        - "Your password for election My test election for Scenario 1"

        And there should be only 2 occurences for other users, with respective subjects:
        - "Your credential for election My test election for Scenario 1"
        - "Your password for election My test election for Scenario 1"
        """

        for email_address in self.voters_email_addresses_who_have_lost_their_password:
            text_to_look_for = 'To: "' + email_address + '"'
            assert self.fake_sent_emails_manager.count_occurences_in_sent_emails(
                text_to_look_for) is 3

        voters_email_addresses_who_have_not_lost_their_password = set(
            self.voters_email_addresses) - set(
                self.voters_email_addresses_who_have_lost_their_password)

        for email_address in voters_email_addresses_who_have_not_lost_their_password:
            text_to_look_for = 'To: "' + email_address + '"'
            assert self.fake_sent_emails_manager.count_occurences_in_sent_emails(
                text_to_look_for) is 2

        log_out(browser, self.election_id)
예제 #40
0
    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()
예제 #41
0
    def administrator_invites_trustees_and_sets_threshold(self):
        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()

        # She clicks on the "threshold mode" link
        threshold_mode_link_label = "threshold mode"
        threshold_mode_link_element = wait_for_an_element_with_partial_link_text_exists(
            browser, threshold_mode_link_label)
        threshold_mode_link_element.click()

        wait_a_bit()

        # She adds `NUMBER_OF_TRUSTEES` trustees (their email address), and remembers the link she will send to each trustee
        # (The threshold field appears only after user has added the first trustee)
        self.links_for_trustees = []
        email_address_field_css_selector = "#main form input[name=__co_eliom_id]"  # TODO: Maybe we should edit Belenios' HTML template to rename `__co_eliom_id` to something more explicit, like `__co_eliom_new_trustee_email_address`
        submit_button_css_selector = "#main form input[type=submit][value=Add]"

        for idx, email_address in enumerate(settings.TRUSTEES_EMAIL_ADDRESSES):
            email_address_field_element = wait_for_element_exists(
                browser, email_address_field_css_selector)
            email_address_field_element.clear()
            email_address_field_element.send_keys(email_address)

            submit_button_element = wait_for_element_exists(
                browser, submit_button_css_selector)
            submit_button_element.click()

            trustee_link_css_selector = "#main table tbody tr:nth-of-type(" + str(
                idx + 2) + ") td:nth-of-type(3) a"
            trustee_link_element = wait_for_element_exists_and_has_non_empty_content(
                browser, trustee_link_css_selector)
            self.links_for_trustees.append(
                trustee_link_element.get_attribute('href'))

            wait_a_bit()

        # In the field next to "Threshold:", she types the value of `U` (aka `TRUSTEES_THRESHOLD_VALUE`)
        threshold_value_field_css_selector = "#main form input[name=__co_eliom_threshold]"
        threshold_value_field_element = wait_for_element_exists(
            browser, threshold_value_field_css_selector,
            settings.EXPLICIT_WAIT_TIMEOUT)
        threshold_value_field_value = settings.TRUSTEES_THRESHOLD_VALUE
        threshold_value_field_element.clear()
        threshold_value_field_element.send_keys(threshold_value_field_value)

        wait_a_bit()

        # She clicks on the "Set" button
        submit_button_css_selector = "#main form input[type=submit][value=Set]"
        submit_button_element = wait_for_element_exists(
            browser, submit_button_css_selector)
        submit_button_element.click()

        wait_a_bit()

        # She checks that in the table, the "STATE" column is "1a" on every row
        expected_value = "1a"
        verify_all_trustee_states_in_table(browser, expected_value)

        # She sends to each trustee an email containing their own link
        subject = "Link to generate the decryption key"
        content_format = """\
Dear trustee,

You will find below the link to generate your private decryption key, used to tally the election.

{link_for_trustee}

Here's the instructions:
1. click on the link
2. click on "generate a new key pair"
3. your private key will appear in another window or tab. Make sure
you SAVE IT properly otherwise it will not possible to tally and the
election will be canceled.
4. in the first window, click on "submit" to send the public part of
your key, used encrypt the votes. For verification purposes, you
should save this part (that starts with "pok" "challenge"), for
example sending yourself an email.

Regarding your private key, it is crucial you save it (otherwise the
election will be canceled) and store it securely (if your private key
is known together with the private keys of the other trustees, then
vote privacy is no longer guaranteed). We suggest two options:
1. you may store the key on a USB stick and store it in a safe.
2. Or you may simply print it and store it in a safe.
Of course, more cryptographic solutions are welcome as well.

Thank you for your help,

--
The election administrator.\
"""
        for idx, trustee_email_address in enumerate(
                settings.TRUSTEES_EMAIL_ADDRESSES):
            custom_content = content_format.format(
                link_for_trustee=self.links_for_trustees[idx])
            self.fake_sent_emails_manager.send_email(
                settings.ADMINISTRATOR_EMAIL_ADDRESS, trustee_email_address,
                subject, custom_content)

        # Optionnaly, she logs out
        # log_out(browser)

        # She closes the window
        browser.quit()
예제 #42
0
    def administrator_invites_trustees(self):
        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
        # TODO: use a better selector: edit Belenios page to use an ID in this DOM element
        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()

        # She adds two trustees (their email address), and remembers the link she will send to each trustee
        self.links_for_trustees = []
        email_address_field_css_selector = "#main form input[type=text]"
        submit_button_css_selector = "#main form input[type=submit][value=Add]"

        for idx, email_address in enumerate(settings.TRUSTEES_EMAIL_ADDRESSES):
            email_address_field_element = wait_for_element_exists(
                browser, email_address_field_css_selector)
            email_address_field_element.clear()
            email_address_field_element.send_keys(email_address)

            submit_button_element = wait_for_element_exists(
                browser, submit_button_css_selector)
            submit_button_element.click()

            wait_a_bit()

            trustee_link_css_selector = "#main table tr:nth-of-type(" + str(
                idx + 3
            ) + ") td:nth-of-type(4) a"  # First row of table corresponds to column titles. Second row correpond to server trustee.
            trustee_link_element = wait_for_element_exists_and_has_non_empty_content(
                browser, trustee_link_css_selector)
            self.links_for_trustees.append(
                trustee_link_element.get_attribute('href'))

            wait_a_bit()

        # She sends to each trustee an email containing their own link
        subject = "Link to generate the decryption key"
        content_format = """\
Dear trustee,

You will find below the link to generate your private decryption key, used to tally the election.

{link_for_trustee}

Here's the instructions:
1. click on the link
2. click on "generate a new key pair"
3. your private key will appear in another window or tab. Make sure
you SAVE IT properly otherwise it will not possible to tally and the
election will be canceled.
4. in the first window, click on "submit" to send the public part of
your key, used encrypt the votes. For verification purposes, you
should save this part (that starts with "pok" "challenge"), for
example sending yourself an email.

Regarding your private key, it is crucial you save it (otherwise the
election will be canceled) and store it securely (if your private key
is known together with the private keys of the other trustees, then
vote privacy is no longer guaranteed). We suggest two options:
1. you may store the key on a USB stick and store it in a safe.
2. Or you may simply print it and store it in a safe.
Of course, more cryptographic solutions are welcome as well.

Thank you for your help,

--
The election administrator.\
"""
        for idx, trustee_email_address in enumerate(
                settings.TRUSTEES_EMAIL_ADDRESSES):
            custom_content = content_format.format(
                link_for_trustee=self.links_for_trustees[idx])
            self.fake_sent_emails_manager.send_email(
                settings.ADMINISTRATOR_EMAIL_ADDRESS, trustee_email_address,
                subject, custom_content)

        # Optionnaly, she logs out
        # log_out(browser)

        # She closes the window
        browser.quit()
예제 #43
0
    def administrator_creates_election(self, nh_question=False):
        # # Setting up a new election (action of the administrator)

        browser = self.browser

        # Alice has been given administrator rights on an online voting app called Belenios. She goes
        # to check out its homepage and logs in
        log_in_as_administrator(browser)

        # She starts creation of the election:
        # - She clicks on the "Prepare a new election" link
        # (- She keeps default values on the form: Credential management is automatic (not manual), and Authentication method is Password, not CAS)
        # - She clicks on the "Proceed" button (this redirects to the "Preparation of election" page)
        # - She changes values of fields name and description of the election
        # - She clicks on the "Save changes button" (the one that is next to the election description field)
        administrator_starts_creation_of_election(browser)

        # She edits election's questions:
        # - She clicks on the "Edit questions" link, to write her own questions
        # - She arrives on the Questions page. She checks that the page title is correct
        # - She removes answer 3
        # - She clicks on the "Save changes" button (this redirects to the "Preparation of election" page)
        administrator_edits_election_questions(browser, nh_question)

        # She sets election's voters:
        # - She clicks on the "Edit voters" link, to then type the list of voters
        # - She types N e-mail addresses (the list of invited voters)
        # - She clicks on the "Add" button to submit changes
        # - She clicks on "Go back to election draft" link
        self.voters_email_addresses = random_email_addresses_generator(
            settings.NUMBER_OF_INVITED_VOTERS)
        administrator_sets_election_voters(browser,
                                           self.voters_email_addresses)

        # She clicks on button "Generate on server"
        generate_on_server_button_label = "Generate on server"
        generate_on_server_button_css_selector = build_css_selector_to_find_buttons_in_page_content_by_value(
            generate_on_server_button_label)
        generate_on_server_button_element = wait_for_element_exists(
            browser, generate_on_server_button_css_selector,
            settings.EXPLICIT_WAIT_TIMEOUT)
        generate_on_server_button_element.click()

        wait_a_bit()

        # (Server sends emails to voters.) She checks that server does not show any error that would happen when trying to send these emails (this can happen if sendmail is not configured)
        confirmation_sentence_expected_text = "Credentials have been generated and mailed!"
        confirmation_sentence_css_selector = "#main p"
        wait_for_element_exists_and_contains_expected_text(
            browser, confirmation_sentence_css_selector,
            confirmation_sentence_expected_text,
            settings.EXPLICIT_WAIT_TIMEOUT)

        # Now we do a sanity check that server has really tried to send emails. For this, we look for email addresses in the temporary file where our fake sendmail executable redirects its inputs to.
        """
        An email sent by Belenios (using sendmail or using the fake sendmail) to a voter looks like this:

Content-type: text/plain; charset="UTF-8"
Content-transfer-encoding: quoted-printable
From: Belenios public server <*****@*****.**>
To: "*****@*****.**"
 <*****@*****.**>
Subject: Your credential for election My test election for Scenario 1
MIME-Version: 1.0
X-Mailer: OcamlNet (ocamlnet.sourceforge.net)
Date: Wed, 31 Oct 2018 15:22:27 +0100

You are listed as a voter for the election

  My test election for Scenario 1

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: yQVDQaKSAQVjdZq
Page of the election: http://localhost:8001/elections/AFFNDEPnpy21bw/

Note that you are allowed to vote several times.  Only the last vote
counts.

----------

Vous =C3=AAtes enregistr=C3=A9(e) en tant qu=27=C3=A9lecteur(trice) pour=20=
l=27=C3=A9lection

  My test election for Scenario 1

Veuillez trouver ci-dessous votre code de vote. Pour soumettre un
bulletin, vous aurez =C3=A9galement besoin d=27un mot de passe, envoy=C3=A9=
 dans
un e-mail s=C3=A9par=C3=A9. Soyez attentif(ve), le mot de passe et le cod=
e de
vote se ressemblent mais jouent des r=C3=B4les diff=C3=A9rents. Le syst=C3=
=A8me vous
demandera votre code de vote d=C3=A8s l=27entr=C3=A9e dans l=27isoloir=20=
virtuel. Le
nom d=27utilisateur et le mot de passe sont n=C3=A9cessaires lorsque votr=
e
bulletin est pr=C3=AAt =C3=A0 =C3=AAtre soumis.

Code de vote=C2=A0: yQVDQaKSAQVjdZq
Page de l=27=C3=A9lection=C2=A0: http://localhost:8001/elections/AFFNDEPn=
py21bw/

Notez que vous pouvez voter plusieurs fois. Seul le dernier vote est
pris en compte.

--=20
        """

        email_address_to_look_for = self.voters_email_addresses[0]
        text_to_look_for = 'To: "' + email_address_to_look_for + '"'
        email_address_found = self.fake_sent_emails_manager.find_in_sent_emails(
            text_to_look_for)
        assert email_address_found, "Text '" + email_address_to_look_for + "' not found in fake sendmail log file " + self.fake_sent_emails_manager.log_file_path

        # She clicks on the "Proceed" link
        proceed_link_css_selector = "#generic_proceed_link"
        proceed_link_element = wait_for_element_exists(
            browser, proceed_link_css_selector, settings.EXPLICIT_WAIT_TIMEOUT)
        proceed_link_element.click()

        wait_a_bit()

        # In "Authentication" section, she clicks on the "Generate and mail missing passwords" button
        generate_and_mail_missing_passwords_button_label = "Generate and mail missing passwords"
        generate_and_mail_missing_passwords_button_element = wait_for_element_exists(
            browser,
            build_css_selector_to_find_buttons_in_page_content_by_value(
                generate_and_mail_missing_passwords_button_label),
            settings.EXPLICIT_WAIT_TIMEOUT)
        generate_and_mail_missing_passwords_button_element.click()

        wait_a_bit()

        # She checks that the page contains expected confirmation text, instead of an error (TODO: explain in which case an error can happen, and check that it does not show)
        confirmation_sentence_expected_text = "Passwords have been generated and mailed!"
        confirmation_sentence_css_selector = "#main p"
        wait_for_element_exists_and_contains_expected_text(
            browser, confirmation_sentence_css_selector,
            confirmation_sentence_expected_text,
            settings.EXPLICIT_WAIT_TIMEOUT)

        # She clicks on the "Proceed" link (this redirects to the "Preparation of election" page)
        proceed_link_css_selector = "#generic_proceed_link"
        proceed_link_element = wait_for_element_exists(
            browser, proceed_link_css_selector, settings.EXPLICIT_WAIT_TIMEOUT)
        proceed_link_element.click()

        wait_a_bit()

        self.election_page_url = administrator_validates_creation_of_election(
            browser)
        console_log("election_page_url:", self.election_page_url)
        self.election_id = election_page_url_to_election_id(
            self.election_page_url)
        console_log("election_id:", self.election_id)

        log_out(browser, self.election_id)
예제 #44
0
    def administrator_starts_tallying_of_election(self, with_threshold=None):
        browser = self.browser

        # Alice goes to the election page
        election_url = self.election_page_url  # Could also be obtained with self.voters_data[self.voters_email_addresses[0]]["election_page_url"]
        browser.get(election_url)

        wait_a_bit()

        # She clicks on "en" language
        english_language_link_expected_label = "en"
        english_language_link_element = wait_for_an_element_with_link_text_exists(
            browser, english_language_link_expected_label,
            settings.EXPLICIT_WAIT_TIMEOUT)
        english_language_link_element.click()

        wait_a_bit()

        # She clicks on the "Administer this election" link
        administration_link_label = "Administer this election"
        administration_link_element = wait_for_an_element_with_partial_link_text_exists(
            browser, administration_link_label, settings.EXPLICIT_WAIT_TIMEOUT)
        administration_link_element.click()

        # She logs in as administrator
        log_in_as_administrator(browser, from_a_login_page=True)

        wait_a_bit()

        # She clicks on the "Close election" button
        close_election_button_label = "Close election"
        close_election_button_css_selector = build_css_selector_to_find_buttons_in_page_content_by_value(
            close_election_button_label)
        close_election_button_element = wait_for_element_exists(
            browser, close_election_button_css_selector,
            settings.EXPLICIT_WAIT_TIMEOUT)
        close_election_button_element.click()

        wait_a_bit()

        # She clicks on the "Proceed to vote counting" button
        proceed_button_label = "Proceed to vote counting"
        proceed_button_css_selector = build_css_selector_to_find_buttons_in_page_content_by_value(
            proceed_button_label)
        proceed_button_element = wait_for_element_exists(
            browser, proceed_button_css_selector,
            settings.EXPLICIT_WAIT_TIMEOUT)
        proceed_button_element.click()

        wait_a_bit()

        if with_threshold is not None:
            # She checks the presence of text "We are now waiting for trustees... At least ${U} trustee(s) must act."
            expected_confirmation_label = "We are now waiting for trustees... At least " + str(
                with_threshold) + " trustee(s) must act."
            expected_confirmation_css_selector = "#main"
            wait_for_element_exists_and_contains_expected_text(
                browser, expected_confirmation_css_selector,
                expected_confirmation_label)

            # She checks that in the table on every content row, the "DONE?" column is "No"
            elements_css_selector = "#main table tr td:nth-of-type(4)"
            attribute_name = "innerText"
            attribute_value = "No"
            verify_all_elements_have_attribute_value(
                browser,
                elements_css_selector,
                attribute_name,
                attribute_value,
                extractor=(lambda x: x[1:]))

        # She remembers the link to send to each trustee, so they can tally the election
        row_padding = 3
        self.closed_election_tally_links_for_trustees = []
        for idx, email_address in enumerate(settings.TRUSTEES_EMAIL_ADDRESSES):
            trustee_link_css_selector = "#main table tr:nth-of-type(" + str(
                idx + row_padding
            ) + ") td:nth-of-type(3) a"  # First row consists in column titles. Second row is for server.
            trustee_link_element = wait_for_element_exists_and_has_non_empty_content(
                browser, trustee_link_css_selector)
            self.closed_election_tally_links_for_trustees.append(
                trustee_link_element.get_attribute('href'))

        # She sends to each trustee an email containing their own link
        subject = "Link to tally the election"
        content_format = """\
Dear trustee,

The election is now closed. Here's the link to proceed to tally:

{link_for_trustee}

Here's the instructions:
1. Follow the link.
2. Enter your private decryption key in the first box and click on
"generate decryption factors"
3. The second box is now filled with crypto material. Please press the
button "submit".

Thank you again for your help,

-- 
The election administrator.\
"""
        for idx, trustee_email_address in enumerate(
                settings.TRUSTEES_EMAIL_ADDRESSES):
            custom_content = content_format.format(
                link_for_trustee=self.
                closed_election_tally_links_for_trustees[idx])
            self.fake_sent_emails_manager.send_email(
                settings.ADMINISTRATOR_EMAIL_ADDRESS, trustee_email_address,
                subject, custom_content)

        # She logs out
        log_out(browser)

        # She closes the window
        browser.quit()
예제 #45
0
    def administrator_starts_tallying_of_election(self):
        browser = self.browser

        # Alice goes to the election page
        election_url = self.election_page_url # Could also be obtained with self.voters_data[self.voters_email_addresses[0]]["election_page_url"]
        browser.get(election_url)

        wait_a_bit()

        # She clicks on the "Administer this election" link
        administration_link_label = "Administer this election"
        administration_link_element = wait_for_an_element_with_partial_link_text_exists(browser, administration_link_label, settings.EXPLICIT_WAIT_TIMEOUT)
        administration_link_element.click()

        # She logs in as administrator
        log_in_as_administrator(browser, from_a_login_page=True)

        wait_a_bit()

        # She clicks on the "Close election" button
        close_election_button_label = "Close election"
        close_election_button_css_selector = build_css_selector_to_find_buttons_in_page_content_by_value(close_election_button_label)
        close_election_button_element = wait_for_element_exists(browser, close_election_button_css_selector, settings.EXPLICIT_WAIT_TIMEOUT)
        close_election_button_element.click()

        wait_a_bit()

        # She clicks on the "Proceed to vote counting" button
        proceed_button_label = "Proceed to vote counting"
        proceed_button_css_selector = build_css_selector_to_find_buttons_in_page_content_by_value(proceed_button_label)
        proceed_button_element = wait_for_element_exists(browser, proceed_button_css_selector, settings.EXPLICIT_WAIT_TIMEOUT)
        proceed_button_element.click()

        wait_a_bit()

        # She remembers the encrypted tally hash
        encrypted_tally_hash_css_selector = "#encrypted_tally_hash"
        encrypted_tally_hash_element = wait_for_element_exists_and_has_non_empty_content(browser, encrypted_tally_hash_css_selector)
        self.encrypted_tally_hash = encrypted_tally_hash_element.get_attribute('innerText')

        # She remembers the link to send to each trustee, so they can tally the election
        self.closed_election_tally_links_for_trustees = []
        for idx, email_address in enumerate(settings.TRUSTEES_EMAIL_ADDRESSES):
            trustee_link_css_selector = "#main table tr:nth-child(" + str(idx + 3) + ") td:nth-child(3) a"
            trustee_link_element = wait_for_element_exists_and_has_non_empty_content(browser, trustee_link_css_selector)
            self.closed_election_tally_links_for_trustees.append(trustee_link_element.get_attribute('href'))

        # She sends to each trustee an email containing their own link
        subject = "Link to tally the election"
        content_format = """\
Dear trustee,

The election is now closed. Here's the link to proceed to tally:

{link_for_trustee}

Here's the instructions:
1. Follow the link.
2. Enter your private decryption key in the first box and click on
"generate decryption factors"
3. The second box is now filled with crypto material. Please press the
button "submit".

Thank you again for your help,

-- 
The election administrator.\
"""
        for idx, trustee_email_address in enumerate(settings.TRUSTEES_EMAIL_ADDRESSES):
            custom_content = content_format.format(link_for_trustee=self.closed_election_tally_links_for_trustees[idx])
            self.fake_sent_emails_manager.send_email(settings.ADMINISTRATOR_EMAIL_ADDRESS, trustee_email_address, subject, custom_content)

        # She logs out
        log_out(browser)

        # She closes the window
        browser.quit()
예제 #46
0
    def administrator_starts_creation_of_manual_election(self):
        # # Setting up a new election (action of the administrator)

        browser = self.browser

        # Alice has been given administrator rights on an online voting app called Belenios. She goes
        # to check out its homepage and logs in
        log_in_as_administrator(browser)

        # She starts creation of the election:
        # - She clicks on the "Prepare a new election" link
        # - She picks the Credential management method: manual
        # (- She keeps default value for Authentication method: it is Password, not CAS)
        # - She clicks on the "Proceed" button (this redirects to the "Preparation of election" page)
        # - In the "Name and description of the election" section, she changes values of fields name and description of the election
        # - She clicks on the "Save changes button" (the one that is next to the election description field)
        # - In "Contact" section, she changes the value of "contact" field
        # - She clicks on the "Save changes" button (the one that is in the "Contact" section)
        administrator_starts_creation_of_election(browser, True)

        # She remembers the URL of the draft election administration page
        self.draft_election_administration_page_url = browser.current_url

        # She edits election's questions:
        # - She clicks on the "Edit questions" link, to write her own questions
        # - She arrives on the Questions page. She checks that the page title is correct
        # - She removes answer 3
        # - She clicks on the "Save changes" button (this redirects to the "Preparation of election" page)
        administrator_edits_election_questions(browser)

        # She sets election's voters:
        # - She clicks on the "Edit voters" link, to then type the list of voters
        # - She types N e-mail addresses (the list of invited voters)
        # - She clicks on the "Add" button to submit changes
        # - She clicks on "Return to draft page" link
        self.voters_email_addresses = random_email_addresses_generator(
            settings.NUMBER_OF_INVITED_VOTERS)
        administrator_sets_election_voters(browser,
                                           self.voters_email_addresses)

        # In "Authentication" section, she clicks on the "Generate and mail missing passwords" button
        generate_and_mail_missing_passwords_button_label = "Generate and mail missing passwords"
        generate_and_mail_missing_passwords_button_css_selector = "#main input[type=submit][value='" + generate_and_mail_missing_passwords_button_label + "']"
        generate_and_mail_missing_passwords_button_element = wait_for_element_exists(
            browser, generate_and_mail_missing_passwords_button_css_selector,
            settings.EXPLICIT_WAIT_TIMEOUT)
        generate_and_mail_missing_passwords_button_element.click(
        )  # FIXME: This click does not get triggered when we have maximized the browser window

        wait_a_bit()

        # She checks that the page contains expected confirmation text, instead of an error (TODO: explain in which case an error can happen, and check that it does not show)
        confirmation_sentence_expected_text = "Passwords have been generated and mailed!"
        confirmation_sentence_css_selector = "#main p"
        wait_for_element_exists_and_contains_expected_text(
            browser, confirmation_sentence_css_selector,
            confirmation_sentence_expected_text,
            settings.EXPLICIT_WAIT_TIMEOUT)

        # She clicks on the "Proceed" link (this redirects to the "Preparation of election" page)
        proceed_link_expected_label = "Proceed"
        proceed_link_css_selector = "#main a"
        proceed_link_element = wait_for_element_exists_and_contains_expected_text(
            browser, proceed_link_css_selector, proceed_link_expected_label,
            settings.EXPLICIT_WAIT_TIMEOUT)
        proceed_link_element.click()

        wait_a_bit()

        # In "Credentials" section, she clicks on "Credential management" link
        credential_management_expected_label = "Credential management"
        credential_management_link_element = wait_for_an_element_with_partial_link_text_exists(
            browser, credential_management_expected_label)
        credential_management_link_element.click()

        wait_a_bit()

        # She fills in her public name, then clicks on "Set"
        credential_authority_css_selector = "#main form input[name=__co_eliom_name]"
        credential_authority_element = wait_for_element_exists(
            browser, credential_authority_css_selector,
            settings.EXPLICIT_WAIT_TIMEOUT)
        credential_authority_element.clear()
        credential_authority_element.send_keys("Cecily")
        credential_authority_set_css_selector = "#main form input[type=submit]"
        credential_authority_set_element = browser.find_element_by_css_selector(
            credential_authority_set_css_selector)
        credential_authority_set_element.click()

        wait_a_bit()

        # She clicks on the "Proceed" link
        proceed_link_expected_label = "Proceed"
        proceed_link_css_selector = "#main a"
        proceed_link_element = wait_for_element_exists_and_contains_expected_text(
            browser, proceed_link_css_selector, proceed_link_expected_label,
            settings.EXPLICIT_WAIT_TIMEOUT)
        proceed_link_element.click()

        wait_a_bit()

        # She remembers the link displayed
        link_for_credential_authority_css_selector = "#credential_authority_link"
        link_for_credential_authority_element = wait_for_element_exists_and_has_non_empty_content(
            browser, link_for_credential_authority_css_selector)
        link_label = link_for_credential_authority_element.get_attribute(
            'innerText').strip()
        self.credential_authority_link = link_label

        # She sends the remembered link to the credential authority by email (actually we don't need to send anything because we will act as the credential authority)

        # Optionnaly, she logs out
        # log_out(browser)

        # She closes the browser window
        browser.quit()
예제 #47
0
    def trustees_do_partial_decryption(self):
        # Each trustee (Tom and Taylor) will do the following process:
        for idx, trustee_email_address in enumerate(settings.TRUSTEES_EMAIL_ADDRESSES):
            # He opens the link that Alice (the election administrator) has sent to him
            self.browser = initialize_browser_for_scenario_2()
            browser = self.browser
            link_for_trustee = self.closed_election_tally_links_for_trustees[idx]
            browser.get(link_for_trustee)

            wait_a_bit()

            # We verify that the encrypted election hash is the same as the one that has been displayed to election administrator
            encrypted_tally_hash_css_selector = "#hash"
            encrypted_tally_hash_element = wait_for_element_exists_and_has_non_empty_content(browser, encrypted_tally_hash_css_selector)
            encrypted_tally_hash = encrypted_tally_hash_element.get_attribute('innerText')
            assert encrypted_tally_hash == self.encrypted_tally_hash, "Error: Encrypted tally hash displayed to trustee (" + encrypted_tally_hash + ") is not the same as the one displayed to election administrator (" + self.encrypted_tally_hash + ")."

            # He verifies that the "private key" input field is empty (at the beginning)
            private_key_field_css_selector = "#private_key"
            private_key_field_element = wait_for_element_exists(browser, private_key_field_css_selector)
            assert private_key_field_element.get_attribute('value') == ""

            # One trustee uploads his private key file, the other copy-pastes its contents into the form field
            private_key_file = self.downloaded_files_paths_per_trustee[trustee_email_address]["private key"]
            if idx % 2 == 0:
                # He clicks on the "Browse..." button and selects his private key file (initially downloaded as `private_key.json` by default)
                browse_button_css_selector = "input[id=private_key_file][type=file]"
                browse_button_element = wait_for_element_exists(browser, browse_button_css_selector)
                path_of_file_to_upload = private_key_file
                browse_button_element.clear()
                browse_button_element.send_keys(path_of_file_to_upload)

                # He waits until the "private key" input field (that has id "#private_key") becomes not empty anymore. This is because once the user has selected the file to upload, the Javascript code in the page detects that a file has been selected, reads it, and fills "private key" input field with file's contents. The computation triggered by click on the "Compute decryption factors" button will use the value of this field, not directly the uploaded file contents.
                private_key_field_expected_non_empty_attribute = "value"
                wait_for_element_exists_and_has_non_empty_attribute(browser, private_key_field_css_selector, private_key_field_expected_non_empty_attribute)
            else:
                with open(private_key_file) as myfile:
                    private_key_field_element.send_keys(myfile.read())
                wait_a_bit()

            # He clicks on the "Compute decryption factors" button
            compute_button_css_selector = "button[id=compute]"
            compute_button_element = wait_for_element_exists(browser, compute_button_css_selector)
            compute_button_element.click()

            # He checks that the text field below (used as visual feedback) now contains text
            visual_feedback_css_selector = "#pd"
            visual_feedback_expected_non_empty_attribute = "value"
            try:
                wait_for_element_exists_and_has_non_empty_attribute(browser, visual_feedback_css_selector, visual_feedback_expected_non_empty_attribute, 60 * 2)
            except UnexpectedAlertPresentException as e:
                raise Exception("An alert was displayed at a moment when no alert should be displayed. Alert displayed probably contains error information about uploaded file contents.") from e

            # He clicks on the "Submit" button
            submit_button_css_selector = "#pd_done input[type=submit]"
            submit_button_element = wait_for_element_exists(browser, submit_button_css_selector)
            submit_button_element.click()

            wait_a_bit()

            # He checks that next screen contains a confirmation sentence
            confirmation_sentence_expected_text = "Your partial decryption has been received and checked!"
            confirmation_sentence_css_selector = "#main p"
            wait_for_element_exists_and_contains_expected_text(browser, confirmation_sentence_css_selector, confirmation_sentence_expected_text, settings.EXPLICIT_WAIT_TIMEOUT)

            # He closes the window
            browser.quit()