from unittest.mock import patch from django.test import override_settings, TestCase from keycloak.exceptions import KeycloakDeleteError, KeycloakPostError from vbv_lernwelt.core.models import User from vbv_lernwelt.sso.models import SsoSyncError from vbv_lernwelt.sso.role_sync.services import ( _filter_non_myvbv_roles, _remove_accents, add_roles_to_user, create_user, remove_roles_from_user, sync_roles_for_user, update_roles_for_user, ) @override_settings(OAUTH_SYNC_ROLES=True) class ApiTestCase(TestCase): def setUp(self): self.user = User(email="test@example.com", first_name="Test", last_name="User") self.user.additional_json_data = {"intermediate_sso_id": "1234"} self.user.save() self.course_roles = [ ("überbetriebliche-kurse", "EXPERT"), ("versicherungsvermittler-in", "MEMBER"), ] self.expected_roles = [ { "name": "myvbv-uberbetriebliche-kurse-expert", "id": "c7e33cb6-d227-4764-9b8e-d42af79fb46d", }, { "name": "myvbv-versicherungsvermittler-in-member", "id": "3ab4eab2-7d7c-43bb-a927-4cf54f24ccc2", }, ] @patch("vbv_lernwelt.sso.role_sync.services.keycloak_admin") def test_add_roles_to_user_success(self, mock_keycloak_admin): mock_keycloak_admin.assign_realm_roles.return_value = None result = add_roles_to_user(self.user, self.course_roles) self.assertTrue(result) mock_keycloak_admin.assign_realm_roles.assert_called_once_with( user_id="1234", roles=self.expected_roles ) @patch("vbv_lernwelt.sso.role_sync.services.keycloak_admin") def test_add_roles_to_user_keycloak_post_error(self, mock_keycloak_admin): mock_keycloak_admin.assign_realm_roles.side_effect = KeycloakPostError with self.assertRaises(KeycloakPostError) as cm: add_roles_to_user(self.user, self.course_roles) exception = cm.exception self.assertIsInstance(exception, KeycloakPostError) error_obj = SsoSyncError.objects.get(user=self.user) self.assertEqual(error_obj.data, self.expected_roles) self.assertEqual(error_obj.action, SsoSyncError.Action.ADD) @patch("vbv_lernwelt.sso.role_sync.services.keycloak_admin") def test_remove_roles_to_user_success(self, mock_keycloak_admin): mock_keycloak_admin.delete_realm_roles_of_user.return_value = None result = remove_roles_from_user(self.user, self.course_roles) self.assertTrue(result) mock_keycloak_admin.delete_realm_roles_of_user.assert_called_once_with( user_id="1234", roles=self.expected_roles ) @patch("vbv_lernwelt.sso.role_sync.services.keycloak_admin") def test_remove_roles_to_user_keycloak_delete_error(self, mock_keycloak_admin): mock_keycloak_admin.delete_realm_roles_of_user.side_effect = KeycloakDeleteError with self.assertRaises(KeycloakDeleteError) as cm: remove_roles_from_user(self.user, self.course_roles) exception = cm.exception self.assertIsInstance(exception, KeycloakDeleteError) error_obj = SsoSyncError.objects.get(user=self.user) self.assertEqual(error_obj.data, self.expected_roles) self.assertEqual(error_obj.action, SsoSyncError.Action.REMOVE) @patch("vbv_lernwelt.sso.role_sync.services.remove_roles_from_user") @patch("vbv_lernwelt.sso.role_sync.services.add_roles_to_user") def test_update_roles_to_user( self, mock_add_roles_to_user, mock_remove_roles_from_user ): mock_add_roles_to_user.return_value = True mock_remove_roles_from_user.return_value = True update_roles_for_user(self.user, self.course_roles, self.course_roles) mock_add_roles_to_user.assert_called_once() mock_remove_roles_from_user.assert_called_once() @patch("vbv_lernwelt.sso.role_sync.services.keycloak_admin") def test_sync_roles_to_user(self, mock_keycloak_admin): mock_keycloak_admin.get_realm_roles_of_user.return_value = ( self.expected_roles ) # just use them here as well mock_keycloak_admin.delete_realm_roles_of_user.return_value = True mock_keycloak_admin.assign_realm_roles.return_value = None sync_roles_for_user(self.user, self.course_roles) mock_keycloak_admin.get_realm_roles_of_user.assert_called_once_with( user_id="1234" ) mock_keycloak_admin.delete_realm_roles_of_user.assert_called_once_with( user_id="1234", roles=self.expected_roles ) mock_keycloak_admin.assign_realm_roles.assert_called_once_with( user_id="1234", roles=self.expected_roles ) @patch("vbv_lernwelt.sso.role_sync.services.keycloak_admin") def test_create_user(self, mock_keycloak_admin): mock_keycloak_admin.create_user.return_value = "im-an-uuid-1234" user_data = { "username": self.user.email, "email": self.user.email, "enabled": True, "firstName": self.user.first_name, "lastName": self.user.last_name, } create_user(self.user) mock_keycloak_admin.create_user.assert_called_once_with( user_data, exist_ok=True ) @patch("vbv_lernwelt.sso.role_sync.services.keycloak_admin") def test_ignore_missing_course(self, mock_keycloak_admin): mock_keycloak_admin.assign_realm_roles.return_value = None course_roles = [ ("blabla-kurse", "EXPERT"), ] result = add_roles_to_user(self.user, course_roles) self.assertFalse(result) mock_keycloak_admin.assign_realm_roles.assert_not_called() class HelpersTestCase(TestCase): def test_remove_accents(self): no_accents = _remove_accents("äüöéèà") self.assertEqual(no_accents, "auoeea") def test_filter_non_myvbv_roles(self): roles = [ {"name": "myvbv-uberbetriebliche-kurse-expert"}, {"name": "myvbv-versicherungsvermittler-in-member"}, {"name": "other-role"}, ] filtered_roles = [ {"name": "myvbv-uberbetriebliche-kurse-expert"}, {"name": "myvbv-versicherungsvermittler-in-member"}, ] result = _filter_non_myvbv_roles(roles) self.assertEqual(result, filtered_roles)