From c723d97fcb3fea95e3f422c0f56b6ab9509c6f74 Mon Sep 17 00:00:00 2001 From: Carol Alexandru Date: Thu, 5 Sep 2024 12:32:06 +0900 Subject: [PATCH] Implement setting assistants via API like participants --- .../uzh/ifi/access/config/SecurityConfig.kt | 2 + .../ifi/access/controller/CourseController.kt | 13 ++++- .../uzh/ifi/access/service/CourseService.kt | 15 ++++-- .../ch/uzh/ifi/access/service/RoleService.kt | 51 ++++++++++++------- 4 files changed, 57 insertions(+), 24 deletions(-) diff --git a/src/main/kotlin/ch/uzh/ifi/access/config/SecurityConfig.kt b/src/main/kotlin/ch/uzh/ifi/access/config/SecurityConfig.kt index 44644c4..b5dadd7 100644 --- a/src/main/kotlin/ch/uzh/ifi/access/config/SecurityConfig.kt +++ b/src/main/kotlin/ch/uzh/ifi/access/config/SecurityConfig.kt @@ -71,6 +71,7 @@ class SecurityConfig(private val env: Environment) { "/courses/contact/**", "/courses/{course}/summary", "/courses/{course}/participants/**", + "/courses/{course}/assistants/**", "/webhooks/**") } .authorizeHttpRequests { authorize -> @@ -83,6 +84,7 @@ class SecurityConfig(private val env: Environment) { ).permitAll() .requestMatchers( "/courses/{course}/participants/**", + "/courses/{course}/assistants/**", "/courses/{course}/summary", "/pruneSubmissions" ).access { _, context -> diff --git a/src/main/kotlin/ch/uzh/ifi/access/controller/CourseController.kt b/src/main/kotlin/ch/uzh/ifi/access/controller/CourseController.kt index 818e9ef..60bbfa4 100644 --- a/src/main/kotlin/ch/uzh/ifi/access/controller/CourseController.kt +++ b/src/main/kotlin/ch/uzh/ifi/access/controller/CourseController.kt @@ -1,5 +1,6 @@ package ch.uzh.ifi.access.controller +import ch.uzh.ifi.access.model.constants.Role import ch.uzh.ifi.access.model.dto.* import ch.uzh.ifi.access.projections.* import ch.uzh.ifi.access.service.CourseService @@ -150,11 +151,21 @@ class CourseController ( @PostMapping("/{course}/participants") fun registerParticipants(@PathVariable course: String, @RequestBody students: List) { // set list of course students - courseService.registerStudents(course, students) + courseService.setStudents(course, students) // update keycloak roles roleService.updateStudentRoles(courseService.getCourseBySlug(course)) } + @PostMapping("/{course}/assistants") + //@PreAuthorize("hasRole('supervisor')") + fun registerAssistants(@PathVariable course: String, @RequestBody assistants: List) { + // set list of course students + courseService.setAssistants(course, assistants) + // update keycloak roles + roleService.updateStudentRoles(courseService.getCourseBySlug(course), + arrayOf(Role.ASSISTANT.withCourse(course))) + } + @GetMapping("/{course}/participants/{participant}") fun getCourseProgress(@PathVariable course: String, @PathVariable participant: String): CourseProgressDTO? { val user = roleService.getUserByUsername(participant)?: diff --git a/src/main/kotlin/ch/uzh/ifi/access/service/CourseService.kt b/src/main/kotlin/ch/uzh/ifi/access/service/CourseService.kt index a38235a..b5d4e4a 100644 --- a/src/main/kotlin/ch/uzh/ifi/access/service/CourseService.kt +++ b/src/main/kotlin/ch/uzh/ifi/access/service/CourseService.kt @@ -726,7 +726,7 @@ exit ${'$'}exit_code; logger.debug { "CourseService updating ${username} roles for ${getCourses().size} courses" } getCourses().forEach { course -> logger.debug { "syncing to ${course.slug}"} - roleService.updateStudentRoles(course, course.registeredStudents, username) + roleService.updateStudentRoles(course, username) } } @@ -817,13 +817,18 @@ exit ${'$'}exit_code; }.toList()) } - fun registerStudents(courseSlug: String, students: List) { + fun setStudents(courseSlug: String, students: List) { val course: Course = getCourseBySlug(courseSlug) - println(students) course.registeredStudents = students.toMutableSet() courseRepository.save(course) - logger.debug { "Registered ${course.registeredStudents.size} in course $courseSlug"} - println() + logger.debug { "Set ${course.registeredStudents.size} students in course $courseSlug"} + } + + fun setAssistants(courseSlug: String, assistants: List) { + val course: Course = getCourseBySlug(courseSlug) + course.assistants = assistants.toMutableSet() + courseRepository.save(course) + logger.debug { "Set ${course.assistants.size} assistants in course $courseSlug"} } fun convertSizeToBytes(sizeStr: String): Long { diff --git a/src/main/kotlin/ch/uzh/ifi/access/service/RoleService.kt b/src/main/kotlin/ch/uzh/ifi/access/service/RoleService.kt index ad2610b..7223a48 100644 --- a/src/main/kotlin/ch/uzh/ifi/access/service/RoleService.kt +++ b/src/main/kotlin/ch/uzh/ifi/access/service/RoleService.kt @@ -125,12 +125,14 @@ class RoleService( } // TODO merge the next two methods - fun updateStudentRoles(course: Course) { - val students = course.registeredStudents - val role = accessRealm.roles()[Role.STUDENT.withCourse(course.slug)] - val rolesToAdd = listOf(role.toRepresentation()) - logger.debug { "A: updating roles for all students (roles to add: ${rolesToAdd})"} - role.getUserMembers(0, -1) + fun updateStudentRoles(course: Course, roleNames: Array = arrayOf(Role.STUDENT.withCourse(course.slug))) { + val students = course.registeredStudents + course.assistants + val roles = roleNames.map { accessRealm.roles()[it] } + val rolesToAdd = roles.map { + it.toRepresentation() + } + logger.debug { "A: updating roles ${rolesToAdd}"} + roles.map { it.getUserMembers(0, -1) }.flatten().toSet() .filter { member: UserRepresentation -> students.stream().noneMatch { student: String -> studentMatchesUser(student, member) } } @@ -142,32 +144,45 @@ class RoleService( students .filter { studentMatchesUser(it, user) } .map { - logger.debug { "A: adding roles ${rolesToAdd} from ${user.username}"} + logger.debug { "A: adding roles ${rolesToAdd} to ${user.username}"} accessRealm.users()[user.id].roles().realmLevel().add(rolesToAdd) accessRealm.users()[user.id].update(updateRoleTimestamp(user)) } } } - fun updateStudentRoles(course: Course, registrationIDs: Set, username: String) { - val role = accessRealm.roles()[Role.STUDENT.withCourse(course.slug)] - val rolesToAdd = listOf(role.toRepresentation()) - logger.debug { "B: updating roles for ${username} (roles to add: ${rolesToAdd})"} - role.getUserMembers(0, -1) + fun updateStudentRoles(course: Course, username: String) { + val registeredStudents = course.registeredStudents + val registeredAssistants = course.assistants + val studentRoleName = Role.STUDENT.withCourse(course.slug) + val assistantRoleName = Role.ASSISTANT.withCourse(course.slug) + val studentRole = accessRealm.roles()[studentRoleName] + val assistantRole = accessRealm.roles()[assistantRoleName] + val studentRoleToAdd = studentRole.toRepresentation() + val assistantRoleToAdd = assistantRole.toRepresentation() + val bothRoles = listOf(studentRole, assistantRole) + val bothRolesToAdd = listOf(studentRoleToAdd, assistantRoleToAdd) + logger.debug { "B: updating roles for ${username} (roles to sync from course ${course.slug}: ${bothRoles})"} + bothRoles.map { it.getUserMembers(0, -1) }.flatten().toSet() .filter { studentMatchesUser(username, it) } .forEach { - logger.debug { "B: removing ${rolesToAdd} from ${username}"} - accessRealm.users()[it.id].roles().realmLevel().remove(rolesToAdd) + logger.debug { "B: removing ${bothRoles} from ${username}"} + accessRealm.users()[it.id].roles().realmLevel().remove(bothRolesToAdd) } accessRealm.users().list(0, -1).forEach { - if (studentMatchesUser(username, it) && userRegisteredForCourse(it, registrationIDs)) { - logger.debug { "B: adding roles ${rolesToAdd} to ${it.username}" } - accessRealm.users()[it.id].roles().realmLevel().add(rolesToAdd) + if (studentMatchesUser(username, it) && userRegisteredForCourse(it, registeredStudents)) { + logger.debug { "B: adding role ${studentRoleToAdd} to ${it.username}" } + accessRealm.users()[it.id].roles().realmLevel().add(listOf(studentRoleToAdd)) + accessRealm.users()[it.id].update(updateRoleTimestamp(it)) + } + if (studentMatchesUser(username, it) && userRegisteredForCourse(it, registeredAssistants)) { + logger.debug { "B: adding roles ${assistantRoleToAdd} to ${it.username}" } + accessRealm.users()[it.id].roles().realmLevel().add(listOf(assistantRoleToAdd)) accessRealm.users()[it.id].update(updateRoleTimestamp(it)) } - else if (studentMatchesUser(username, it)) { + if (studentMatchesUser(username, it)) { logger.debug { "Y: matching user ${username} to ${it.username} did not register for course ${course.slug}" } } }