def test_rename_auth_app(self): # User has already add an unconfirmed auth app old_device_name = "Nokia 3310" setup_2fa_totp_device(self.user, old_device_name) self.visit(AccountPages.two_factors_authentication_url) # refresh page # User rename existing emergency code new_device_name = "My precious yPhone xD" self.rename_auth_app(new_name=new_device_name) # User see the set name have been updated in the device list self.assertNotIn(old_device_name, self.get_elem_text(self.auth_app_divs)) self.assertIn(new_device_name, self.get_elem_text(self.auth_app_divs)) # User logout self.visit(LoginPage.logout_url) # Remove middleware mock, to restore check pages self.middleware_patcher.stop() # User login again, the 2fa check page appears. self.log_user() self.wait_for_elem_to_disappear(self.login_submit_input) # The new device name is available in the select self.assertEqual(new_device_name, self.get_elem_text(self.check_pages_device_input))
def test_add_auth_app_confirmed(self): # Make TOTP.time setter set a hard coded secret_time to always be able to confirm app with the same valid_token totp_time_setter.side_effect = mocked_totp_time_setter # User has already add an unconfirmed auth app device_name = "My precious yPhone xD" setup_2fa_totp_device(self.user, device_name, secret_key=TOTP_DEVICE_SECRET_KEY, confirmed=False) self.visit(AccountPages.two_factors_authentication_url) # refresh page # User resume its totp device setup self.get_elem(self.unconfirmed_badges).click() # User can see qr code to scan self.get_elem(self.qr_code_image) # User scan the QR code and input generated code self.enter_2fa_code(TOTP_DEVICE_VALID_TOKEN) # User is invited to add a emergency code as backup self.assertIn("emergency codes", self.get_elem_text(self.device_name_label).lower()) # User go back to 2fa index page and can see the auth app he just added self.get_elem(self.cancel_button).click() self.assertIn(device_name, self.get_elem_text(self.auth_app_divs)) self.assertNotIn( "not confirmed", self.get_elem_text(self.auth_app_divs).lower(), "The totp device should be confirmed", ) # User logout self.visit(LoginPage.logout_url) # Remove middleware mock, to restore check pages self.middleware_patcher.stop() # User login again, the 2fa check page appears. self.log_user() # Check page appears ask for totp self.assertIn( "authenticator app", self.get_elem_text(self.check_pages_device_label).lower(), ) # No alternative 2FA are shown with self.assertRaises(NoSuchElementException): self.get_elem(self.check_pages_alternatives_list) # User can complete login using a valid 2FA code self.enter_2fa_code(TOTP_DEVICE_VALID_TOKEN) self.assertIn("home", self.head_title)
def test_delete_auth_app_with_emergency_code(self): # User has already added an auth app and a emergency codes set setup_2fa_totp_device(self.user) setup_2fa_static_device(self.user) self.visit(AccountPages.two_factors_authentication_url) # refresh page # User delete existing auth app self.get_elems(self.delete_auth_app_buttons)[0].click() # A warning message appears to inform user 2FA will be completely disable as emergency code are only available # when another 2fa device type is set self.assertIn("last 2FA device", self.get_elem_text(self.delete_warning))
def test_user_setup_all_2fa(self): # Make TOTP.time setter set a hard coded secret_time to always be able to confirm app with the same valid_token totp_time_setter.side_effect = mocked_totp_time_setter # User add an auth app (can't be really added in this test) setup_2fa_totp_device(self.user, secret_key=TOTP_DEVICE_SECRET_KEY) self.visit(AccountPages.two_factors_authentication_url) # refresh page # User add emergency code too self.add_emergency_codes_set("My emergency codes") # Finally, user add a security key (can't be really added in this test) setup_2fa_fido2_device(self.user) self.visit(AccountPages.two_factors_authentication_url) # refresh page # The 3 2FA devices appears in the list self.assertEqual(1, len(self.get_elems(self.auth_app_divs))) self.assertEqual(1, len(self.get_elems(self.emergency_codes_divs))) self.assertEqual(1, len(self.get_elems(self.security_key_divs))) # User logout and login self.visit(LoginPage.logout_url) self.middleware_patcher.stop( ) # Remove middleware mock, to restore check pages self.log_user() # The 2FA check page appears with all device available self.assertEqual( 2, len(self.get_elems(self.check_pages_alternatives_list))) # Login using totp for i, item_text in enumerate( self.get_elems_text(self.check_pages_alternatives_list)): if "authentication app" in item_text: self.get_elems(self.check_pages_alternatives_list)[i].click() self.enter_2fa_code(TOTP_DEVICE_VALID_TOKEN) self.assertIn("home", self.head_title) # user delete its security key 2FA self.visit(self.two_factors_authentication_url) # User delete existing security key self.get_elems(self.delete_security_key_buttons)[0].click() # There is no special warning about it, as there is auth app 2fa left with self.assertRaises(NoSuchElementException): self.get_elem(self.delete_warning)
def test_add_auth_app_with_wrong_confirmation_code(self): # Make TOTP.time setter set a hard coded secret_time to always be able to confirm app with the same valid_token totp_time_setter.side_effect = mocked_totp_time_setter setup_2fa_static_device(self.user) # emergency code already set # User has already add an unconfirmed auth app and resume its totp device setup device_name = "My precious yPhone xD" setup_2fa_totp_device(self.user, device_name, secret_key=TOTP_DEVICE_SECRET_KEY, confirmed=False) self.visit(AccountPages.two_factors_authentication_url) # refresh page self.get_elem(self.unconfirmed_badges).click() # User enter invalid code self.enter_2fa_code(TOTP_DEVICE_INVALID_TOKEN) # An error message appears self.get_elem(self.error_message)
def test_add_auth_app_with_emergency_code_already_set(self): # Make TOTP.time setter set a hard coded secret_time to always be able to confirm app with the same valid_token totp_time_setter.side_effect = mocked_totp_time_setter setup_2fa_static_device(self.user) # emergency code already set # User has already add an unconfirmed auth app and resume its totp device setup device_name = "My precious yPhone xD" setup_2fa_totp_device(self.user, device_name, secret_key=TOTP_DEVICE_SECRET_KEY, confirmed=False) self.visit(AccountPages.two_factors_authentication_url) # refresh page self.get_elem(self.unconfirmed_badges).click() # User scan the QR code and input generated code self.enter_2fa_code(TOTP_DEVICE_VALID_TOKEN) # User has already a "emergency codes set" set, so he get redirected to 2FA devices list self.wait_for_elem_to_show(self.auth_app_divs)
def test_otp_list_returns_correct_html(self): # Make TOTP.time setter set a hard coded secret_time to always be able to confirm app with the same valid_token totp_time_setter.side_effect = mocked_totp_time_setter # Given no 2fa devices are setup response = self.client.get("/accounts/2fa/") self.assertContains(response, "Protect your Paper Matter account") self.assertTemplateUsed(response, "otp_ftl/device_list.html") # Given 2fa devices are setup setup_2fa_static_device(self.user, codes_list=tv.STATIC_DEVICE_CODES_LIST) setup_2fa_fido2_device(self.user) setup_2fa_totp_device(self.user) response = self.client.get("/accounts/2fa/") self.assertContains(response, "Emergency codes") self.assertContains(response, "Security keys (U2F/FIDO2)") self.assertContains(response, "Authenticator apps")
def setUp(self): self.org_1 = setup_org() self.org_2 = setup_org(name=tv.ORG_NAME_2, slug=tv.ORG_SLUG_2) self.user_1 = setup_user(org=self.org_1) self.user_2 = setup_user(org=self.org_1, email=tv.USER2_EMAIL, password=tv.USER2_PASS) self.user_1_codes_1 = ["AA1222", "AA1223"] self.user_1_static_device_1 = setup_2fa_static_device( self.user_1, "user1 valid token set 1", codes_list=self.user_1_codes_1) self.user_1_fido2_device_1 = setup_2fa_fido2_device( self.user_1, "user1 fido2 device 1") self.user_1_totp_device_1 = setup_2fa_totp_device( self.user_1, "user1 totp 1") self.user_1_totp_device_2 = setup_2fa_totp_device( self.user_1, "user1 totp 2") self.user_2_totp_device_2 = setup_2fa_totp_device( self.user_2, "user2 totp 1")
def test_delete_auth_app(self): # User has already added an auth app setup_2fa_totp_device(self.user) self.visit(AccountPages.two_factors_authentication_url) # refresh page # User delete existing auth app self.delete_auth_app() # User see the set have been remove from the device list with self.assertRaises(NoSuchElementException): self.get_elem(self.auth_app_divs) # User logout self.visit(LoginPage.logout_url) # Remove middleware mock, to restore check pages self.middleware_patcher.stop() # When user login again there is no 2FA check page as there is no 2fa devices set self.log_user() self.assertIn("home", self.head_title)
def test_otp_list_context(self): # Make TOTP.time setter set a hard coded secret_time to always be able to confirm app with the same valid_token totp_time_setter.side_effect = mocked_totp_time_setter # Given 2fa devices are setup static_device_1 = setup_2fa_static_device( self.user, "SD1", codes_list=tv.STATIC_DEVICE_CODES_LIST) static_device_2 = setup_2fa_static_device( self.user, "SD2", codes_list=tv.STATIC_DEVICE_CODES_LIST) totp_device_1 = setup_2fa_totp_device(self.user, "TD1") totp_device_2 = setup_2fa_totp_device(self.user, "TD2") fido2_device_1 = setup_2fa_fido2_device(self.user, "FD1") fido2_device_2 = setup_2fa_fido2_device(self.user, "FD2") response = self.client.get("/accounts/2fa/") self.assertCountEqual(response.context["static_devices"], [static_device_1, static_device_2]) self.assertCountEqual(response.context["totp_devices"], [totp_device_1, totp_device_2]) self.assertCountEqual(response.context["fido2_devices"], [fido2_device_1, fido2_device_2])
def setUp(self): # Setup org, user, a totp device and the user is logged self.org = setup_org() self.user = setup_user(self.org) self.totp_device = setup_2fa_totp_device(self.user, confirmed=False) setup_authenticated_session(self.client, self.org, self.user) # mock OTPMiddleware._verify_user() to skip check page self.middleware_patcher = patch.object(OTPMiddleware, "_verify_user", mocked_verify_user) self.middleware_patcher.start() self.addCleanup( patch.stopall ) # ensure mock is remove after each test, even if the test crash self.addCleanup(totp_time_setter.reset_mock, side_effect=True)
def test_otp_static_check_context(self): response = self.client.get("/accounts/2fa/static/check/") self.assertEqual(response.context["have_static"], True) self.assertEqual(response.context["have_fido2"], False) self.assertEqual(response.context["have_totp"], False) # given user setup static + fido2 devices setup_2fa_fido2_device(self.user) response = self.client.get("/accounts/2fa/static/check/") self.assertEqual(response.context["have_static"], True) self.assertEqual(response.context["have_fido2"], True) self.assertEqual(response.context["have_totp"], False) # given user setup static + fido2 + totp devices setup_2fa_totp_device(self.user) response = self.client.get("/accounts/2fa/static/check/") self.assertEqual(response.context["have_static"], True) self.assertEqual(response.context["have_fido2"], True) self.assertEqual(response.context["have_totp"], True)
def test_form_save_proper_model(self, random_hex_mock): random_hex_mock.return_value = tv.TOTP_DEVICE_SECRET_KEY expected_model = setup_2fa_totp_device( self.user_1, tv.TOTP_DEVICE_NAME, secret_key=tv.TOTP_DEVICE_SECRET_KEY) form = TOTPDeviceForm(data={"name": tv.TOTP_DEVICE_NAME}) self.assertTrue(form.is_valid()) saved_model = form.save(self.user_1) self.assertEqual(expected_model.name, saved_model.name) self.assertEqual(expected_model.key, saved_model.key)
def test_otp_fido2_check_context(self): response = self.client.get("/accounts/2fa/fido2/check/") self.assertEqual(response.context["have_fido2"], True) self.assertEqual(response.context["have_totp"], False) self.assertEqual(response.context["have_static"], False) # given user setup totp + fido2 devices setup_2fa_totp_device(self.user) response = self.client.get("/accounts/2fa/fido2/check/") self.assertEqual(response.context["have_fido2"], True) self.assertEqual(response.context["have_totp"], True) self.assertEqual(response.context["have_static"], False) # given user setup static + fido2 + totp devices setup_2fa_static_device(self.user, codes_list=tv.STATIC_DEVICE_CODES_LIST) response = self.client.get("/accounts/2fa/fido2/check/") self.assertEqual(response.context["have_fido2"], True) self.assertEqual(response.context["have_totp"], True) self.assertEqual(response.context["have_static"], True)
def test_otp_check_redirect_to_proper_view(self): # Given no 2fa devices are setup response = self.client.get("/app/") # Home page is displayed self.assertTemplateUsed(response, "core/home.html") # Given a static device is setup setup_2fa_static_device(self.user, codes_list=tv.STATIC_DEVICE_CODES_LIST) response = self.client.get("/app/", follow=True) # User is redirect to otp_static_check self.assertRedirects(response, reverse_lazy("otp_static_check")) # Given static + totp device are setup setup_2fa_totp_device(self.user) response = self.client.get("/app/", follow=True) # User is redirect to otp_static_check self.assertRedirects(response, reverse_lazy("otp_totp_check")) # Given static + totp + 2fa device are setup setup_2fa_fido2_device(self.user) response = self.client.get("/app/", follow=True) # User is redirect to otp_static_check self.assertRedirects(response, reverse_lazy("otp_fido2_check"))
def setUp(self, **kwargs): # first org, admin, user, first 2fa device are already created, user is already logged on 2FA account page super().setUp() self.org = setup_org() setup_admin(self.org) self.user = setup_user(self.org) self.visit(LoginPage.url) self.log_user() # setup the first device to access emergency codes feature self.required_totp_device = setup_2fa_totp_device(self.user) # mock OTPMiddleware._verify_user() to skip check page self.middleware_patcher = patch.object(OTPMiddleware, "_verify_user", mocked_verify_user) self.middleware_patcher.start() self.addCleanup( patch.stopall ) # ensure mock is remove after each test, even if the test crash self.visit(AccountPages.two_factors_authentication_url)
def test_otp_static_delete_context(self): response = self.client.get( f"/accounts/2fa/totp/{self.totp_device.id}/delete/") self.assertEqual(response.context["last_otp"], True) # given user add a totp device totp_device_2 = setup_2fa_totp_device(self.user) response = self.client.get( f"/accounts/2fa/totp/{self.totp_device.id}/delete/") self.assertEqual(response.context["last_otp"], False) # given delete second totp device and add a fido2 device totp_device_2.delete() setup_2fa_fido2_device(self.user) response = self.client.get( f"/accounts/2fa/totp/{self.totp_device.id}/delete/") self.assertEqual(response.context["last_otp"], False)
def test_session_timeout_reduced_during_2fa_check(self): totp_device = setup_2fa_totp_device(self.user, secret_key=TOTP_DEVICE_SECRET_KEY) totp_time_setter.side_effect = mocked_totp_time_setter self.client.get("/app/", follow=True) # During 2fa session duration must be less than 10 mins now = datetime.now(timezone.utc) cookie_expiration_time = self.client.cookies["sessionid"]["expires"] cookie_expiration_time = datetime.strptime( cookie_expiration_time, "%a, %d %b %Y %H:%M:%S GMT") cookie_expiration_time = django_timezone.make_aware( cookie_expiration_time, timezone.utc) delta = cookie_expiration_time - now self.assertLessEqual(delta, timedelta(minutes=10)) self.client.post( "/accounts/2fa/totp/check/", { "otp_device": totp_device.persistent_id, "otp_token": TOTP_DEVICE_VALID_TOKEN, }, follow=True, ) # After 2FA check session have to be restore to default value cookie_expiration_time = self.client.cookies["sessionid"]["expires"] cookie_expiration_time = datetime.strptime( cookie_expiration_time, "%a, %d %b %Y %H:%M:%S GMT") cookie_expiration_time = django_timezone.make_aware( cookie_expiration_time, timezone.utc) delta = cookie_expiration_time - now self.assertAlmostEqual(round(delta.total_seconds()), settings.SESSION_COOKIE_AGE, delta=5)
def setUp(self): self.org_1 = setup_org() self.user_1 = setup_user(org=self.org_1) self.user_1_totp_device_1 = setup_2fa_totp_device(self.user_1, "user1 totp 1", confirmed=False)
def setUp(self, **kwargs): # orgs, admin, users are already created super().setUp() self.admin_org = setup_org(name="admin-org", slug="admin-org") self.admin = setup_admin(self.admin_org) self.user1_org = setup_org(name=tv.ORG_NAME_1, slug=tv.ORG_SLUG_1) self.user1 = setup_user(self.user1_org, email=tv.USER1_EMAIL, password=tv.USER1_PASS) self.user2_org = setup_org(name=tv.ORG_NAME_2, slug=tv.ORG_SLUG_2) self.user2 = setup_user(self.user2_org, email=tv.USER2_EMAIL, password=tv.USER2_PASS) # mock OTPMiddleware._verify_user() to skip check page self.middleware_patcher = patch.object(OTPMiddleware, "_verify_user", mocked_verify_user) self.middleware_patcher.start() self.addCleanup( patch.stopall ) # ensure mock is remove after each test, even if the test crash self.addCleanup(totp_time_setter.reset_mock, side_effect=True) # admin, user1 and user2 have added documents, folders, otp devices self.admin_resources = {} self.admin_resources["folder1"] = setup_folder(self.admin_org) self.admin_resources["sub_folder1"] = setup_folder( self.admin_org, parent=self.admin_resources["folder1"]) self.admin_resources["doc1"] = setup_document( self.admin_org, ftl_user=self.admin, binary=setup_temporary_file().name) self.admin_resources["doc2"] = setup_document( self.admin_org, ftl_user=self.admin, ftl_folder=self.admin_resources["folder1"], binary=setup_temporary_file().name, ) self.admin_resources["doc3"] = setup_document( self.admin_org, ftl_user=self.admin, ftl_folder=self.admin_resources["sub_folder1"], binary=setup_temporary_file().name, ) self.admin_resources["totp_device"] = setup_2fa_totp_device( self.admin, secret_key=TOTP_DEVICE_SECRET_KEY) self.admin_resources["fido2_device"] = setup_2fa_fido2_device( self.admin) self.admin_resources["static_device"] = setup_2fa_static_device( self.admin, codes_list=["AAA"]) self.user1_resources = {} self.user1_resources["folder1"] = setup_folder(self.user1_org) self.user1_resources["sub_folder1"] = setup_folder( self.user1_org, parent=self.user1_resources["folder1"]) self.user1_resources["doc1"] = setup_document( self.user1_org, ftl_user=self.user1, binary=setup_temporary_file().name) self.user1_resources["doc2"] = setup_document( self.user1_org, ftl_user=self.user1, ftl_folder=self.user1_resources["folder1"], binary=setup_temporary_file().name, ) self.user1_resources["doc3"] = setup_document( self.user1_org, ftl_user=self.user1, ftl_folder=self.user1_resources["sub_folder1"], binary=setup_temporary_file().name, ) self.user1_resources["totp_device"] = setup_2fa_totp_device( self.user1, secret_key=TOTP_DEVICE_SECRET_KEY) self.user1_resources["fido2_device"] = setup_2fa_fido2_device( self.user1) self.user1_resources["static_device"] = setup_2fa_static_device( self.user1, codes_list=["AAA"]) self.user2_resources = {} self.user2_resources["folder1"] = setup_folder(self.user2_org) self.user2_resources["sub_folder1"] = setup_folder( self.user2_org, parent=self.user2_resources["folder1"]) self.user2_resources["doc1"] = setup_document(self.user2_org, ftl_user=self.user2) self.user2_resources["doc2"] = setup_document( self.user2_org, ftl_user=self.user2, ftl_folder=self.user2_resources["folder1"], ) self.user2_resources["doc3"] = setup_document( self.user2_org, ftl_user=self.user2, ftl_folder=self.user2_resources["sub_folder1"], ) self.user2_resources["totp_device"] = setup_2fa_totp_device( self.user2, secret_key=TOTP_DEVICE_SECRET_KEY) self.user2_resources["fido2_device"] = setup_2fa_fido2_device( self.user2) self.user2_resources["static_device"] = setup_2fa_static_device( self.user2, codes_list=["AAA"])