Error 401
` 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 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 if __name__ == "__main__": random_seed = os.getenv('RANDOM_SEED', None) if not random_seed: random_seed = random.randrange(sys.maxsize) console_log("Python random seed being used:", random_seed) random.seed(random_seed) settings.SERVER_URL = os.getenv('SERVER_URL', settings.SERVER_URL) if os.getenv('START_SERVER', None): settings.START_SERVER = bool(strtobool(os.getenv('START_SERVER'))) if os.getenv('USE_HEADLESS_BROWSER', None): settings.USE_HEADLESS_BROWSER = bool(strtobool(os.getenv('USE_HEADLESS_BROWSER'))) settings.ELECTION_ID = os.getenv('ELECTION_ID', None) or None settings.VOTER_USERNAME = os.getenv('VOTER_USERNAME', None) or None settings.VOTER_PASSWORD = os.getenv('VOTER_PASSWORD', None) or None settings.VOTER_CREDENTIAL = os.getenv('VOTER_CREDENTIAL', None) or None settings.FAKE_SENT_EMAILS_FILE_RELATIVE_URL = os.getenv('FAKE_SENT_EMAILS_FILE_RELATIVE_URL', "static/mail.txt") settings.SENT_EMAILS_TEXT_FILE_ABSOLUTE_PATH = os.getenv('SENT_EMAILS_TEXT_FILE_ABSOLUTE_PATH', settings.SENT_EMAILS_TEXT_FILE_ABSOLUTE_PATH) settings.WAIT_TIME_BETWEEN_EACH_STEP = float(os.getenv('WAIT_TIME_BETWEEN_EACH_STEP', settings.WAIT_TIME_BETWEEN_EACH_STEP)) # Do not set a value below 0.02 seconds, otherwise hypothesis test becomes flaky. settings.EXPLICIT_WAIT_TIMEOUT = int(os.getenv('EXPLICIT_WAIT_TIMEOUT', settings.EXPLICIT_WAIT_TIMEOUT)) if os.getenv('CLEAN_UP_POLICY', None): input_clean_up_policy = os.getenv('CLEAN_UP_POLICY') if hasattr(settings.CLEAN_UP_POLICIES, input_clean_up_policy): settings.CLEAN_UP_POLICY = getattr(settings.CLEAN_UP_POLICIES, input_clean_up_policy) else: raise Exception("Error: Unknown value for CLEAN_UP_POLICY:", input_clean_up_policy) settings.NUMBER_OF_INVITED_VOTERS = int(os.getenv('NUMBER_OF_INVITED_VOTERS', settings.NUMBER_OF_INVITED_VOTERS)) settings.NUMBER_OF_VOTING_VOTERS = int(os.getenv('NUMBER_OF_VOTING_VOTERS', settings.NUMBER_OF_VOTING_VOTERS)) settings.NUMBER_OF_REVOTING_VOTERS = int(os.getenv('NUMBER_OF_REVOTING_VOTERS', settings.NUMBER_OF_REVOTING_VOTERS)) settings.NUMBER_OF_REGENERATED_PASSWORD_VOTERS = int(os.getenv('NUMBER_OF_REGENERATED_PASSWORD_VOTERS', settings.NUMBER_OF_REGENERATED_PASSWORD_VOTERS)) settings.LOGIN_MODE = os.getenv('LOGIN_MODE', settings.LOGIN_MODE) settings.ADMINISTRATOR_USERNAME = os.getenv('ADMINISTRATOR_USERNAME', settings.ADMINISTRATOR_USERNAME) settings.ADMINISTRATOR_PASSWORD = os.getenv('ADMINISTRATOR_PASSWORD', settings.ADMINISTRATOR_PASSWORD) settings.ELECTION_TITLE = os.getenv('ELECTION_TITLE', settings.ELECTION_TITLE) settings.ELECTION_DESCRIPTION = os.getenv('ELECTION_DESCRIPTION', settings.ELECTION_DESCRIPTION) console_log("SERVER_URL:", settings.SERVER_URL) console_log("START_SERVER:", settings.START_SERVER) console_log("USE_HEADLESS_BROWSER:", settings.USE_HEADLESS_BROWSER) console_log("FAKE_SENT_EMAILS_FILE_RELATIVE_URL:", settings.FAKE_SENT_EMAILS_FILE_RELATIVE_URL) console_log("SENT_EMAILS_TEXT_FILE_ABSOLUTE_PATH:", settings.SENT_EMAILS_TEXT_FILE_ABSOLUTE_PATH) console_log("WAIT_TIME_BETWEEN_EACH_STEP:", settings.WAIT_TIME_BETWEEN_EACH_STEP) console_log("EXPLICIT_WAIT_TIMEOUT:", settings.EXPLICIT_WAIT_TIMEOUT) console_log("NUMBER_OF_INVITED_VOTERS:", settings.NUMBER_OF_INVITED_VOTERS) console_log("NUMBER_OF_VOTING_VOTERS:", settings.NUMBER_OF_VOTING_VOTERS) console_log("NUMBER_OF_REVOTING_VOTERS:", settings.NUMBER_OF_REVOTING_VOTERS) console_log("NUMBER_OF_REGENERATED_PASSWORD_VOTERS:", settings.NUMBER_OF_REGENERATED_PASSWORD_VOTERS) console_log("LOGIN_MODE:", settings.LOGIN_MODE) console_log("ELECTION_TITLE:", settings.ELECTION_TITLE) console_log("ELECTION_DESCRIPTION:", settings.ELECTION_DESCRIPTION) unittest.main(failfast=True) belenios-2.2-10-gbb6b7ea8/tests/selenium/test_fuzz_login.py 0000644 0001750 0001750 00000010035 14476041226 022657 0 ustar steph steph #!/usr/bin/python # coding: utf-8 import unittest import random import os import sys from hypothesis import given import hypothesis.strategies as st from hypothesis import settings as hypothesis_settings from util.election_testing import strtobool, console_log, wait_a_bit, initialize_server from util.page_objects import ServerHomePage, VoterLoginPage, LoginFailedPage, AdministrationHomeLoggedInPage from test_scenario_2 import BeleniosTestElectionScenario2Base, initialize_browser_for_scenario_2 import settings def go_to_log_in_page(browser): # Alice has been given administrator rights on an online voting app called Belenios. She goes # to check out its homepage timeout = settings.EXPLICIT_WAIT_TIMEOUT browser.get(settings.SERVER_URL) wait_a_bit() # She verifies she is on the server home page server_home_page = ServerHomePage(browser, timeout) server_home_page.verify_page() # If a personal data policy modal appears (it does not appear after it has been accepted), she clicks on the "Accept" button server_home_page.click_on_accept_button_in_personal_data_policy_modal_if_available() # She clicks on "local" to go to the login page server_home_page.click_on_login_link(settings.LOGIN_MODE) wait_a_bit() class BeleniosMonkeyTestFuzzLogin(BeleniosTestElectionScenario2Base): def __init__(self, *args, **kw): super().__init__(*args, **kw) def setUp(self): if settings.START_SERVER: self.server = initialize_server() self.browser = initialize_browser_for_scenario_2() def tearDown(self): self.browser.quit() if settings.START_SERVER: self.server.kill() @given(st.text(), st.text()) @hypothesis_settings(deadline=None) def test_log_in(self, username, password): browser = self.browser timeout = settings.EXPLICIT_WAIT_TIMEOUT go_to_log_in_page(browser) login_page = VoterLoginPage(browser, timeout) login_page.verify_page() login_page.log_in(username, password) try: unauthorized_page = LoginFailedPage(browser, timeout) unauthorized_page.verify_page() except Exception: administration_page = AdministrationHomeLoggedInPage(browser, timeout) administration_page.verify_page() console_log(f"### Warning: Submitting random input (\"{username}\", \"{password}\") to log in form directs to administration logged in page.") # Or should we rather re-raise an exception because it is very unlikely? if __name__ == "__main__": if not hasattr(settings, "LOGIN_MODE"): settings.LOGIN_MODE = "local" if not hasattr(settings, "START_SERVER"): settings.START_SERVER = True random_seed = os.getenv('RANDOM_SEED', None) if not random_seed: random_seed = random.randrange(sys.maxsize) console_log("Python random seed being used:", random_seed) random.seed(random_seed) settings.SERVER_URL = os.getenv('SERVER_URL', settings.SERVER_URL) if os.getenv('START_SERVER', None): settings.START_SERVER = bool(strtobool(os.getenv('START_SERVER'))) if os.getenv('USE_HEADLESS_BROWSER', None): settings.USE_HEADLESS_BROWSER = bool(strtobool(os.getenv('USE_HEADLESS_BROWSER'))) settings.WAIT_TIME_BETWEEN_EACH_STEP = float(os.getenv('WAIT_TIME_BETWEEN_EACH_STEP', settings.WAIT_TIME_BETWEEN_EACH_STEP)) # Do not set a value below 0.02 seconds, otherwise hypothesis test becomes flaky. settings.EXPLICIT_WAIT_TIMEOUT = int(os.getenv('EXPLICIT_WAIT_TIMEOUT', settings.EXPLICIT_WAIT_TIMEOUT)) settings.LOGIN_MODE = os.getenv('LOGIN_MODE', settings.LOGIN_MODE) console_log("SERVER_URL:", settings.SERVER_URL) console_log("START_SERVER:", settings.START_SERVER) console_log("USE_HEADLESS_BROWSER:", settings.USE_HEADLESS_BROWSER) console_log("WAIT_TIME_BETWEEN_EACH_STEP:", settings.WAIT_TIME_BETWEEN_EACH_STEP) console_log("EXPLICIT_WAIT_TIMEOUT:", settings.EXPLICIT_WAIT_TIMEOUT) console_log("LOGIN_MODE:", settings.LOGIN_MODE) unittest.main() belenios-2.2-10-gbb6b7ea8/tests/selenium/vote_with_prepared_ballots_direct.py 0000644 0001750 0001750 00000005673 14476041226 026412 0 ustar steph steph #!/usr/bin/python # coding: utf-8 import unittest import os import csv import json import re import urllib.request import base64 from util.election_testing import strtobool, console_log from util.execution import ConsoleLogDuration, try_several_times import settings class BeleniosVoteWithPreparedBallots(unittest.TestCase): def __init__(self, *args, **kw): super().__init__(*args, **kw) def cast_all_votes_from_csv(self): generated_files_destination_folder = settings.GENERATED_FILES_DESTINATION_FOLDER csv_file_path = os.path.join(generated_files_destination_folder, 'all_votes.csv') pat = re.compile(r'^(.+)/elections/([^/]+)/$') with open(csv_file_path, 'r', newline='') as csvfile: csvreader = csv.DictReader(csvfile, delimiter=',', quotechar='|') current_row = 0 for row in csvreader: current_row += 1 if current_row <= settings.SKIP_ROWS_IN_CSV_FILE: continue voter_email_address = row['voter_email_address'] auth_dict = { "username": voter_email_address, "password": row['voter_password'] } auth_json = json.dumps(auth_dict) auth_token = base64.b64encode(auth_json.encode()) headers = { "Content-Type": b"application/json", "Authorization": b"Bearer " + auth_token } voter_encrypted_ballot_file_name = row['voter_encrypted_ballot_file_name'] with open(os.path.join(generated_files_destination_folder, voter_encrypted_ballot_file_name), "r") as f: data = f.read().strip().encode() election_page_url = row['election_page_url'] mat = pat.match(election_page_url) prefix = mat.group(1) uuid = mat.group(2) with ConsoleLogDuration(f"Row {current_row} (voter {voter_email_address})"): req = urllib.request.urlopen(urllib.request.Request(prefix + "/api/elections/" + uuid + "/ballots", data=data, headers=headers)) if req.status != 200: console_log("failure") def test_vote_with_prepared_ballots(self): # Generate ballot for each voter console_log("### Starting step: cast_all_votes_from_csv") self.cast_all_votes_from_csv() console_log("### Step complete: cast_all_votes_from_csv") if __name__ == "__main__": settings.GENERATED_FILES_DESTINATION_FOLDER = os.getenv('GENERATED_FILES_DESTINATION_FOLDER', settings.GENERATED_FILES_DESTINATION_FOLDER) settings.SKIP_ROWS_IN_CSV_FILE = int(os.getenv('SKIP_ROWS_IN_CSV_FILE', 0)) console_log("GENERATED_FILES_DESTINATION_FOLDER:", settings.GENERATED_FILES_DESTINATION_FOLDER) console_log("SKIP_ROWS_IN_CSV_FILE:", settings.SKIP_ROWS_IN_CSV_FILE) unittest.main() belenios-2.2-10-gbb6b7ea8/tests/selenium/test_clicker_monkey.py 0000644 0001750 0001750 00000030202 14476041226 023465 0 ustar steph steph #!/usr/bin/python # coding: utf-8 import unittest import random import os import sys from urllib.parse import urljoin, urlsplit from util.election_testing import strtobool, wait_a_bit from util.page_objects import ElectionHomePage, ResponsiveBoothStep1Page, ResponsiveBoothStep2Page, ResponsiveBoothStep3Page, VoterLoginPage, NormalVoteStep6Page, BallotBoxPage from util.monkeys import SeleniumClickerMonkey, SeleniumFormFillerMonkey from util.execution import console_log from test_fuzz_vote import BeleniosTestElectionWithCreationBase import settings def verify_page_is_not_an_error_page(browser): # Belenios web server returns a "Unauthorized" "Error 401" page in several situations, for example when we pick the "local" login method and submit an empty login form. For now, we consider this behaviour as normal. # But what we consider an unexpected error is other types of errors returned by the server, for example "Internal Server Error", "Error 500". error_content = ["Internal Server Error", "Error 500", "Not Found", "Error 404"] page_source = browser.page_source if not page_source or not len(page_source): raise Exception(f"Server returned an unexpected blank page. Page source was: {page_source}") for content in error_content: if content in page_source: page_source = str(browser.page_source.encode("utf-8")) raise Exception(f"Server returned an unexpected error page. Page source was: {page_source}") def belenios_fence_filter(initial_page_url, href_value): """ A kind of geofencing: We filter out URLs which are out of the scope of the test """ target_url = urljoin(initial_page_url, href_value) # If this link points to a different host (domain name), we abort if urlsplit(target_url).hostname != urlsplit(initial_page_url).hostname: return False # We abort if this link: # - points to a downloadable element which works correctly for sure or which we don't want to test (for example because it would be tested too often or would take too much resources to download) # - is the election creation page (if monkey accesses the administration panel by logging in using the "demo" mode) # - is the election edition page (if monkey accesses the administration panel by logging in using the "demo" mode) forbidden_urls = ["belenios.tar.gz", ".bel", "/draft/new", "/draft/election?uuid="] for url in forbidden_urls: if url in target_url: return False return True def get_election_url(election_id): return "/".join([settings.SERVER_URL, "elections", election_id, ""]) class BeleniosMonkeyTestClicker(BeleniosTestElectionWithCreationBase): def test_clicker_monkey_on_election_home(self): console_log("# test_clicker_monkey_on_election_home()") browser = self.browser election_url = get_election_url(self.election_id) console_log("## Going to election page:", election_url) monkey = SeleniumClickerMonkey(browser, election_url, 0.25, belenios_fence_filter, verify_page_is_not_an_error_page) monkey.start(200) def test_sometimes_smart_monkey_votes(self): console_log("# test_sometimes_smart_monkey_votes()") browser = self.browser timeout = settings.EXPLICIT_WAIT_TIMEOUT election_url = get_election_url(self.election_id) console_log("## Going to election page:", election_url) browser.get(election_url) wait_a_bit() console_log("## Starting clicker monkey behaviour") monkey = SeleniumClickerMonkey(browser, election_url, 0.25, belenios_fence_filter, verify_page_is_not_an_error_page) maximum_monkey_clicks = 50 monkey.start(maximum_monkey_clicks) console_log("## End of clicker monkey behaviour") console_log("## Going to election page again", election_url) browser.get(election_url) wait_a_bit() console_log("## Clicking on 'en' language link") election_home_page = ElectionHomePage(browser, timeout) election_home_page.click_on_language_link("en") wait_a_bit() console_log("## Clicking on 'Start' button") election_home_page.click_on_start_button() wait_a_bit() console_log("## Verifying that we are on step 1 page") step_1_page = ResponsiveBoothStep1Page(browser, timeout) step_1_page.verify_page() step_1_page.type_voter_credential(settings.VOTER_CREDENTIAL) step_1_page.click_next_button() wait_a_bit() step_2_page = ResponsiveBoothStep2Page(browser, timeout) step_2_page.verify_page() # Here: # We can check any checkbox for the question (check 0 to n checkboxes) # We can click on the "Next" button # We can go back. This would go back to the election home page (not to the "Step 1" page, which would probably have been the intuitive behaviour) console_log("## Answering vote question by checking randomly some checkboxes") step_2_parent_css_selector = '.question-with-votable-answers' form_filler_monkey = SeleniumFormFillerMonkey(browser, step_2_parent_css_selector) # Warning: In the DOM of the vote page, step 2, checkboxes are not in a `