def test_otp_totp_check_context(self): # given user have confirmed is totp device self.totp_device.confirmed = True self.totp_device.save() response = self.client.get("/accounts/2fa/totp/check/") self.assertEqual(response.context["have_totp"], True) self.assertEqual(response.context["have_fido2"], False) self.assertEqual(response.context["have_static"], False) # given user setup totp + fido2 devices setup_2fa_fido2_device(self.user) response = self.client.get("/accounts/2fa/totp/check/") self.assertEqual(response.context["have_totp"], True) self.assertEqual(response.context["have_fido2"], 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/totp/check/") self.assertEqual(response.context["have_totp"], True) self.assertEqual(response.context["have_fido2"], True) self.assertEqual(response.context["have_static"], True)
def test_security_key_added(self): # User has already added a security key key_name = "O Key" setup_2fa_fido2_device(self.user, name=key_name) self.visit(AccountPages.two_factors_authentication_url) # refresh page # User can see the security key in the list self.assertIn(key_name, self.get_elem_text(self.security_key_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() # Check page appears ask for fido2 self.assertIn("security key", self.get_elem_text(self.confirm_button).lower()) # No alternative 2FA are shown with self.assertRaises(NoSuchElementException): self.get_elem(self.check_pages_alternatives_list)
def test_delete_security_key_with_emergency_code(self): # User has already added a security key and a emergency codes set setup_2fa_fido2_device(self.user) setup_2fa_static_device(self.user) self.visit(AccountPages.two_factors_authentication_url) # refresh page # User delete existing security key self.get_elems(self.delete_security_key_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_rename_security_key(self): # User has already added a security key old_key_name = "MO Key" setup_2fa_fido2_device(self.user, name=old_key_name) self.visit(AccountPages.two_factors_authentication_url) # refresh page # User rename existing security key new_key_name = "O Key" self.rename_security_key(new_name=new_key_name) # User see the key name have been updated in the device list self.wait_for_elem_to_show(self.security_key_divs) self.assertNotIn(old_key_name, self.get_elem_text(self.security_key_divs)) self.assertIn(new_key_name, self.get_elem_text(self.security_key_divs))
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_otp_fido2_api_login_begin(self, acd_mock, cbor2_mock, pkcre_mock, fido2_server_mock): # given user setup another fido2_device setup_2fa_fido2_device(self.user, name="second fido device") authenticate_begin_return_value = ["fake_auth_data", "fake_state"] fido2_server_mock().authenticate_begin.return_value = ( authenticate_begin_return_value) fido2_server_mock.reset_mock( ) # previous assignment count as a call so we need to reset mock counter fake_credentials_list = ["fake_credential_1", "fake_credential_2"] acd_mock.side_effect = fake_credentials_list fake_cbor2_loaded_list = ["fake_cbor2_loaded_1", "fake_cbor2_loaded_2"] cbor2_mock.loads.side_effect = fake_cbor2_loaded_list cbor2_mock.dumps.return_value = b"fakeAuthData" response = self.client.get("/accounts/2fa/fido2/api/login_begin") self.assertEqual(acd_mock.call_count, 2) acd_mock.assert_any_call(fake_cbor2_loaded_list[0]) acd_mock.assert_any_call(fake_cbor2_loaded_list[1]) pkcre_mock.assert_called_once_with("testserver", settings.FIDO2_RP_NAME) fido2_server_mock.assert_called_once_with(pkcre_mock()) fido2_server_mock().authenticate_begin.assert_called_once_with( fake_credentials_list, user_verification="discouraged", ) self.assertEqual( response.wsgi_request.session.get("fido2_state"), authenticate_begin_return_value[1], ) self.assertEqual(response.wsgi_request.session.get("fido2_domain"), "testserver") cbor2_mock.dumps.assert_called_once_with( authenticate_begin_return_value[0]) self.assertEqual(response.content, cbor2_mock.dumps.return_value) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response["Content-Type"], "application/cbor")
def test_otp_fido2_api_register_begin(self, fido2_server_mock, pkcre_mock, cbor2_mock, acd_mock): # given user setup another fido2_device fido2_device_2 = setup_2fa_fido2_device(self.user, name="second fido device") register_begin_return_value = ["fake_registration_data", "fake_state"] fido2_server_mock( ).register_begin.return_value = register_begin_return_value fido2_server_mock.reset_mock( ) # previous assignment count as a call so we need to reset mock counter fake_credentials_list = ["fake_credential_1", "fake_credential_2°"] acd_mock.side_effect = fake_credentials_list fake_cbor2_loaded_list = ["fake_cbor2_loaded_1", "fake_cbor2_loaded_2"] cbor2_mock.loads.side_effect = fake_cbor2_loaded_list cbor2_mock.dumps.return_value = b"fake_registration_data" response = self.client.get("/accounts/2fa/fido2/api/register_begin") pkcre_mock.assert_called_once_with("testserver", settings.FIDO2_RP_NAME) fido2_server_mock.assert_called_once_with(pkcre_mock()) self.assertEqual(cbor2_mock.loads.call_count, 2) _, loads_mock_first_call_args, _ = cbor2_mock.loads.mock_calls[0] _, loads_mock_second_call_args, _ = cbor2_mock.loads.mock_calls[1] self.assertEqual( loads_mock_first_call_args[0].tobytes(), self.fido2_device.authenticator_data, ) self.assertEqual(loads_mock_second_call_args[0].tobytes(), fido2_device_2.authenticator_data) fido2_server_mock().register_begin.assert_called_once_with( { "id": self.user.email.encode(), "name": self.user.email, "displayName": self.user.email, "icon": "", }, credentials=fake_credentials_list, user_verification="discouraged", authenticator_attachment="cross-platform", ) cbor2_mock.dumps.assert_called_once_with( register_begin_return_value[0]) self.assertEqual( response.wsgi_request.session.get("fido2_register_state"), register_begin_return_value[1], ) self.assertEqual(response.content, cbor2_mock.dumps.return_value) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response["Content-Type"], "application/octet-stream")
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 test_delete_security_key(self): # User has already added a security key setup_2fa_fido2_device(self.user) self.visit(AccountPages.two_factors_authentication_url) # refresh page # User delete existing security key self.delete_security_key() # User see the set have been remove from the device list with self.assertRaises(NoSuchElementException): self.get_elem(self.security_key_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_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_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 fido2 device and the user is logged self.org = setup_org() self.user = setup_user(self.org) self.fido2_device = setup_2fa_fido2_device(self.user) 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_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): 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 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"])