Skip to content
Snippets Groups Projects
Commit 68692631 authored by Daniel Gerhardt's avatar Daniel Gerhardt
Browse files

Merge branch 'room-moderators' into 'master'

Room moderators

See merge request arsnova/arsnova-backend!131
parents 170a4a5b f32e330f
Branches
1 merge request!131Room moderators
Pipeline #26908 passed with warnings with stages
in 1 minute and 54 seconds
...@@ -19,13 +19,22 @@ package de.thm.arsnova.controller; ...@@ -19,13 +19,22 @@ package de.thm.arsnova.controller;
import de.thm.arsnova.model.Room; import de.thm.arsnova.model.Room;
import de.thm.arsnova.service.RoomService; import de.thm.arsnova.service.RoomService;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import java.util.Set;
@RestController @RestController
@RequestMapping(RoomController.REQUEST_MAPPING) @RequestMapping(RoomController.REQUEST_MAPPING)
public class RoomController extends AbstractEntityController<Room> { public class RoomController extends AbstractEntityController<Room> {
protected static final String REQUEST_MAPPING = "/room"; protected static final String REQUEST_MAPPING = "/room";
private static final String GET_MODERATORS_MAPPING = DEFAULT_ID_MAPPING + "/moderator";
private static final String MODERATOR_MAPPING = DEFAULT_ID_MAPPING + "/moderator/{userId}";
private RoomService roomService; private RoomService roomService;
...@@ -43,4 +52,29 @@ public class RoomController extends AbstractEntityController<Room> { ...@@ -43,4 +52,29 @@ public class RoomController extends AbstractEntityController<Room> {
protected String resolveAlias(final String shortId) { protected String resolveAlias(final String shortId) {
return roomService.getIdByShortId(shortId); return roomService.getIdByShortId(shortId);
} }
@GetMapping(GET_MODERATORS_MAPPING)
public Set<Room.Moderator> getModerators(@PathVariable final String id) {
return roomService.get(id).getModerators();
}
@PutMapping(MODERATOR_MAPPING)
public void putModerator(@PathVariable final String id, @PathVariable final String userId,
@RequestBody final Room.Moderator moderator) {
final Room room = roomService.get(id);
moderator.setUserId(userId);
if (moderator.getRoles().isEmpty()) {
moderator.getRoles().add(Room.Moderator.Role.EXECUTIVE_MODERATOR);
}
room.getModerators().removeIf(m -> m.getUserId().equals(userId));
room.getModerators().add(moderator);
roomService.update(room);
}
@DeleteMapping(MODERATOR_MAPPING)
public void deleteModerator(@PathVariable final String id, @PathVariable final String userId) {
final Room room = roomService.get(id);
room.getModerators().removeIf(m -> m.getUserId().equals(userId));
roomService.update(room);
}
} }
...@@ -98,6 +98,40 @@ public class Room extends Entity { ...@@ -98,6 +98,40 @@ public class Room extends Entity {
} }
} }
public static class Moderator {
public enum Role {
EDITING_MODERATOR,
EXECUTIVE_MODERATOR
}
private String userId;
private Set<Role> roles;
@JsonView({View.Persistence.class, View.Public.class})
public String getUserId() {
return userId;
}
@JsonView({View.Persistence.class, View.Public.class})
public void setUserId(final String userId) {
this.userId = userId;
}
@JsonView({View.Persistence.class, View.Public.class})
public Set<Role> getRoles() {
if (roles == null) {
roles = new HashSet<>();
}
return roles;
}
@JsonView({View.Persistence.class, View.Public.class})
public void setRoles(final Set<Role> roles) {
this.roles = roles;
}
}
public static class Settings { public static class Settings {
private boolean questionsEnabled = true; private boolean questionsEnabled = true;
private boolean slidesEnabled = true; private boolean slidesEnabled = true;
...@@ -420,6 +454,7 @@ public class Room extends Entity { ...@@ -420,6 +454,7 @@ public class Room extends Entity {
private String description; private String description;
private boolean closed; private boolean closed;
private Set<ContentGroup> contentGroups; private Set<ContentGroup> contentGroups;
private Set<Moderator> moderators;
private Settings settings; private Settings settings;
private Author author; private Author author;
private PoolProperties poolProperties; private PoolProperties poolProperties;
...@@ -509,6 +544,20 @@ public class Room extends Entity { ...@@ -509,6 +544,20 @@ public class Room extends Entity {
this.contentGroups = new HashSet<>(groups.values()); this.contentGroups = new HashSet<>(groups.values());
} }
@JsonView(View.Persistence.class)
public Set<Moderator> getModerators() {
if (moderators == null) {
moderators = new HashSet<>();
}
return moderators;
}
@JsonView(View.Persistence.class)
public void setModerators(final Set<Moderator> moderators) {
this.moderators = moderators;
}
@JsonView({View.Persistence.class, View.Public.class}) @JsonView({View.Persistence.class, View.Public.class})
public Settings getSettings() { public Settings getSettings() {
if (settings == null) { if (settings == null) {
......
...@@ -30,6 +30,7 @@ public interface RoomRepository extends CrudRepository<Room, String> { ...@@ -30,6 +30,7 @@ public interface RoomRepository extends CrudRepository<Room, String> {
List<Room> findByOwner(ClientAuthentication owner, int start, int limit); List<Room> findByOwner(ClientAuthentication owner, int start, int limit);
List<Room> findByOwnerId(String ownerId, int start, int limit); List<Room> findByOwnerId(String ownerId, int start, int limit);
List<String> findIdsByOwnerId(String ownerId); List<String> findIdsByOwnerId(String ownerId);
List<String> findIdsByModeratorId(String moderatorId);
List<Room> findAllForPublicPool(); List<Room> findAllForPublicPool();
List<Room> findForPublicPoolByOwnerId(String ownerId); List<Room> findForPublicPoolByOwnerId(String ownerId);
List<Room> getRoomsWithStatsForOwnerId(String ownerId, int start, int limit); List<Room> getRoomsWithStatsForOwnerId(String ownerId, int start, int limit);
......
...@@ -296,6 +296,15 @@ public class CouchDbRoomRepository extends CouchDbCrudRepository<Room> implement ...@@ -296,6 +296,15 @@ public class CouchDbRoomRepository extends CouchDbCrudRepository<Room> implement
return result.getRows().stream().map(ViewResult.Row::getId).collect(Collectors.toList()); return result.getRows().stream().map(ViewResult.Row::getId).collect(Collectors.toList());
} }
@Override
public List<String> findIdsByModeratorId(final String moderatorId) {
ViewResult result = db.queryView(createQuery("by_moderators_containing_userid")
.key(moderatorId)
.includeDocs(false));
return result.getRows().stream().map(ViewResult.Row::getId).collect(Collectors.toList());
}
@Override @Override
public List<Room> findAllForPublicPool() { public List<Room> findAllForPublicPool() {
// TODO replace with new view // TODO replace with new view
......
...@@ -161,11 +161,13 @@ public class ApplicationPermissionEvaluator implements PermissionEvaluator { ...@@ -161,11 +161,13 @@ public class ApplicationPermissionEvaluator implements PermissionEvaluator {
final String permission) { final String permission) {
switch (permission) { switch (permission) {
case "read": case "read":
return !targetRoom.isClosed(); return !targetRoom.isClosed() || hasUserIdRoomModeratingPermission(targetRoom, userId);
case "create": case "create":
return !userId.isEmpty(); return !userId.isEmpty();
case "owner":
case "update": case "update":
return targetRoom.getOwnerId().equals(userId)
|| hasUserIdRoomModeratorRole(targetRoom, userId, Room.Moderator.Role.EDITING_MODERATOR);
case "owner":
case "delete": case "delete":
return targetRoom.getOwnerId().equals(userId); return targetRoom.getOwnerId().equals(userId);
default: default:
...@@ -177,15 +179,21 @@ public class ApplicationPermissionEvaluator implements PermissionEvaluator { ...@@ -177,15 +179,21 @@ public class ApplicationPermissionEvaluator implements PermissionEvaluator {
final String userId, final String userId,
final Content targetContent, final Content targetContent,
final String permission) { final String permission) {
final Room room = roomRepository.findOne(targetContent.getRoomId());
if (room == null) {
return false;
}
switch (permission) { switch (permission) {
case "read": case "read":
return !roomRepository.findOne(targetContent.getRoomId()).isClosed(); return !room.isClosed() || hasUserIdRoomModeratingPermission(room, userId);
case "create": case "create":
case "owner":
case "update": case "update":
case "delete": case "delete":
final Room room = roomRepository.findOne(targetContent.getRoomId()); case "owner":
return room != null && room.getOwnerId().equals(userId); /* TODO: Remove owner permission for content. Use create/update/delete instead. */
return room.getOwnerId().equals(userId)
|| hasUserIdRoomModeratorRole(room, userId, Room.Moderator.Role.EDITING_MODERATOR);
default: default:
return false; return false;
} }
...@@ -206,7 +214,7 @@ public class ApplicationPermissionEvaluator implements PermissionEvaluator { ...@@ -206,7 +214,7 @@ public class ApplicationPermissionEvaluator implements PermissionEvaluator {
return true; return true;
} }
room = roomRepository.findOne(targetAnswer.getRoomId()); room = roomRepository.findOne(targetAnswer.getRoomId());
return room != null && hasRoomPermission(userId, room, "owner"); return room != null && hasUserIdRoomModeratingPermission(room, userId);
case "create": case "create":
return content.getState().isResponsesEnabled(); return content.getState().isResponsesEnabled();
case "owner": case "owner":
...@@ -216,7 +224,7 @@ public class ApplicationPermissionEvaluator implements PermissionEvaluator { ...@@ -216,7 +224,7 @@ public class ApplicationPermissionEvaluator implements PermissionEvaluator {
return false; return false;
case "delete": case "delete":
room = roomRepository.findOne(targetAnswer.getRoomId()); room = roomRepository.findOne(targetAnswer.getRoomId());
return room != null && hasRoomPermission(userId, room, "owner"); return room != null && hasUserIdRoomModeratingPermission(room, userId);
default: default:
return false; return false;
} }
...@@ -241,7 +249,7 @@ public class ApplicationPermissionEvaluator implements PermissionEvaluator { ...@@ -241,7 +249,7 @@ public class ApplicationPermissionEvaluator implements PermissionEvaluator {
/* Allow reading & deletion by session owner */ /* Allow reading & deletion by session owner */
final Room room = roomRepository.findOne(targetComment.getRoomId()); final Room room = roomRepository.findOne(targetComment.getRoomId());
return room != null && room.getOwnerId().equals(userId); return room != null && hasUserIdRoomModeratingPermission(room, userId);
default: default:
return false; return false;
} }
...@@ -254,7 +262,6 @@ public class ApplicationPermissionEvaluator implements PermissionEvaluator { ...@@ -254,7 +262,6 @@ public class ApplicationPermissionEvaluator implements PermissionEvaluator {
Room room; Room room;
switch (permission) { switch (permission) {
case "create": case "create":
case "owner":
case "update": case "update":
case "delete": case "delete":
if (userId.isEmpty() || targetMotd.getRoomId() == null || targetMotd.getAudience() != Motd.Audience.ROOM) { if (userId.isEmpty() || targetMotd.getRoomId() == null || targetMotd.getAudience() != Motd.Audience.ROOM) {
...@@ -265,19 +272,42 @@ public class ApplicationPermissionEvaluator implements PermissionEvaluator { ...@@ -265,19 +272,42 @@ public class ApplicationPermissionEvaluator implements PermissionEvaluator {
return false; return false;
} }
return userId.equals(room.getOwnerId()); return userId.equals(room.getOwnerId())
|| hasUserIdRoomModeratorRole(room, userId, Room.Moderator.Role.EDITING_MODERATOR);
case "read": case "read":
if (targetMotd.getAudience() != Motd.Audience.ROOM) { if (targetMotd.getAudience() != Motd.Audience.ROOM) {
return true; return true;
} }
room = roomRepository.findOne(targetMotd.getRoomId()); room = roomRepository.findOne(targetMotd.getRoomId());
return room != null && (!room.isClosed() || room.getOwnerId().equals(userId)); return room != null && (!room.isClosed() || hasUserIdRoomModeratingPermission(room, userId));
default: default:
return false; return false;
} }
} }
/**
* Checks if the user is owner or has any moderating role for the room.
*/
private boolean hasUserIdRoomModeratingPermission(final Room room, final String userId) {
return room.getOwnerId().equals(userId) || room.getModerators().stream()
.anyMatch(m -> m.getUserId().equals(userId));
}
/**
* Checks if the user has a specific moderating role for the room.
*
* @param room The room to check the role for.
* @param userId The ID of the user to check the role for.
* @param role The role that is checked.
* @return Returns true if the user has the moderator role for the room.
*/
private boolean hasUserIdRoomModeratorRole(final Room room, final String userId, Room.Moderator.Role role) {
return room.getModerators().stream()
.filter(m -> m.getUserId().equals(userId))
.anyMatch(m -> m.getRoles().contains(role));
}
private boolean hasAdminRole(final String username) { private boolean hasAdminRole(final String username) {
/* TODO: only allow accounts from arsnova db */ /* TODO: only allow accounts from arsnova db */
return Arrays.asList(adminAccounts).contains(username); return Arrays.asList(adminAccounts).contains(username);
......
...@@ -46,6 +46,11 @@ public class RoomFindQueryService implements FindQueryService<Room> { ...@@ -46,6 +46,11 @@ public class RoomFindQueryService implements FindQueryService<Room> {
ids.add(inHistoryOfUser.getRoomHistory().stream() ids.add(inHistoryOfUser.getRoomHistory().stream()
.map(UserProfile.RoomHistoryEntry::getRoomId).collect(Collectors.toList())); .map(UserProfile.RoomHistoryEntry::getRoomId).collect(Collectors.toList()));
} }
if (findQuery.getExternalFilters().get("moderatedByUserId") instanceof String) {
List<String> moderatedRoomIds = roomService.getRoomIdsByModeratorId(
(String) findQuery.getExternalFilters().get("moderatedByUserId"));
ids.add(moderatedRoomIds);
}
if (findQuery.getProperties().getOwnerId() != null) { if (findQuery.getProperties().getOwnerId() != null) {
ids.add(roomService.getUserRoomIds(findQuery.getProperties().getOwnerId())); ids.add(roomService.getUserRoomIds(findQuery.getProperties().getOwnerId()));
} }
......
...@@ -46,6 +46,8 @@ public interface RoomService extends EntityService<Room> { ...@@ -46,6 +46,8 @@ public interface RoomService extends EntityService<Room> {
List<String> getUserRoomIds(String userId); List<String> getUserRoomIds(String userId);
List<String> getRoomIdsByModeratorId(String userId);
List<Room> getUserRoomHistory(String userId); List<Room> getUserRoomHistory(String userId);
List<Room> getMyRooms(int offset, int limit); List<Room> getMyRooms(int offset, int limit);
......
...@@ -278,6 +278,12 @@ public class RoomServiceImpl extends DefaultEntityServiceImpl<Room> implements R ...@@ -278,6 +278,12 @@ public class RoomServiceImpl extends DefaultEntityServiceImpl<Room> implements R
return roomRepository.findIdsByOwnerId(userId); return roomRepository.findIdsByOwnerId(userId);
} }
@Override
@PreAuthorize("isAuthenticated() and hasPermission(#userId, 'userprofile', 'owner')")
public List<String> getRoomIdsByModeratorId(final String userId) {
return roomRepository.findIdsByModeratorId(userId);
}
@Override @Override
@PreAuthorize("isAuthenticated()") @PreAuthorize("isAuthenticated()")
public List<Room> getMyRooms(final int offset, final int limit) { public List<Room> getMyRooms(final int offset, final int limit) {
......
...@@ -31,6 +31,15 @@ var designDoc = { ...@@ -31,6 +31,15 @@ var designDoc = {
} }
} }
}, },
"by_moderators_containing_userid": {
"map": function (doc) {
if (doc.type === "Room" && doc.moderators) {
doc.moderators.forEach(function (moderator) {
emit(moderator.userId, {_rev: doc._rev});
});
}
}
},
"by_lastactivity_for_guests": { /* needs rewrite */ "by_lastactivity_for_guests": { /* needs rewrite */
"map": function (doc) { "map": function (doc) {
if (doc.type === "Room" && !doc.poolProperties && doc.creator.indexOf("Guest") === 0) { if (doc.type === "Room" && !doc.poolProperties && doc.creator.indexOf("Guest") === 0) {
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment