Add migration, handle sync for mentors and supervisors

This commit is contained in:
Christian Cueni 2024-06-27 09:43:34 +02:00
parent cc3b6bbf0d
commit 8af955f794
6 changed files with 73 additions and 22 deletions

View File

@ -147,10 +147,10 @@ class User(AbstractUser):
self.additional_json_data = (
self.additional_json_data
| sanitize_json_data_input(
{
**data,
}
)
{
**data,
}
)
)
@property

View File

@ -4,6 +4,8 @@ from django.utils.translation import gettext_lazy as _
from keycloak.exceptions import KeycloakDeleteError, KeycloakPostError
from vbv_lernwelt.course.models import CourseSessionUser
from vbv_lernwelt.course_session_group.models import CourseSessionGroup
from vbv_lernwelt.learning_mentor.models import LearningMentor
from vbv_lernwelt.sso.models import SsoSyncError, SsoUser
from vbv_lernwelt.sso.role_sync.services import (
create_and_update_user,
@ -33,6 +35,16 @@ def sync_sso_roles_from_admin(user: User, request):
(csu.course_session.course.slug, csu.role)
for csu in CourseSessionUser.objects.filter(user=user)
]
course_roles += [
(lm.course_session.course.slug, "LEARNING_MENTOR")
for lm in LearningMentor.objects.filter(mentor=user)
]
for csg in CourseSessionGroup.objects.filter(supervisor=user):
for course_session in csg.course_session.all():
course_roles.append((course_session.course.slug, "SUPERVISOR"))
try:
sync_roles_for_user(user, course_roles)
messages.add_message(

View File

@ -1,18 +1,34 @@
# Generated by Django 3.2.25 on 2024-06-20 05:24
# Generated by Django 3.2.25 on 2024-06-26 15:34
import django.contrib.auth.models
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
("core", "0007_auto_20240220_1058"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name="SsoUser",
fields=[],
options={
"proxy": True,
"indexes": [],
"constraints": [],
},
bases=("core.user",),
managers=[
("objects", django.contrib.auth.models.UserManager()),
],
),
migrations.CreateModel(
name="SsoSyncError",
fields=[

View File

@ -24,7 +24,7 @@ if settings.OAUTH_SYNC_ROLES:
user_realm_name=settings.OAUTH_SIGNIN_REALM,
client_id=settings.OAUTH_SIGNIN_ADMIN_CLIENT_ID,
client_secret_key=settings.OAUTH_SIGNIN_ADMIN_CLIENT_SECRET,
verify=False,
verify=True,
)
keycloak_admin = KeycloakAdmin(connection=keycloak_connection)
@ -48,7 +48,7 @@ def _handle_add_remove_action(
action: SsoSyncError.Action,
):
user_id = user.additional_json_data.get("intermediate_sso_id", "")
if settings.OAUTH_SYNC_ROLES and user_id:
if keycloak_admin and user_id:
request_roles = _get_role_request_data(course_roles)
if not request_roles:
return False
@ -65,7 +65,7 @@ def _handle_add_remove_action(
def update_roles_for_user(
user: User, add_course_roles: CourseRolesType, remove_course_roles: CourseRolesType
):
if settings.OAUTH_SYNC_ROLES:
if keycloak_admin:
remove_ret_value = remove_roles_from_user(user, remove_course_roles)
add_ret_value = add_roles_to_user(user, add_course_roles)
return remove_ret_value and add_ret_value
@ -73,10 +73,9 @@ def update_roles_for_user(
def sync_roles_for_user(user: User, course_roles: CourseRolesType):
if settings.OAUTH_SYNC_ROLES:
if keycloak_admin:
user_id = user.additional_json_data.get("intermediate_sso_id", "")
if user_id:
# ignore for the moment, just in the admin
assigned_roles = _filter_non_myvbv_roles(
keycloak_admin.get_realm_roles_of_user(user_id=user_id)
)
@ -91,7 +90,7 @@ def sync_roles_for_user(user: User, course_roles: CourseRolesType):
def create_user(user: User):
if settings.OAUTH_SYNC_ROLES:
if keycloak_admin:
return _kc_create_user(user)
return ""
@ -104,7 +103,7 @@ def create_and_update_user(user: User, save=False):
def get_roles_for_user(user_id: str):
if settings.OAUTH_SYNC_ROLES:
if keycloak_admin:
return keycloak_admin.get_realm_roles_of_user(
user_id=user_id,
)

View File

@ -34,7 +34,10 @@ def update_sso_roles_in_cs(sender, instance: CourseSessionUser, **kwargs):
_add_sso_role(instance.user, instance.course_session.course.slug, instance.role)
else:
old_csu = CourseSessionUser.objects.get(pk=instance.pk)
if old_csu.role != instance.role:
if (
old_csu.role != instance.role
or old_csu.course_session.course != instance.course_session.course
):
try:
update_roles_for_user(
instance.user,
@ -42,7 +45,7 @@ def update_sso_roles_in_cs(sender, instance: CourseSessionUser, **kwargs):
(instance.course_session.course.slug, instance.role)
],
remove_course_roles=[
(instance.course_session.course.slug, old_csu.role)
(old_csu.course_session.course.slug, old_csu.role)
],
)
except KeycloakError:

View File

@ -92,7 +92,9 @@ class CourseSessionUserTests(TestCase):
)
@patch("vbv_lernwelt.sso.signals.update_roles_for_user")
def test_update_role_for_user_on_creation(self, mock_update_roles_for_user):
def test_update_role_for_user_on_update_with_role_change(
self, mock_update_roles_for_user
):
mock_update_roles_for_user.return_value = None
self.csu1_student1.role = "TRAINER"
@ -113,7 +115,32 @@ class CourseSessionUserTests(TestCase):
)
@patch("vbv_lernwelt.sso.signals.update_roles_for_user")
def test_dont_update_role_for_user_on_creation(self, mock_update_roles_for_user):
def test_update_role_for_user_on_update_with_course_change(
self, mock_update_roles_for_user
):
mock_update_roles_for_user.return_value = None
course, self.course_page = create_course("Test Course")
course_session = create_course_session(course=course, title="Test VV")
old_course_slug = self.csu1_student1.course_session.course.slug
self.csu1_student1.course_session = course_session
self.mock_pre_save_signal.send(
sender=CourseSessionUser, instance=self.csu1_student1
)
self.assertEqual(mock_update_roles_for_user.call_count, 1)
mock_update_roles_for_user.assert_called_with(
self.student1,
add_course_roles=[
(self.csu1_student1.course_session.course.slug, "MEMBER")
],
remove_course_roles=[(old_course_slug, "MEMBER")],
)
@patch("vbv_lernwelt.sso.signals.update_roles_for_user")
def test_dont_update_role_for_user_on_update(self, mock_update_roles_for_user):
mock_update_roles_for_user.return_value = None
self.csu1_student1.role = "MEMBER"
@ -138,12 +165,6 @@ class CourseSessionGroupTests(TestCase):
self.trainer = User.objects.get(id=TEST_TRAINER1_USER_ID)
self.supervisor = User.objects.get(id=TEST_SUPERVISOR1_USER_ID)
# m2m_changed.disconnect(receiver=update_sso_roles_in_csg, sender=CourseSessionGroup.supervisor.through)
#
# # Connect a mock signal handler
# self.mock_signal = Signal()
# self.mock_signal.connect(receiver=update_sso_roles_in_csg, sender=CourseSessionGroup.supervisor.through)
@patch("vbv_lernwelt.sso.signals.remove_roles_from_user")
def test_remove_roles_for_csg_supervisors(self, mock_remove_roles_from_user):
mock_remove_roles_from_user.return_value = None