diff --git a/src/main/java/org/olat/restapi/repository/course/CourseWebService.java b/src/main/java/org/olat/restapi/repository/course/CourseWebService.java index ba0c26fdf5c04a88eaa159afb45a8b502ee57c39..8a122cf3b5f7a1a559cc8ec5b470a3e852a09830 100644 --- a/src/main/java/org/olat/restapi/repository/course/CourseWebService.java +++ b/src/main/java/org/olat/restapi/repository/course/CourseWebService.java @@ -24,6 +24,7 @@ import static org.olat.restapi.security.RestSecurityHelper.getUserRequest; import static org.olat.restapi.security.RestSecurityHelper.isAdmin; import static org.olat.restapi.security.RestSecurityHelper.isAuthor; import static org.olat.restapi.security.RestSecurityHelper.isAuthorEditor; +import static org.olat.restapi.security.RestSecurityHelper.isInstitutionalResourceManager; import java.util.ArrayList; import java.util.Collections; @@ -222,7 +223,7 @@ public class CourseWebService { } UserRequest ureq = getUserRequest(request); - if (!isAuthorEditor(course, request)) { + if (!isAuthorEditor(course, request) && !isInstitutionalResourceManager(request)) { return Response.serverError().status(Status.UNAUTHORIZED).build(); } @@ -343,7 +344,7 @@ public class CourseWebService { public Response deleteCourse(@Context HttpServletRequest request) { if(!isAuthor(request)) { return Response.serverError().status(Status.UNAUTHORIZED).build(); - } else if (!isAuthorEditor(course, request)) { + } else if (!isAuthorEditor(course, request) && !isInstitutionalResourceManager(request)) { return Response.serverError().status(Status.UNAUTHORIZED).build(); } @@ -383,7 +384,7 @@ public class CourseWebService { public Response deleteCoursePermanently(@FormParam("newStatus") String newStatus, @Context HttpServletRequest request) { if(!isAuthor(request)) { return Response.serverError().status(Status.UNAUTHORIZED).build(); - } else if (!isAuthorEditor(course, request)) { + } else if (!isAuthorEditor(course, request) && !isInstitutionalResourceManager(request)) { return Response.serverError().status(Status.UNAUTHORIZED).build(); } @@ -437,7 +438,7 @@ public class CourseWebService { public Response getConfiguration(@Context HttpServletRequest request) { if(!isAuthor(request)) { return Response.serverError().status(Status.UNAUTHORIZED).build(); - } else if (!isAuthorEditor(course, request)) { + } else if (!isAuthorEditor(course, request) && !isInstitutionalResourceManager(request)) { return Response.serverError().status(Status.UNAUTHORIZED).build(); } CourseConfigVO vo = ObjectFactory.getConfig(course); @@ -520,7 +521,7 @@ public class CourseWebService { @Path("runstructure") @Produces(MediaType.APPLICATION_XML) public Response findRunStructureById(@Context HttpServletRequest httpRequest, @Context Request request) { - if (!isAuthorEditor(course, httpRequest)) { + if (!isAuthorEditor(course, httpRequest) && !isInstitutionalResourceManager(httpRequest)) { return Response.serverError().status(Status.UNAUTHORIZED).build(); } @@ -548,7 +549,7 @@ public class CourseWebService { @Path("editortreemodel") @Produces(MediaType.APPLICATION_XML) public Response findEditorTreeModelById(@Context HttpServletRequest httpRequest, @Context Request request) { - if (!isAuthorEditor(course, httpRequest)) { + if (!isAuthorEditor(course, httpRequest) && !isInstitutionalResourceManager(httpRequest)) { return Response.serverError().status(Status.UNAUTHORIZED).build(); } VFSItem editorModelItem = course.getCourseBaseContainer().resolve("editortreemodel.xml"); @@ -575,7 +576,7 @@ public class CourseWebService { @Path("authors") @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) public Response getAuthors(@Context HttpServletRequest httpRequest) { - if (!isAuthorEditor(course, httpRequest)) { + if (!isAuthorEditor(course, httpRequest) && !isInstitutionalResourceManager(httpRequest)) { return Response.serverError().status(Status.UNAUTHORIZED).build(); } @@ -605,7 +606,7 @@ public class CourseWebService { @Path("tutors") @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) public Response getTutors(@Context HttpServletRequest httpRequest) { - if (!isAuthorEditor(course, httpRequest)) { + if (!isAuthorEditor(course, httpRequest) && !isInstitutionalResourceManager(httpRequest)) { return Response.serverError().status(Status.UNAUTHORIZED).build(); } @@ -635,7 +636,7 @@ public class CourseWebService { @Path("participants") @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) public Response getParticipants(@Context HttpServletRequest httpRequest) { - if (!isAuthorEditor(course, httpRequest)) { + if (!isAuthorEditor(course, httpRequest) && !isInstitutionalResourceManager(httpRequest)) { return Response.serverError().status(Status.UNAUTHORIZED).build(); } @@ -667,7 +668,7 @@ public class CourseWebService { @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) public Response getAuthor(@PathParam("identityKey") Long identityKey, @Context HttpServletRequest httpRequest) { - if (!isAuthorEditor(course, httpRequest)) { + if (!isAuthorEditor(course, httpRequest) && !isInstitutionalResourceManager(httpRequest)) { return Response.serverError().status(Status.UNAUTHORIZED).build(); } @@ -699,7 +700,7 @@ public class CourseWebService { @Path("authors/{identityKey}") public Response addAuthor(@PathParam("identityKey") Long identityKey, @Context HttpServletRequest httpRequest) { - if (!isAuthorEditor(course, httpRequest)) { + if (!isAuthorEditor(course, httpRequest) && !isInstitutionalResourceManager(httpRequest)) { return Response.serverError().status(Status.UNAUTHORIZED).build(); } @@ -733,7 +734,7 @@ public class CourseWebService { @Path("authors") @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) public Response addAuthors(UserVO[] authors, @Context HttpServletRequest httpRequest) { - if (!isAuthorEditor(course, httpRequest)) { + if (!isAuthorEditor(course, httpRequest) && !isInstitutionalResourceManager(httpRequest)) { return Response.serverError().status(Status.UNAUTHORIZED).build(); } @@ -769,10 +770,9 @@ public class CourseWebService { */ @DELETE @Path("authors/{identityKey}") - @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) public Response removeAuthor(@PathParam("identityKey") Long identityKey, @Context HttpServletRequest httpRequest) { - if (!isAuthorEditor(course, httpRequest)) { + if (!isAuthorEditor(course, httpRequest) && !isInstitutionalResourceManager(httpRequest)) { return Response.serverError().status(Status.UNAUTHORIZED).build(); } @@ -805,7 +805,7 @@ public class CourseWebService { @Path("tutors/{identityKey}") public Response addCoach(@PathParam("identityKey") Long identityKey, @Context HttpServletRequest httpRequest) { - if (!isAuthorEditor(course, httpRequest)) { + if (!isAuthorEditor(course, httpRequest) && !isInstitutionalResourceManager(httpRequest)) { return Response.serverError().status(Status.UNAUTHORIZED).build(); } @@ -832,7 +832,7 @@ public class CourseWebService { @Path("tutors") @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) public Response addCoaches(UserVO[] coaches, @Context HttpServletRequest httpRequest) { - if (!isAuthorEditor(course, httpRequest)) { + if (!isAuthorEditor(course, httpRequest) && !isInstitutionalResourceManager(httpRequest)) { return Response.serverError().status(Status.UNAUTHORIZED).build(); } @@ -848,6 +848,39 @@ public class CourseWebService { return Response.ok().build(); } + /** + * Remove a coach from the course + * @response.representation.200.doc The user was successfully removed as coach of the course + * @response.representation.401.doc The roles of the authenticated user are not sufficient + * @response.representation.404.doc The course or the user not found + * @param identityKey The user identifier + * @param httpRequest The HTTP request + * @return It returns 200 if the user is removed as coach of the course + */ + @DELETE + @Path("tutors/{identityKey}") + public Response removeCoach(@PathParam("identityKey") Long identityKey, + @Context HttpServletRequest httpRequest) { + if (!isAuthorEditor(course, httpRequest) && !isInstitutionalResourceManager(httpRequest)) { + return Response.serverError().status(Status.UNAUTHORIZED).build(); + } + + BaseSecurity securityManager = BaseSecurityManager.getInstance(); + Identity coach = securityManager.loadIdentityByKey(identityKey, false); + if(coach == null) { + return Response.serverError().status(Status.NOT_FOUND).build(); + } + + Identity identity = getIdentity(httpRequest); + + //remove the user as coach of the course + RepositoryManager rm = RepositoryManager.getInstance(); + RepositoryEntry repositoryEntry = course.getCourseEnvironment().getCourseGroupManager().getCourseEntry(); + List<Identity> coaches = Collections.singletonList(coach); + rm.removeTutors(identity, coaches, repositoryEntry, new MailPackage(false)); + return Response.ok().build(); + } + /** * Add an participant to the course * @response.representation.200.doc The user is a participant of the course @@ -861,7 +894,7 @@ public class CourseWebService { @Path("participants/{identityKey}") public Response addParticipant(@PathParam("identityKey") Long identityKey, @Context HttpServletRequest httpRequest) { - if (!isAuthorEditor(course, httpRequest)) { + if (!isAuthorEditor(course, httpRequest) && !isInstitutionalResourceManager(httpRequest)) { return Response.serverError().status(Status.UNAUTHORIZED).build(); } @@ -898,7 +931,7 @@ public class CourseWebService { @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) public Response addParticipants(UserVO[] participants, @Context HttpServletRequest httpRequest) { - if (!isAuthorEditor(course, httpRequest)) { + if (!isAuthorEditor(course, httpRequest) && !isInstitutionalResourceManager(httpRequest)) { return Response.serverError().status(Status.UNAUTHORIZED).build(); } @@ -914,6 +947,39 @@ public class CourseWebService { return Response.ok().build(); } + /** + * Remove a participant from the course + * @response.representation.200.doc The user was successfully removed as participant of the course + * @response.representation.401.doc The roles of the authenticated user are not sufficient + * @response.representation.404.doc The course or the user not found + * @param identityKey The user identifier + * @param httpRequest The HTTP request + * @return It returns 200 if the user is removed as participant of the course + */ + @DELETE + @Path("participants/{identityKey}") + public Response removeParticipant(@PathParam("identityKey") Long identityKey, + @Context HttpServletRequest httpRequest) { + if (!isAuthorEditor(course, httpRequest) && !isInstitutionalResourceManager(httpRequest)) { + return Response.serverError().status(Status.UNAUTHORIZED).build(); + } + + BaseSecurity securityManager = BaseSecurityManager.getInstance(); + Identity participant = securityManager.loadIdentityByKey(identityKey, false); + if(participant == null) { + return Response.serverError().status(Status.NOT_FOUND).build(); + } + + Identity identity = getIdentity(httpRequest); + + //remove the user as participant of the course + RepositoryManager rm = RepositoryManager.getInstance(); + RepositoryEntry repositoryEntry = course.getCourseEnvironment().getCourseGroupManager().getCourseEntry(); + List<Identity> participants = Collections.singletonList(participant); + rm.removeParticipants(identity, participants, repositoryEntry, new MailPackage(false), false); + return Response.ok().build(); + } + private List<Identity> loadIdentities(UserVO[] users) { List<Long> identityKeys = new ArrayList<>(); for(UserVO user:users) { diff --git a/src/main/java/org/olat/restapi/security/RestSecurityHelper.java b/src/main/java/org/olat/restapi/security/RestSecurityHelper.java index 4ca6e30f5e5eda429165042034fab13825603a0a..097b4cb10c9433fbb27e2737119e092cc914351f 100644 --- a/src/main/java/org/olat/restapi/security/RestSecurityHelper.java +++ b/src/main/java/org/olat/restapi/security/RestSecurityHelper.java @@ -96,7 +96,7 @@ public class RestSecurityHelper { public static boolean isAuthor(HttpServletRequest request) { try { Roles roles = getRoles(request); - return (roles.isAuthor() || roles.isOLATAdmin()); + return (roles.isAuthor() || roles.isOLATAdmin() || roles.isInstitutionalResourceManager()); } catch (Exception e) { return false; } @@ -151,6 +151,15 @@ public class RestSecurityHelper { } } + public static boolean isInstitutionalResourceManager(HttpServletRequest request) { + try { + Roles roles = getRoles(request); + return (roles.isInstitutionalResourceManager() || roles.isOLATAdmin()); + } catch (Exception e) { + return false; + } + } + public static boolean isQuestionPoolManager(HttpServletRequest request) { try { Roles roles = getRoles(request); diff --git a/src/test/java/org/olat/restapi/CourseTest.java b/src/test/java/org/olat/restapi/CourseTest.java index b76d8b70d4b1ca42dc53bdd60ae0508ca65a905f..b5f47ec8bf02ac80b2bb86474e502fbd0ba47a58 100644 --- a/src/test/java/org/olat/restapi/CourseTest.java +++ b/src/test/java/org/olat/restapi/CourseTest.java @@ -354,7 +354,7 @@ public class CourseTest extends OlatJerseyTestCase { //make auth1 and auth2 owner RepositoryEntry repositoryEntry = repositoryManager.lookupRepositoryEntry(course1, true); - List<Identity> authors = new ArrayList<Identity>(); + List<Identity> authors = new ArrayList<>(); authors.add(auth1); authors.add(auth2); IdentitiesAddEvent identitiesAddedEvent = new IdentitiesAddEvent(authors); @@ -425,6 +425,29 @@ public class CourseTest extends OlatJerseyTestCase { dbInstance.intermediateCommit(); assertTrue(isTutor); } + + @Test + public void removeCoach() throws IOException, URISyntaxException { + //add a coach + Identity coach = JunitTestHelper.createAndPersistIdentityAsRndUser("Course-coach"); + RepositoryEntry repositoryEntry = repositoryManager.lookupRepositoryEntry(course1, true); + repositoryService.addRole(coach, repositoryEntry, GroupRoles.coach.name()); + dbInstance.commitAndCloseSession(); + boolean isTutor = repositoryService.hasRole(coach, repositoryEntry, GroupRoles.coach.name()); + Assert.assertTrue(isTutor); + + //test remove + assertTrue(conn.login("administrator", "openolat")); + URI request = UriBuilder.fromUri(getContextURI()).path("/repo/courses/" + course1.getResourceableId() + "/tutors/" + coach.getKey()).build(); + HttpDelete method = conn.createDelete(request, MediaType.APPLICATION_JSON); + HttpResponse response = conn.execute(method); + assertEquals(200, response.getStatusLine().getStatusCode()); + EntityUtils.consume(response.getEntity()); + + //check database + boolean deletedCoach = repositoryService.hasRole(coach, repositoryEntry, GroupRoles.coach.name()); + Assert.assertFalse(deletedCoach); + } @Test public void addCoaches() throws IOException, URISyntaxException { @@ -500,6 +523,29 @@ public class CourseTest extends OlatJerseyTestCase { Assert.assertTrue(isParticipant); } + @Test + public void removeParticipant() throws IOException, URISyntaxException { + //add a coach + Identity participant = JunitTestHelper.createAndPersistIdentityAsRndUser("Course-part"); + RepositoryEntry repositoryEntry = repositoryManager.lookupRepositoryEntry(course1, true); + repositoryService.addRole(participant, repositoryEntry, GroupRoles.participant.name()); + dbInstance.commitAndCloseSession(); + boolean isParticipant = repositoryService.hasRole(participant, repositoryEntry, GroupRoles.participant.name()); + Assert.assertTrue(isParticipant); + + //test remove + assertTrue(conn.login("administrator", "openolat")); + URI request = UriBuilder.fromUri(getContextURI()).path("/repo/courses/" + course1.getResourceableId() + "/participants/" + participant.getKey()).build(); + HttpDelete method = conn.createDelete(request, MediaType.APPLICATION_JSON); + HttpResponse response = conn.execute(method); + assertEquals(200, response.getStatusLine().getStatusCode()); + EntityUtils.consume(response.getEntity()); + + //check database + boolean stillParticipant = repositoryService.hasRole(participant, repositoryEntry, GroupRoles.participant.name()); + Assert.assertFalse(stillParticipant); + } + @Test public void addParticipants() throws IOException, URISyntaxException { Assert.assertTrue(conn.login("administrator", "openolat"));