blob: fa2a9419adce5a0686ea758dae49fbbdea43832b [file] [log] [blame]
margaretha0b63de42017-12-20 18:48:09 +01001package de.ids_mannheim.korap.service;
2
margarethaed053fb2019-04-11 15:15:13 +02003import java.sql.SQLException;
margarethae6c711b2018-02-06 21:55:04 +01004import java.time.ZonedDateTime;
margaretha0b63de42017-12-20 18:48:09 +01005import java.util.ArrayList;
margarethab874ef52018-01-23 20:26:31 +01006import java.util.Collections;
margaretha18533fd2018-03-28 16:01:06 +02007import java.util.HashSet;
8import java.util.Iterator;
margaretha0b63de42017-12-20 18:48:09 +01009import java.util.List;
margaretha18533fd2018-03-28 16:01:06 +020010import java.util.Set;
margarethada5a6ab2019-11-08 10:06:05 +010011import java.util.regex.Pattern;
margaretha0b63de42017-12-20 18:48:09 +010012
margaretha49cb6882018-07-04 04:19:54 +020013import org.apache.logging.log4j.LogManager;
14import org.apache.logging.log4j.Logger;
margaretha0b63de42017-12-20 18:48:09 +010015import org.springframework.beans.factory.annotation.Autowired;
16import org.springframework.stereotype.Service;
17
margaretha2c019fa2018-02-01 19:50:51 +010018import de.ids_mannheim.korap.config.FullConfiguration;
margaretha9d3eb042017-12-22 11:02:30 +010019import de.ids_mannheim.korap.constant.GroupMemberStatus;
margaretha0b63de42017-12-20 18:48:09 +010020import de.ids_mannheim.korap.constant.PredefinedRole;
margaretha22d3a7c2024-07-17 12:57:09 +020021import de.ids_mannheim.korap.constant.PrivilegeType;
margaretha9d3eb042017-12-22 11:02:30 +010022import de.ids_mannheim.korap.constant.UserGroupStatus;
margaretha4edc70e2018-03-14 22:34:29 +010023import de.ids_mannheim.korap.dao.AdminDao;
margaretha9d3eb042017-12-22 11:02:30 +010024import de.ids_mannheim.korap.dao.RoleDao;
margaretha0b63de42017-12-20 18:48:09 +010025import de.ids_mannheim.korap.dao.UserGroupDao;
26import de.ids_mannheim.korap.dao.UserGroupMemberDao;
27import de.ids_mannheim.korap.dto.UserGroupDto;
28import de.ids_mannheim.korap.dto.converter.UserGroupConverter;
margaretha3ccaeb72019-02-28 18:40:22 +010029import de.ids_mannheim.korap.encryption.RandomCodeGenerator;
margaretha9d3eb042017-12-22 11:02:30 +010030import de.ids_mannheim.korap.entity.Role;
margaretha0b63de42017-12-20 18:48:09 +010031import de.ids_mannheim.korap.entity.UserGroup;
32import de.ids_mannheim.korap.entity.UserGroupMember;
33import de.ids_mannheim.korap.exceptions.KustvaktException;
margarethab874ef52018-01-23 20:26:31 +010034import de.ids_mannheim.korap.exceptions.StatusCodes;
margarethab874ef52018-01-23 20:26:31 +010035import de.ids_mannheim.korap.utils.ParameterChecker;
margarethaa0d4d3c2018-01-02 12:06:11 +010036import de.ids_mannheim.korap.web.controller.UserGroupController;
margaretha0b63de42017-12-20 18:48:09 +010037
margarethaf7abb362018-09-18 20:09:37 +020038/**
39 * UserGroupService defines the logic behind user group web
40 * controller.
margarethaa0d4d3c2018-01-02 12:06:11 +010041 *
42 * @see UserGroupController
43 *
44 * @author margaretha
45 *
46 */
margaretha0b63de42017-12-20 18:48:09 +010047@Service
48public class UserGroupService {
49
margarethadda4ef72018-12-06 14:20:51 +010050 public static Logger jlog = LogManager.getLogger(UserGroupService.class);
51 public static boolean DEBUG = false;
margaretha35e1ca22023-11-16 22:00:01 +010052
margaretha9645ab02022-03-31 11:33:03 +020053 public static Pattern groupNamePattern = Pattern
54 .compile("[a-zA-Z0-9]+[a-zA-Z_0-9-.]+");
margaretha35e1ca22023-11-16 22:00:01 +010055
margaretha0b63de42017-12-20 18:48:09 +010056 @Autowired
57 private UserGroupDao userGroupDao;
58 @Autowired
59 private UserGroupMemberDao groupMemberDao;
60 @Autowired
margaretha9d3eb042017-12-22 11:02:30 +010061 private RoleDao roleDao;
62 @Autowired
margaretha4edc70e2018-03-14 22:34:29 +010063 private AdminDao adminDao;
margarethab874ef52018-01-23 20:26:31 +010064 @Autowired
margaretha4edc70e2018-03-14 22:34:29 +010065 private UserGroupConverter converter;
margaretha2c019fa2018-02-01 19:50:51 +010066 @Autowired
67 private FullConfiguration config;
margaretha58e18632018-02-15 13:04:42 +010068 @Autowired
69 private MailService mailService;
margaretha3ccaeb72019-02-28 18:40:22 +010070 @Autowired
71 private RandomCodeGenerator random;
margaretha35e1ca22023-11-16 22:00:01 +010072
margarethaf7abb362018-09-18 20:09:37 +020073 /**
margaretha777ef102024-07-22 10:10:50 +020074 * Only users with {@link PredefinedRole#GROUP_ADMIN}
margarethaa0d4d3c2018-01-02 12:06:11 +010075 * are allowed to see the members of the group.
margaretha9d3eb042017-12-22 11:02:30 +010076 *
margarethaf7abb362018-09-18 20:09:37 +020077 * @param username
78 * username
margaretha9d3eb042017-12-22 11:02:30 +010079 * @return a list of usergroups
80 * @throws KustvaktException
margarethaa0d4d3c2018-01-02 12:06:11 +010081 *
82 * @see {@link PredefinedRole}
margaretha9d3eb042017-12-22 11:02:30 +010083 */
margaretha3ccaeb72019-02-28 18:40:22 +010084 public List<UserGroup> retrieveUserGroup (String username)
margaretha293ee032018-03-20 20:11:52 +010085 throws KustvaktException {
margaretha0b63de42017-12-20 18:48:09 +010086
margaretha35e1ca22023-11-16 22:00:01 +010087 List<UserGroup> userGroups = userGroupDao
88 .retrieveGroupByUserId(username);
margarethab874ef52018-01-23 20:26:31 +010089 Collections.sort(userGroups);
margaretha3ccaeb72019-02-28 18:40:22 +010090 return userGroups;
91 }
margaretha35e1ca22023-11-16 22:00:01 +010092
margaretha3ccaeb72019-02-28 18:40:22 +010093 public List<UserGroupDto> retrieveUserGroupDto (String username)
94 throws KustvaktException {
95 List<UserGroup> userGroups = retrieveUserGroup(username);
margaretha35e1ca22023-11-16 22:00:01 +010096
margaretha0b63de42017-12-20 18:48:09 +010097 ArrayList<UserGroupDto> dtos = new ArrayList<>(userGroups.size());
margaretha45667922018-01-25 21:23:03 +010098 UserGroupMember userAsMember;
99 List<UserGroupMember> members;
margaretha6f288372018-03-08 18:35:23 +0100100 UserGroupDto groupDto;
margaretha0b63de42017-12-20 18:48:09 +0100101 for (UserGroup group : userGroups) {
margaretha45667922018-01-25 21:23:03 +0100102 members = retrieveMembers(group.getId(), username);
margaretha35e1ca22023-11-16 22:00:01 +0100103 userAsMember = groupMemberDao.retrieveMemberById(username,
104 group.getId());
margaretha6f288372018-03-08 18:35:23 +0100105 groupDto = converter.createUserGroupDto(group, members,
106 userAsMember.getStatus(), userAsMember.getRoles());
107 dtos.add(groupDto);
margaretha0b63de42017-12-20 18:48:09 +0100108 }
109
110 return dtos;
margaretha3ccaeb72019-02-28 18:40:22 +0100111
margaretha0b63de42017-12-20 18:48:09 +0100112 }
margarethacfea1ae2018-01-15 20:27:26 +0100113
margaretha45667922018-01-25 21:23:03 +0100114 private List<UserGroupMember> retrieveMembers (int groupId, String username)
115 throws KustvaktException {
116 List<UserGroupMember> groupAdmins = groupMemberDao.retrieveMemberByRole(
margaretha777ef102024-07-22 10:10:50 +0200117 groupId, PredefinedRole.GROUP_ADMIN);
margaretha45667922018-01-25 21:23:03 +0100118
119 List<UserGroupMember> members = null;
120 for (UserGroupMember admin : groupAdmins) {
121 if (admin.getUserId().equals(username)) {
122 members = groupMemberDao.retrieveMemberByGroupId(groupId);
123 break;
124 }
125 }
margaretha2c019fa2018-02-01 19:50:51 +0100126
margaretha45667922018-01-25 21:23:03 +0100127 return members;
128 }
129
margaretha541b8cc2018-01-10 13:02:46 +0100130 public UserGroup retrieveUserGroupById (int groupId)
131 throws KustvaktException {
132 return userGroupDao.retrieveGroupById(groupId);
133 }
margaretha35e1ca22023-11-16 22:00:01 +0100134
margaretha3ccaeb72019-02-28 18:40:22 +0100135 public UserGroup retrieveUserGroupByName (String groupName)
136 throws KustvaktException {
margarethaa18ab2b2019-11-11 12:55:26 +0100137 return userGroupDao.retrieveGroupByName(groupName, false);
margaretha3ccaeb72019-02-28 18:40:22 +0100138 }
margarethacfea1ae2018-01-15 20:27:26 +0100139
margarethab097fb02021-02-22 19:28:33 +0100140 public UserGroup retrieveHiddenUserGroupByQuery (int queryId)
margaretha293ee032018-03-20 20:11:52 +0100141 throws KustvaktException {
margarethab097fb02021-02-22 19:28:33 +0100142 return userGroupDao.retrieveHiddenGroupByQuery(queryId);
margaretha45667922018-01-25 21:23:03 +0100143 }
144
margaretha293ee032018-03-20 20:11:52 +0100145 public List<UserGroupDto> retrieveUserGroupByStatus (String username,
margarethabdde7f42023-02-10 08:24:03 +0100146 UserGroupStatus status) throws KustvaktException {
margaretha293ee032018-03-20 20:11:52 +0100147
margaretha35e1ca22023-11-16 22:00:01 +0100148 List<UserGroup> userGroups = userGroupDao
149 .retrieveGroupByStatus(username, status);
margarethabdde7f42023-02-10 08:24:03 +0100150 Collections.sort(userGroups);
151 ArrayList<UserGroupDto> dtos = new ArrayList<>(userGroups.size());
margaretha293ee032018-03-20 20:11:52 +0100152
margarethabdde7f42023-02-10 08:24:03 +0100153 List<UserGroupMember> members;
154 UserGroupDto groupDto;
155 for (UserGroup group : userGroups) {
margaretha35e1ca22023-11-16 22:00:01 +0100156 members = groupMemberDao.retrieveMemberByGroupId(group.getId(),
157 true);
margarethabdde7f42023-02-10 08:24:03 +0100158 groupDto = converter.createUserGroupDto(group, members, null, null);
159 dtos.add(groupDto);
margaretha293ee032018-03-20 20:11:52 +0100160 }
margarethabdde7f42023-02-10 08:24:03 +0100161 return dtos;
margaretha293ee032018-03-20 20:11:52 +0100162 }
163
margarethab097fb02021-02-22 19:28:33 +0100164 public List<UserGroupMember> retrieveQueryAccessAdmins (UserGroup userGroup)
margarethacfea1ae2018-01-15 20:27:26 +0100165 throws KustvaktException {
166 List<UserGroupMember> groupAdmins = groupMemberDao.retrieveMemberByRole(
margaretha777ef102024-07-22 10:10:50 +0200167 userGroup.getId(), PredefinedRole.QUERY_ACCESS_ADMIN);
margarethacfea1ae2018-01-15 20:27:26 +0100168 return groupAdmins;
169 }
margaretha71e6fca2018-01-18 18:11:48 +0100170
margaretha26c592e2024-07-18 11:19:00 +0200171 private Set<Role> prepareMemberRoles (UserGroup userGroup) {
margaretha777ef102024-07-22 10:10:50 +0200172 Role r1 = new Role(PredefinedRole.GROUP_MEMBER,
173 PrivilegeType.DELETE_MEMBER, userGroup);
margaretha22d3a7c2024-07-17 12:57:09 +0200174 roleDao.addRole(r1);
margaretha777ef102024-07-22 10:10:50 +0200175 Set<Role>memberRoles = new HashSet<Role>(1);
margaretha22d3a7c2024-07-17 12:57:09 +0200176 memberRoles.add(r1);
margaretha26c592e2024-07-18 11:19:00 +0200177 return memberRoles;
margaretha45667922018-01-25 21:23:03 +0100178 }
margarethacfea1ae2018-01-15 20:27:26 +0100179
margarethaf7abb362018-09-18 20:09:37 +0200180 /**
181 * Group owner is automatically added when creating a group.
182 * Do not include owners in group members.
183 *
margaretha777ef102024-07-22 10:10:50 +0200184 * {@link PredefinedRole#GROUP_MEMBER} and
margaretha4a1a18c2021-02-26 10:19:54 +0100185 * {@link PredefinedRole#VC_ACCESS_MEMBER} roles are
margarethaf7abb362018-09-18 20:09:37 +0200186 * automatically assigned to each group member.
187 *
margaretha777ef102024-07-22 10:10:50 +0200188 * {@link PredefinedRole#GROUP_MEMBER} restrict users
margarethaf7abb362018-09-18 20:09:37 +0200189 * to see other group members and allow users to remove
190 * themselves from the groups.
191 *
margaretha4a1a18c2021-02-26 10:19:54 +0100192 * {@link PredefinedRole#VC_ACCESS_MEMBER} allow user to
margarethab097fb02021-02-22 19:28:33 +0100193 * read group query.
margaretha9d3eb042017-12-22 11:02:30 +0100194 *
195 * @see /full/src/main/resources/db/predefined/V3.2__insert_predefined_roles.sql
196 *
margarethaf7abb362018-09-18 20:09:37 +0200197 * @param createdBy
198 * the user creating the group
margaretha9d3eb042017-12-22 11:02:30 +0100199 * @throws KustvaktException
200 *
201 *
202 */
margaretha35e1ca22023-11-16 22:00:01 +0100203 public boolean createUpdateUserGroup (String groupName, String description,
204 String createdBy) throws KustvaktException {
margaretha21d32962019-11-14 17:08:15 +0100205 ParameterChecker.checkNameValue(groupName, "groupName");
margarethaca7cff82019-11-12 12:06:37 +0100206 ParameterChecker.checkStringValue(createdBy, "createdBy");
margarethad8aa1352019-12-19 11:04:41 +0100207
margarethaca7cff82019-11-12 12:06:37 +0100208 if (!groupNamePattern.matcher(groupName).matches()) {
margarethada5a6ab2019-11-08 10:06:05 +0100209 throw new KustvaktException(StatusCodes.INVALID_ARGUMENT,
margaretha9645ab02022-03-31 11:33:03 +0200210 "User-group name must consists of alphanumerical characters "
211 + "(limited to ASCII), underscores, dashes and periods. "
212 + "The name has to start with an alphanumerical character.",
margarethaca7cff82019-11-12 12:06:37 +0100213 groupName);
margarethada5a6ab2019-11-08 10:06:05 +0100214 }
margaretha35e1ca22023-11-16 22:00:01 +0100215
margarethaca7cff82019-11-12 12:06:37 +0100216 UserGroup userGroup = null;
217 boolean groupExists = false;
margaretha35e1ca22023-11-16 22:00:01 +0100218 try {
219 userGroup = userGroupDao.retrieveGroupByName(groupName, false);
margarethaca7cff82019-11-12 12:06:37 +0100220 groupExists = true;
margarethada5a6ab2019-11-08 10:06:05 +0100221 }
222 catch (KustvaktException e) {
margaretha35e1ca22023-11-16 22:00:01 +0100223 if (e.getStatusCode() != StatusCodes.NO_RESOURCE_FOUND) {
margarethada5a6ab2019-11-08 10:06:05 +0100224 throw e;
225 }
226 }
margaretha35e1ca22023-11-16 22:00:01 +0100227
228 if (!groupExists) {
margarethaca7cff82019-11-12 12:06:37 +0100229 try {
margarethad8aa1352019-12-19 11:04:41 +0100230 userGroupDao.createGroup(groupName, description, createdBy,
margarethaca7cff82019-11-12 12:06:37 +0100231 UserGroupStatus.ACTIVE);
margaretha35e1ca22023-11-16 22:00:01 +0100232 userGroup = userGroupDao.retrieveGroupByName(groupName, false);
margarethaed053fb2019-04-11 15:15:13 +0200233 }
margarethaca7cff82019-11-12 12:06:37 +0100234 // handle DB exceptions, e.g. unique constraint
235 catch (Exception e) {
236 Throwable cause = e;
237 Throwable lastCause = null;
238 while ((cause = cause.getCause()) != null
239 && !cause.equals(lastCause)) {
240 if (cause instanceof SQLException) {
241 break;
242 }
243 lastCause = cause;
margaretha39cec602019-02-05 19:48:49 +0100244 }
margarethaca7cff82019-11-12 12:06:37 +0100245 throw new KustvaktException(StatusCodes.DB_INSERT_FAILED,
246 cause.getMessage());
247 }
margarethad8aa1352019-12-19 11:04:41 +0100248 }
249 else if (description != null) {
250 userGroup.setDescription(description);
251 userGroupDao.updateGroup(userGroup);
margarethab874ef52018-01-23 20:26:31 +0100252 }
margarethaca7cff82019-11-12 12:06:37 +0100253 return groupExists;
margarethab874ef52018-01-23 20:26:31 +0100254 }
255
margaretha39cec602019-02-05 19:48:49 +0100256 public void deleteGroup (String groupName, String username)
257 throws KustvaktException {
margaretha35e1ca22023-11-16 22:00:01 +0100258 UserGroup userGroup = userGroupDao.retrieveGroupByName(groupName,
259 false);
margaretha39cec602019-02-05 19:48:49 +0100260 if (userGroup.getStatus() == UserGroupStatus.DELETED) {
261 // EM: should this be "not found" instead?
262 throw new KustvaktException(StatusCodes.GROUP_DELETED,
263 "Group " + userGroup.getName() + " has been deleted.",
264 userGroup.getName());
265 }
266 else if (userGroup.getCreatedBy().equals(username)
267 || adminDao.isAdmin(username)) {
268 // soft delete
269 userGroupDao.deleteGroup(userGroup.getId(), username,
270 config.isSoftDeleteGroup());
271 }
272 else {
273 throw new KustvaktException(StatusCodes.AUTHORIZATION_FAILED,
274 "Unauthorized operation for user: " + username, username);
275 }
276 }
margaretha9d3eb042017-12-22 11:02:30 +0100277
margarethaa18ab2b2019-11-11 12:55:26 +0100278 public int createAutoHiddenGroup () throws KustvaktException {
margaretha3ccaeb72019-02-28 18:40:22 +0100279 String code = random.createRandomCode();
margaretha35e1ca22023-11-16 22:00:01 +0100280 String groupName = "auto-" + code;
margarethad8aa1352019-12-19 11:04:41 +0100281 int groupId = userGroupDao.createGroup(groupName, "auto-hidden-group",
282 "system", UserGroupStatus.HIDDEN);
margaretha45dde682018-01-04 21:33:46 +0100283
284 return groupId;
285 }
margaretha71e6fca2018-01-18 18:11:48 +0100286
margarethab874ef52018-01-23 20:26:31 +0100287 public void deleteAutoHiddenGroup (int groupId, String deletedBy)
288 throws KustvaktException {
289 // default hard delete
margarethae6c711b2018-02-06 21:55:04 +0100290 userGroupDao.deleteGroup(groupId, deletedBy,
291 config.isSoftDeleteAutoGroup());
margarethab874ef52018-01-23 20:26:31 +0100292 }
293
margarethaf7abb362018-09-18 20:09:37 +0200294 /**
295 * Adds a user to the specified usergroup. If the username with
296 * {@link GroupMemberStatus} DELETED exists as a member of the
297 * group,
298 * the entry will be deleted first, and a new entry will be added.
299 *
300 * If a username with other statuses exists, a KustvaktException
301 * will
302 * be thrown.
margarethab874ef52018-01-23 20:26:31 +0100303 *
304 * @see GroupMemberStatus
305 *
margarethaf7abb362018-09-18 20:09:37 +0200306 * @param username
307 * a username
308 * @param userGroup
309 * a user group
310 * @param createdBy
margaretha35e1ca22023-11-16 22:00:01 +0100311 * the user (query-access admin/system) adding the user
312 * the user-group
margarethaf7abb362018-09-18 20:09:37 +0200313 * @param status
314 * the status of the membership
margarethab874ef52018-01-23 20:26:31 +0100315 * @throws KustvaktException
316 */
margaretha58e18632018-02-15 13:04:42 +0100317 public void inviteGroupMember (String username, UserGroup userGroup,
margarethab874ef52018-01-23 20:26:31 +0100318 String createdBy, GroupMemberStatus status)
margarethae8ab51d2018-01-16 19:27:40 +0100319 throws KustvaktException {
margaretha71e6fca2018-01-18 18:11:48 +0100320
margaretha18533fd2018-03-28 16:01:06 +0200321 addGroupMember(username, userGroup, createdBy, status);
322
323 if (config.isMailEnabled()
324 && userGroup.getStatus() != UserGroupStatus.HIDDEN) {
325 mailService.sendMemberInvitationNotification(username,
326 userGroup.getName(), createdBy);
327 }
328 }
329
330 public void addGroupMember (String username, UserGroup userGroup,
331 String createdBy, GroupMemberStatus status)
332 throws KustvaktException {
margarethab874ef52018-01-23 20:26:31 +0100333 int groupId = userGroup.getId();
334 ParameterChecker.checkIntegerValue(groupId, "userGroupId");
335
margaretha35e1ca22023-11-16 22:00:01 +0100336 GroupMemberStatus existingStatus = memberExists(username, groupId,
337 status);
margaretha97bb3bd2018-03-14 18:41:14 +0100338 if (existingStatus != null) {
margarethae6c711b2018-02-06 21:55:04 +0100339 throw new KustvaktException(StatusCodes.GROUP_MEMBER_EXISTS,
margarethac1c3f1d2018-03-13 14:39:32 +0100340 "Username " + username + " with status " + existingStatus
margarethae6c711b2018-02-06 21:55:04 +0100341 + " exists in the user-group "
342 + userGroup.getName(),
margarethac1c3f1d2018-03-13 14:39:32 +0100343 username, existingStatus.name(), userGroup.getName());
margarethab874ef52018-01-23 20:26:31 +0100344 }
345
margarethae8ab51d2018-01-16 19:27:40 +0100346 UserGroupMember member = new UserGroupMember();
margarethab874ef52018-01-23 20:26:31 +0100347 member.setCreatedBy(createdBy);
margarethae8ab51d2018-01-16 19:27:40 +0100348 member.setGroup(userGroup);
margarethab874ef52018-01-23 20:26:31 +0100349 member.setStatus(status);
margarethae8ab51d2018-01-16 19:27:40 +0100350 member.setUserId(username);
margarethae8ab51d2018-01-16 19:27:40 +0100351 groupMemberDao.addMember(member);
352 }
margaretha45dde682018-01-04 21:33:46 +0100353
margarethac1c3f1d2018-03-13 14:39:32 +0100354 private GroupMemberStatus memberExists (String username, int groupId,
margarethab874ef52018-01-23 20:26:31 +0100355 GroupMemberStatus status) throws KustvaktException {
356 UserGroupMember existingMember;
357 try {
margaretha35e1ca22023-11-16 22:00:01 +0100358 existingMember = groupMemberDao.retrieveMemberById(username,
359 groupId);
margarethab874ef52018-01-23 20:26:31 +0100360 }
361 catch (KustvaktException e) {
margarethac1c3f1d2018-03-13 14:39:32 +0100362 return null;
margarethab874ef52018-01-23 20:26:31 +0100363 }
364
margaretha45667922018-01-25 21:23:03 +0100365 GroupMemberStatus existingStatus = existingMember.getStatus();
366 if (existingStatus.equals(GroupMemberStatus.ACTIVE)
367 || existingStatus.equals(status)) {
margarethac1c3f1d2018-03-13 14:39:32 +0100368 return existingStatus;
margarethab874ef52018-01-23 20:26:31 +0100369 }
margaretha45667922018-01-25 21:23:03 +0100370 else if (existingStatus.equals(GroupMemberStatus.DELETED)) {
margaretha2c019fa2018-02-01 19:50:51 +0100371 // hard delete, not customizable
margaretha18533fd2018-03-28 16:01:06 +0200372 doDeleteMember(username, groupId, "system", false);
margarethab874ef52018-01-23 20:26:31 +0100373 }
374
margarethac1c3f1d2018-03-13 14:39:32 +0100375 return null;
margarethab874ef52018-01-23 20:26:31 +0100376 }
377
margarethaca7cff82019-11-12 12:06:37 +0100378 public void inviteGroupMembers (String groupName, String groupMembers,
margarethaa18ab2b2019-11-11 12:55:26 +0100379 String inviter) throws KustvaktException {
margarethaca7cff82019-11-12 12:06:37 +0100380 String[] members = groupMembers.split(",");
margarethaa18ab2b2019-11-11 12:55:26 +0100381 ParameterChecker.checkStringValue(groupName, "group name");
margarethaca7cff82019-11-12 12:06:37 +0100382 ParameterChecker.checkStringValue(groupMembers, "members");
margarethab874ef52018-01-23 20:26:31 +0100383
margarethaa18ab2b2019-11-11 12:55:26 +0100384 UserGroup userGroup = retrieveUserGroupByName(groupName);
margarethac1c3f1d2018-03-13 14:39:32 +0100385 if (userGroup.getStatus() == UserGroupStatus.DELETED) {
386 throw new KustvaktException(StatusCodes.GROUP_DELETED,
387 "Group " + userGroup.getName() + " has been deleted.",
388 userGroup.getName());
389 }
390
margaretha4edc70e2018-03-14 22:34:29 +0100391 if (isUserGroupAdmin(inviter, userGroup) || adminDao.isAdmin(inviter)) {
margarethab874ef52018-01-23 20:26:31 +0100392 for (String memberName : members) {
margaretha58e18632018-02-15 13:04:42 +0100393 inviteGroupMember(memberName, userGroup, inviter,
margarethab874ef52018-01-23 20:26:31 +0100394 GroupMemberStatus.PENDING);
395 }
396 }
397 else {
398 throw new KustvaktException(StatusCodes.AUTHORIZATION_FAILED,
margaretha58e18632018-02-15 13:04:42 +0100399 "Unauthorized operation for user: " + inviter, inviter);
margarethab874ef52018-01-23 20:26:31 +0100400 }
401 }
402
margaretha777ef102024-07-22 10:10:50 +0200403 public boolean isUserGroupAdmin (String username, UserGroup userGroup)
margarethab874ef52018-01-23 20:26:31 +0100404 throws KustvaktException {
margaretha18533fd2018-03-28 16:01:06 +0200405
margaretha35e1ca22023-11-16 22:00:01 +0100406 List<UserGroupMember> userGroupAdmins = groupMemberDao
407 .retrieveMemberByRole(userGroup.getId(),
margaretha777ef102024-07-22 10:10:50 +0200408 PredefinedRole.GROUP_ADMIN);
margaretha18533fd2018-03-28 16:01:06 +0200409
margarethab874ef52018-01-23 20:26:31 +0100410 for (UserGroupMember admin : userGroupAdmins) {
411 if (username.equals(admin.getUserId())) {
412 return true;
413 }
414 }
415 return false;
416 }
417
margaretha777ef102024-07-22 10:10:50 +0200418 public boolean isUserGroupAdmin (UserGroupMember member)
419 throws KustvaktException {
420
421 for (Role r : member.getRoles()) {
422 if (r.getName().equals(PredefinedRole.GROUP_ADMIN)) {
423 return true;
424 }
425 }
426 return false;
427 }
428
429
margarethaf7abb362018-09-18 20:09:37 +0200430 /**
431 * Updates the {@link GroupMemberStatus} of a pending member
432 * to {@link GroupMemberStatus#ACTIVE} and add default member
433 * roles.
margarethaa0d4d3c2018-01-02 12:06:11 +0100434 *
margarethaf7abb362018-09-18 20:09:37 +0200435 * @param groupId
436 * groupId
437 * @param username
438 * the username of the group member
margarethaa0d4d3c2018-01-02 12:06:11 +0100439 * @throws KustvaktException
440 */
margarethaa18ab2b2019-11-11 12:55:26 +0100441 public void acceptInvitation (String groupName, String username)
margaretha9d3eb042017-12-22 11:02:30 +0100442 throws KustvaktException {
margarethae6c711b2018-02-06 21:55:04 +0100443
444 ParameterChecker.checkStringValue(username, "userId");
margaretha22d3a7c2024-07-17 12:57:09 +0200445 ParameterChecker.checkStringValue(groupName, "groupName");
margaretha58e18632018-02-15 13:04:42 +0100446
margaretha35e1ca22023-11-16 22:00:01 +0100447 UserGroup userGroup = userGroupDao.retrieveGroupByName(groupName,
448 false);
margaretha97bb3bd2018-03-14 18:41:14 +0100449 if (userGroup.getStatus() == UserGroupStatus.DELETED) {
450 throw new KustvaktException(StatusCodes.GROUP_DELETED,
451 "Group " + userGroup.getName() + " has been deleted.",
452 userGroup.getName());
453 }
margaretha58e18632018-02-15 13:04:42 +0100454
margaretha35e1ca22023-11-16 22:00:01 +0100455 UserGroupMember member = groupMemberDao.retrieveMemberById(username,
456 userGroup.getId());
margarethae6c711b2018-02-06 21:55:04 +0100457 GroupMemberStatus status = member.getStatus();
458 if (status.equals(GroupMemberStatus.DELETED)) {
margarethae6c711b2018-02-06 21:55:04 +0100459 throw new KustvaktException(StatusCodes.GROUP_MEMBER_DELETED,
460 username + " has already been deleted from the group "
margaretha97bb3bd2018-03-14 18:41:14 +0100461 + userGroup.getName(),
462 username, userGroup.getName());
margarethae6c711b2018-02-06 21:55:04 +0100463 }
464 else if (member.getStatus().equals(GroupMemberStatus.ACTIVE)) {
margarethae6c711b2018-02-06 21:55:04 +0100465 throw new KustvaktException(StatusCodes.GROUP_MEMBER_EXISTS,
466 "Username " + username + " with status " + status
margaretha97bb3bd2018-03-14 18:41:14 +0100467 + " exists in the user-group "
468 + userGroup.getName(),
469 username, status.name(), userGroup.getName());
margarethae6c711b2018-02-06 21:55:04 +0100470 }
471 // status pending
472 else {
margarethadda4ef72018-12-06 14:20:51 +0100473 if (DEBUG) {
474 jlog.debug("status: " + member.getStatusDate());
475 }
margarethae6c711b2018-02-06 21:55:04 +0100476 ZonedDateTime expiration = member.getStatusDate().plusMinutes(30);
477 ZonedDateTime now = ZonedDateTime.now();
margarethadda4ef72018-12-06 14:20:51 +0100478 if (DEBUG) {
479 jlog.debug("expiration: " + expiration + ", now: " + now);
480 }
margarethae6c711b2018-02-06 21:55:04 +0100481
margaretha58e18632018-02-15 13:04:42 +0100482 if (expiration.isAfter(now)) {
margarethae6c711b2018-02-06 21:55:04 +0100483 member.setStatus(GroupMemberStatus.ACTIVE);
margaretha26c592e2024-07-18 11:19:00 +0200484 Set<Role> memberRoles = prepareMemberRoles(userGroup);
margaretha18533fd2018-03-28 16:01:06 +0200485 member.setRoles(memberRoles);
margarethae6c711b2018-02-06 21:55:04 +0100486 groupMemberDao.updateMember(member);
487 }
margaretha58e18632018-02-15 13:04:42 +0100488 else {
margarethae6c711b2018-02-06 21:55:04 +0100489 throw new KustvaktException(StatusCodes.INVITATION_EXPIRED);
490 }
491 }
margaretha9d3eb042017-12-22 11:02:30 +0100492 }
493
margaretha2c249912018-01-17 20:07:20 +0100494 public boolean isMember (String username, UserGroup userGroup)
495 throws KustvaktException {
margaretha35e1ca22023-11-16 22:00:01 +0100496 List<UserGroupMember> members = groupMemberDao
497 .retrieveMemberByGroupId(userGroup.getId());
margaretha2c249912018-01-17 20:07:20 +0100498 for (UserGroupMember member : members) {
499 if (member.getUserId().equals(username)
500 && member.getStatus().equals(GroupMemberStatus.ACTIVE)) {
501 return true;
502 }
503 }
504 return false;
505 }
margaretha71e6fca2018-01-18 18:11:48 +0100506
margarethaa18ab2b2019-11-11 12:55:26 +0100507 public void deleteGroupMember (String memberId, String groupName,
margaretha45667922018-01-25 21:23:03 +0100508 String deletedBy) throws KustvaktException {
margaretha4edc70e2018-03-14 22:34:29 +0100509
margaretha35e1ca22023-11-16 22:00:01 +0100510 UserGroup userGroup = userGroupDao.retrieveGroupByName(groupName,
511 false);
margaretha97bb3bd2018-03-14 18:41:14 +0100512 if (userGroup.getStatus() == UserGroupStatus.DELETED) {
513 throw new KustvaktException(StatusCodes.GROUP_DELETED,
514 "Group " + userGroup.getName() + " has been deleted.",
515 userGroup.getName());
516 }
517 else if (memberId.equals(userGroup.getCreatedBy())) {
margaretha45667922018-01-25 21:23:03 +0100518 throw new KustvaktException(StatusCodes.NOT_ALLOWED,
519 "Operation " + "'delete group owner'" + "is not allowed.",
520 "delete group owner");
521 }
margarethae6c711b2018-02-06 21:55:04 +0100522 else if (memberId.equals(deletedBy)
523 || isUserGroupAdmin(deletedBy, userGroup)
margaretha4edc70e2018-03-14 22:34:29 +0100524 || adminDao.isAdmin(deletedBy)) {
margaretha2c019fa2018-02-01 19:50:51 +0100525 // soft delete
margarethaa18ab2b2019-11-11 12:55:26 +0100526 doDeleteMember(memberId, userGroup.getId(), deletedBy,
margaretha2c019fa2018-02-01 19:50:51 +0100527 config.isSoftDeleteGroupMember());
margaretha45667922018-01-25 21:23:03 +0100528 }
529 else {
530 throw new KustvaktException(StatusCodes.AUTHORIZATION_FAILED,
531 "Unauthorized operation for user: " + deletedBy, deletedBy);
532 }
margaretha71e6fca2018-01-18 18:11:48 +0100533 }
margarethab874ef52018-01-23 20:26:31 +0100534
margarethaf7abb362018-09-18 20:09:37 +0200535 /**
536 * Updates the {@link GroupMemberStatus} of a member to
margarethae6c711b2018-02-06 21:55:04 +0100537 * {@link GroupMemberStatus#DELETED}
538 *
margarethaf7abb362018-09-18 20:09:37 +0200539 * @param userId
540 * user to be deleted
541 * @param groupId
542 * user-group id
543 * @param deletedBy
544 * user that issue the delete
545 * @param isSoftDelete
546 * true if database entry is to be deleted
547 * permanently, false otherwise
margarethae6c711b2018-02-06 21:55:04 +0100548 * @throws KustvaktException
549 */
margaretha18533fd2018-03-28 16:01:06 +0200550 private void doDeleteMember (String username, int groupId, String deletedBy,
margarethae6c711b2018-02-06 21:55:04 +0100551 boolean isSoftDelete) throws KustvaktException {
margaretha58e18632018-02-15 13:04:42 +0100552
margarethac9f1dfa2018-02-07 17:50:33 +0100553 UserGroup group = userGroupDao.retrieveGroupById(groupId);
margaretha58e18632018-02-15 13:04:42 +0100554
margaretha35e1ca22023-11-16 22:00:01 +0100555 UserGroupMember member = groupMemberDao.retrieveMemberById(username,
556 groupId);
margarethae6c711b2018-02-06 21:55:04 +0100557 GroupMemberStatus status = member.getStatus();
558 if (isSoftDelete && status.equals(GroupMemberStatus.DELETED)) {
margarethae6c711b2018-02-06 21:55:04 +0100559 throw new KustvaktException(StatusCodes.GROUP_MEMBER_DELETED,
560 username + " has already been deleted from the group "
561 + group.getName(),
562 username, group.getName());
563 }
564
565 groupMemberDao.deleteMember(member, deletedBy, isSoftDelete);
566 }
margaretha293ee032018-03-20 20:11:52 +0100567
margarethab0eca9d2023-02-15 15:03:36 +0100568 public UserGroupDto searchByName (String groupName)
margaretha44573832018-03-21 16:59:59 +0100569 throws KustvaktException {
margarethab0eca9d2023-02-15 15:03:36 +0100570 UserGroup userGroup = userGroupDao.retrieveGroupByName(groupName, true);
571 UserGroupDto groupDto = converter.createUserGroupDto(userGroup,
572 userGroup.getMembers(), null, null);
573 return groupDto;
margaretha44573832018-03-21 16:59:59 +0100574 }
575
margaretha26c592e2024-07-18 11:19:00 +0200576 @Deprecated
margarethaa18ab2b2019-11-11 12:55:26 +0100577 public void editMemberRoles (String username, String groupName,
margaretha99470ce2024-07-17 13:52:37 +0200578 String memberUsername, List<PredefinedRole> roleList)
margarethaf7abb362018-09-18 20:09:37 +0200579 throws KustvaktException {
margaretha293ee032018-03-20 20:11:52 +0100580
margaretha18533fd2018-03-28 16:01:06 +0200581 ParameterChecker.checkStringValue(username, "username");
margarethaa18ab2b2019-11-11 12:55:26 +0100582 ParameterChecker.checkStringValue(groupName, "groupName");
margaretha18533fd2018-03-28 16:01:06 +0200583 ParameterChecker.checkStringValue(memberUsername, "memberUsername");
584
margarethaa18ab2b2019-11-11 12:55:26 +0100585 UserGroup userGroup = userGroupDao.retrieveGroupByName(groupName, true);
margaretha18533fd2018-03-28 16:01:06 +0200586 UserGroupStatus groupStatus = userGroup.getStatus();
margarethaf7abb362018-09-18 20:09:37 +0200587 if (groupStatus == UserGroupStatus.DELETED) {
margaretha18533fd2018-03-28 16:01:06 +0200588 throw new KustvaktException(StatusCodes.GROUP_DELETED,
margarethaf7abb362018-09-18 20:09:37 +0200589 "Usergroup has been deleted.");
590 }
591 else if (isUserGroupAdmin(username, userGroup)
592 || adminDao.isAdmin(username)) {
593
margaretha35e1ca22023-11-16 22:00:01 +0100594 UserGroupMember member = groupMemberDao
595 .retrieveMemberById(memberUsername, userGroup.getId());
margarethaf7abb362018-09-18 20:09:37 +0200596
597 if (!member.getStatus().equals(GroupMemberStatus.ACTIVE)) {
598 throw new KustvaktException(StatusCodes.GROUP_MEMBER_INACTIVE,
599 memberUsername + " has status " + member.getStatus(),
600 memberUsername, member.getStatus().name());
601 }
602
603 Set<Role> roles = new HashSet<>();
margaretha99470ce2024-07-17 13:52:37 +0200604 for (int i = 0; i < roleList.size(); i++) {
605 roles.add(roleDao.retrieveRoleByName(roleList.get(i)));
margarethaf7abb362018-09-18 20:09:37 +0200606 }
607 member.setRoles(roles);
608 groupMemberDao.updateMember(member);
609
610 }
611 else {
612 throw new KustvaktException(StatusCodes.AUTHORIZATION_FAILED,
613 "Unauthorized operation for user: " + username, username);
614 }
615 }
616
margarethaa18ab2b2019-11-11 12:55:26 +0100617 public void addMemberRoles (String username, String groupName,
margaretha22d3a7c2024-07-17 12:57:09 +0200618 String memberUsername, List<PredefinedRole> roleNames)
margarethaf7abb362018-09-18 20:09:37 +0200619 throws KustvaktException {
620
margarethaf7abb362018-09-18 20:09:37 +0200621 ParameterChecker.checkStringValue(username, "username");
margarethaa18ab2b2019-11-11 12:55:26 +0100622 ParameterChecker.checkStringValue(groupName, "groupName");
margarethaf7abb362018-09-18 20:09:37 +0200623 ParameterChecker.checkStringValue(memberUsername, "memberUsername");
624
margarethaa18ab2b2019-11-11 12:55:26 +0100625 UserGroup userGroup = userGroupDao.retrieveGroupByName(groupName, true);
margarethaf7abb362018-09-18 20:09:37 +0200626 UserGroupStatus groupStatus = userGroup.getStatus();
627 if (groupStatus == UserGroupStatus.DELETED) {
628 throw new KustvaktException(StatusCodes.GROUP_DELETED,
629 "Usergroup has been deleted.");
margaretha18533fd2018-03-28 16:01:06 +0200630 }
631 else if (isUserGroupAdmin(username, userGroup)
632 || adminDao.isAdmin(username)) {
633
margaretha35e1ca22023-11-16 22:00:01 +0100634 UserGroupMember member = groupMemberDao
635 .retrieveMemberById(memberUsername, userGroup.getId());
margaretha18533fd2018-03-28 16:01:06 +0200636
637 if (!member.getStatus().equals(GroupMemberStatus.ACTIVE)) {
638 throw new KustvaktException(StatusCodes.GROUP_MEMBER_INACTIVE,
639 memberUsername + " has status " + member.getStatus(),
640 memberUsername, member.getStatus().name());
641 }
642
643 Set<Role> roles = member.getRoles();
margaretha22d3a7c2024-07-17 12:57:09 +0200644 for (PredefinedRole role : roleNames) {
margaretha777ef102024-07-22 10:10:50 +0200645 if (role.equals(PredefinedRole.GROUP_ADMIN)) {
646 Role r1 = new Role(role,PrivilegeType.READ_MEMBER, userGroup);
647 roleDao.addRole(r1);
648 roles.add(r1);
649
650 Role r2 = new Role(role,PrivilegeType.DELETE_MEMBER, userGroup);
651 roleDao.addRole(r2);
652 roles.add(r2);
653
654 Role r3 = new Role(role,PrivilegeType.WRITE_MEMBER, userGroup);
655 roleDao.addRole(r3);
656 roles.add(r3);
657
658 Role r4 = new Role(role,PrivilegeType.SHARE_QUERY, userGroup);
659 roleDao.addRole(r4);
660 roles.add(r4);
661
662 Role r5 = new Role(role,PrivilegeType.DELETE_QUERY, userGroup);
663 roleDao.addRole(r5);
664 roles.add(r5);
665 }
margaretha18533fd2018-03-28 16:01:06 +0200666 }
667 member.setRoles(roles);
668 groupMemberDao.updateMember(member);
669
670 }
671 else {
672 throw new KustvaktException(StatusCodes.AUTHORIZATION_FAILED,
673 "Unauthorized operation for user: " + username, username);
674 }
675 }
676
margarethaa18ab2b2019-11-11 12:55:26 +0100677 public void deleteMemberRoles (String username, String groupName,
margaretha99470ce2024-07-17 13:52:37 +0200678 String memberUsername, List<PredefinedRole> rolesToBeDeleted)
margarethaf7abb362018-09-18 20:09:37 +0200679 throws KustvaktException {
margaretha18533fd2018-03-28 16:01:06 +0200680
margaretha18533fd2018-03-28 16:01:06 +0200681 ParameterChecker.checkStringValue(username, "username");
margarethaa18ab2b2019-11-11 12:55:26 +0100682 ParameterChecker.checkStringValue(groupName, "groupName");
margaretha18533fd2018-03-28 16:01:06 +0200683 ParameterChecker.checkStringValue(memberUsername, "memberUsername");
684
margarethaa18ab2b2019-11-11 12:55:26 +0100685 UserGroup userGroup = userGroupDao.retrieveGroupByName(groupName, true);
margaretha18533fd2018-03-28 16:01:06 +0200686
687 if (isUserGroupAdmin(username, userGroup)
688 || adminDao.isAdmin(username)) {
689
margaretha35e1ca22023-11-16 22:00:01 +0100690 UserGroupMember member = groupMemberDao
691 .retrieveMemberById(memberUsername, userGroup.getId());
margaretha18533fd2018-03-28 16:01:06 +0200692
693 Set<Role> roles = member.getRoles();
694 Iterator<Role> i = roles.iterator();
margarethaf7abb362018-09-18 20:09:37 +0200695 while (i.hasNext()) {
margaretha99470ce2024-07-17 13:52:37 +0200696 if (rolesToBeDeleted.contains(i.next().getName())) {
margaretha18533fd2018-03-28 16:01:06 +0200697 i.remove();
698 }
699 }
700
701 member.setRoles(roles);
702 groupMemberDao.updateMember(member);
703
704 }
705 else {
706 throw new KustvaktException(StatusCodes.AUTHORIZATION_FAILED,
707 "Unauthorized operation for user: " + username, username);
708 }
709 }
margaretha0b63de42017-12-20 18:48:09 +0100710}