Skip to content

Commit

Permalink
Implement setting assistants via API like participants
Browse files Browse the repository at this point in the history
  • Loading branch information
sealexan committed Sep 5, 2024
1 parent f6dab52 commit 7b2fc37
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 22 deletions.
2 changes: 2 additions & 0 deletions src/main/kotlin/ch/uzh/ifi/access/config/SecurityConfig.kt
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ class SecurityConfig(private val env: Environment) {
"/courses/contact/**",
"/courses/{course}/summary",
"/courses/{course}/participants/**",
"/courses/{course}/assistants/**",
"/webhooks/**")
}
.authorizeHttpRequests { authorize ->
Expand All @@ -83,6 +84,7 @@ class SecurityConfig(private val env: Environment) {
).permitAll()
.requestMatchers(
"/courses/{course}/participants/**",
"/courses/{course}/assistants/**",
"/courses/{course}/summary",
"/pruneSubmissions"
).access { _, context ->
Expand Down
13 changes: 12 additions & 1 deletion src/main/kotlin/ch/uzh/ifi/access/controller/CourseController.kt
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -150,11 +151,21 @@ class CourseController (
@PostMapping("/{course}/participants")
fun registerParticipants(@PathVariable course: String, @RequestBody students: List<String>) {
// 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<String>) {
// 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)?:
Expand Down
15 changes: 10 additions & 5 deletions src/main/kotlin/ch/uzh/ifi/access/service/CourseService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}

Expand Down Expand Up @@ -817,13 +817,18 @@ exit ${'$'}exit_code;
}.toList())
}

fun registerStudents(courseSlug: String, students: List<String>) {
fun setStudents(courseSlug: String, students: List<String>) {
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<String>) {
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 {
Expand Down
47 changes: 31 additions & 16 deletions src/main/kotlin/ch/uzh/ifi/access/service/RoleService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -125,12 +125,14 @@ class RoleService(
}

// TODO merge the next two methods
fun updateStudentRoles(course: Course) {
fun updateStudentRoles(course: Course, roleNames: Array<String> = arrayOf(Role.STUDENT.withCourse(course.slug))) {
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)
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) }
}
Expand All @@ -149,25 +151,38 @@ class RoleService(
}
}

fun updateStudentRoles(course: Course, registrationIDs: Set<String>, 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}" }
}
}
Expand Down

0 comments on commit 7b2fc37

Please sign in to comment.