package com.sismics.docs.rest.resource; import com.google.common.base.Strings; import com.google.common.collect.Sets; import com.sismics.docs.core.constant.AclTargetType; import com.sismics.docs.core.dao.GroupDao; import com.sismics.docs.core.dao.RoleBaseFunctionDao; import com.sismics.docs.core.dao.UserDao; import com.sismics.docs.core.dao.criteria.GroupCriteria; import com.sismics.docs.core.dao.criteria.UserCriteria; import com.sismics.docs.core.dao.dto.GroupDto; import com.sismics.docs.core.dao.dto.UserDto; import com.sismics.docs.core.model.jpa.Group; import com.sismics.docs.core.model.jpa.User; import com.sismics.docs.core.model.jpa.UserGroup; import com.sismics.docs.core.util.RoutingUtil; import com.sismics.docs.core.util.jpa.SortCriteria; import com.sismics.docs.rest.constant.BaseFunction; import com.sismics.rest.exception.ClientException; import com.sismics.rest.exception.ForbiddenClientException; import com.sismics.rest.util.ValidationUtil; import com.sismics.util.JsonUtil; import jakarta.json.Json; import jakarta.json.JsonArrayBuilder; import jakarta.json.JsonObjectBuilder; import jakarta.ws.rs.*; import jakarta.ws.rs.core.Response; import java.text.MessageFormat; import java.util.List; import java.util.Set; /** * Group REST resources. * * @author bgamard */ @Path("/group") public class GroupResource extends BaseResource { /** * Add a group. * * @api {put} /group Add a group * @apiName PutGroup * @apiGroup Group * @apiParam {String} name Group name * @apiParam {String} [parent] Parent group name * @apiSuccess {String} status Status OK * @apiError (client) ForbiddenError Access denied * @apiError (client) ValidationError Validation error * @apiError (client) GroupAlreadyExists This group already exists * @apiError (client) ParentGroupNotFound Parent group not found * @apiPermission admin * @apiVersion 1.5.0 * * @return Response */ @PUT public Response add(@FormParam("parent") String parentName, @FormParam("name") String name) { if (!authenticate()) { throw new ForbiddenClientException(); } checkBaseFunction(BaseFunction.ADMIN); // Validate input name = ValidationUtil.validateLength(name, "name", 1, 50, false); ValidationUtil.validateAlphanumeric(name, "name"); // Avoid duplicates GroupDao groupDao = new GroupDao(); Group existingGroup = groupDao.getActiveByName(name); if (existingGroup != null) { throw new ClientException("GroupAlreadyExists", MessageFormat.format("This group already exists: {0}", name)); } // Validate parent String parentId = null; if (!Strings.isNullOrEmpty(parentName)) { Group parentGroup = groupDao.getActiveByName(parentName); if (parentGroup == null) { throw new ClientException("ParentGroupNotFound", MessageFormat.format("This group does not exists: {0}", parentName)); } parentId = parentGroup.getId(); } // Create the group groupDao.create(new Group() .setName(name) .setParentId(parentId), principal.getId()); // Always return OK JsonObjectBuilder response = Json.createObjectBuilder() .add("status", "ok"); return Response.ok().entity(response.build()).build(); } /** * Update a group. * * @api {post} /group/:name Update a group * @apiName PostGroup * @apiGroup Group * @apiParam {String} name Group name * @apiParam {String} [parent] Parent group name * @apiSuccess {String} status Status OK * @apiError (client) ForbiddenError Access denied * @apiError (client) ValidationError Validation error * @apiError (client) GroupAlreadyExists This group already exists * @apiError (client) ParentGroupNotFound Parent group not found * @apiError (client) NotFound Group not found * @apiPermission admin * @apiVersion 1.5.0 * * @return Response */ @POST @Path("{groupName: [a-zA-Z0-9_]+}") public Response update(@PathParam("groupName") String groupName, @FormParam("parent") String parentName, @FormParam("name") String name) { if (!authenticate()) { throw new ForbiddenClientException(); } checkBaseFunction(BaseFunction.ADMIN); // Validate input name = ValidationUtil.validateLength(name, "name", 1, 50, false); ValidationUtil.validateAlphanumeric(name, "name"); // Get the group (by its old name) GroupDao groupDao = new GroupDao(); Group group = groupDao.getActiveByName(groupName); if (group == null) { throw new NotFoundException(); } // Avoid duplicates Group existingGroup = groupDao.getActiveByName(name); if (existingGroup != null && !existingGroup.getId().equals(group.getId())) { throw new ClientException("GroupAlreadyExists", MessageFormat.format("This group already exists: {0}", name)); } // Validate parent String parentId = null; if (!Strings.isNullOrEmpty(parentName)) { Group parentGroup = groupDao.getActiveByName(parentName); if (parentGroup == null) { throw new ClientException("ParentGroupNotFound", MessageFormat.format("This group does not exists: {0}", parentName)); } parentId = parentGroup.getId(); } // Check that this group is not used in any workflow in case of renaming if (!name.equals(groupName)) { String routeModelName = RoutingUtil.findRouteModelNameByTargetName(AclTargetType.GROUP, groupName); if (routeModelName != null) { throw new ClientException("GroupUsedInRouteModel", routeModelName); } } // Update the group groupDao.update(group.setName(name) .setParentId(parentId), principal.getId()); // Always return OK JsonObjectBuilder response = Json.createObjectBuilder() .add("status", "ok"); return Response.ok().entity(response.build()).build(); } /** * Delete a group. * * @api {delete} /group/:name Delete a group * @apiName DeleteGroup * @apiGroup Group * @apiParam {String} name Group name * @apiSuccess {String} status Status OK * @apiError (client) ForbiddenError Access denied * @apiError (client) NotFound Group not found * @apiError (client) GroupUsedInRouteModel The group is used in a route model * @apiPermission admin * @apiVersion 1.5.0 * * @return Response */ @DELETE @Path("{groupName: [a-zA-Z0-9_]+}") public Response delete(@PathParam("groupName") String groupName) { if (!authenticate()) { throw new ForbiddenClientException(); } checkBaseFunction(BaseFunction.ADMIN); // Get the group GroupDao groupDao = new GroupDao(); Group group = groupDao.getActiveByName(groupName); if (group == null) { throw new NotFoundException(); } // Ensure that the admin group is not deleted if (group.getRoleId() != null) { RoleBaseFunctionDao roleBaseFunctionDao = new RoleBaseFunctionDao(); Set baseFunctionSet = roleBaseFunctionDao.findByRoleId(Sets.newHashSet(group.getRoleId())); if (baseFunctionSet.contains(BaseFunction.ADMIN.name())) { throw new ClientException("ForbiddenError", "The administrators group cannot be deleted"); } } // Check that this group is not used in any workflow String routeModelName = RoutingUtil.findRouteModelNameByTargetName(AclTargetType.GROUP, groupName); if (routeModelName != null) { throw new ClientException("GroupUsedInRouteModel", routeModelName); } // Delete the group groupDao.delete(group.getId(), principal.getId()); // Always return OK JsonObjectBuilder response = Json.createObjectBuilder() .add("status", "ok"); return Response.ok().entity(response.build()).build(); } /** * Add a user to a group. * * @api {put} /group/:name Add a user to a group * @apiName PutGroupMember * @apiGroup Group * @apiParam {String} name Group name * @apiParam {String} username Username * @apiSuccess {String} status Status OK * @apiError (client) ForbiddenError Access denied * @apiError (client) ValidationError Validation error * @apiError (client) NotFound Group or user not found * @apiPermission admin * @apiVersion 1.5.0 * * @param groupName Group name * @param username Username * @return Response */ @PUT @Path("{groupName: [a-zA-Z0-9_]+}") public Response addMember(@PathParam("groupName") String groupName, @FormParam("username") String username) { if (!authenticate()) { throw new ForbiddenClientException(); } checkBaseFunction(BaseFunction.ADMIN); // Validate input groupName = ValidationUtil.validateLength(groupName, "name", 1, 50, false); username = ValidationUtil.validateLength(username, "username", 1, 50, false); // Get the group GroupDao groupDao = new GroupDao(); Group group = groupDao.getActiveByName(groupName); if (group == null) { throw new NotFoundException(); } // Get the user UserDao userDao = new UserDao(); User user = userDao.getActiveByUsername(username); if (user == null) { throw new NotFoundException(); } // Avoid duplicates List groupDtoList = groupDao.findByCriteria(new GroupCriteria().setUserId(user.getId()), null); boolean found = false; for (GroupDto groupDto : groupDtoList) { if (groupDto.getId().equals(group.getId())) { found = true; } } if (!found) { // Add the membership UserGroup userGroup = new UserGroup(); userGroup.setGroupId(group.getId()); userGroup.setUserId(user.getId()); groupDao.addMember(userGroup); } // Always return OK JsonObjectBuilder response = Json.createObjectBuilder() .add("status", "ok"); return Response.ok().entity(response.build()).build(); } /** * Remove an user from a group. * * @api {delete} /group/:name/:username Remove a user from a group * @apiName DeleteGroupMember * @apiGroup Group * @apiParam {String} name Group name * @apiParam {String} username Username * @apiSuccess {String} status Status OK * @apiError (client) ForbiddenError Access denied * @apiError (client) ValidationError Validation error * @apiError (client) NotFound Group or user not found * @apiPermission admin * @apiVersion 1.5.0 * * @param groupName Group name * @param username Username * @return Response */ @DELETE @Path("{groupName: [a-zA-Z0-9_]+}/{username: [a-zA-Z0-9_@.-]+}") public Response removeMember(@PathParam("groupName") String groupName, @PathParam("username") String username) { if (!authenticate()) { throw new ForbiddenClientException(); } checkBaseFunction(BaseFunction.ADMIN); // Validate input groupName = ValidationUtil.validateLength(groupName, "name", 1, 50, false); username = ValidationUtil.validateLength(username, "username", 1, 50, false); // Get the group GroupDao groupDao = new GroupDao(); Group group = groupDao.getActiveByName(groupName); if (group == null) { throw new NotFoundException(); } // Get the user UserDao userDao = new UserDao(); User user = userDao.getActiveByUsername(username); if (user == null) { throw new NotFoundException(); } // Remove the membership groupDao.removeMember(group.getId(), user.getId()); // Always return OK JsonObjectBuilder response = Json.createObjectBuilder() .add("status", "ok"); return Response.ok().entity(response.build()).build(); } /** * Returns all active groups. * * @api {get} /group Get groups * @apiName GetGroupList * @apiGroup Group * @apiParam {Number} sort_column Column index to sort on * @apiParam {Boolean} asc If true, sort in ascending order * @apiSuccess {Object[]} groups List of groups * @apiSuccess {String} groups.name Name * @apiSuccess {String} groups.parent Parent name * @apiError (client) ForbiddenError Access denied * @apiPermission user * @apiVersion 1.5.0 * * @param sortColumn Sort index * @param asc If true, ascending sorting, else descending * @return Response */ @GET public Response list( @QueryParam("sort_column") Integer sortColumn, @QueryParam("asc") Boolean asc) { if (!authenticate()) { throw new ForbiddenClientException(); } JsonArrayBuilder groups = Json.createArrayBuilder(); SortCriteria sortCriteria = new SortCriteria(sortColumn, asc); GroupDao groupDao = new GroupDao(); List groupDtoList = groupDao.findByCriteria(new GroupCriteria(), sortCriteria); for (GroupDto groupDto : groupDtoList) { groups.add(Json.createObjectBuilder() .add("name", groupDto.getName()) .add("parent", JsonUtil.nullable(groupDto.getParentName()))); } JsonObjectBuilder response = Json.createObjectBuilder() .add("groups", groups); return Response.ok().entity(response.build()).build(); } /** * Get a group. * * @api {get} /group/:name Get a group * @apiName GetGroup * @apiGroup Group * @apiParam {String} name Group name * @apiSuccess {String} name Group name * @apiSuccess {String} parent Parent name * @apiSuccess {String[]} members List of members * @apiError (client) ForbiddenError Access denied * @apiError (client) NotFound Group not found * @apiPermission user * @apiVersion 1.5.0 * * @param groupName Group name * @return Response */ @GET @Path("{groupName: [a-zA-Z0-9_]+}") public Response get(@PathParam("groupName") String groupName) { if (!authenticate()) { throw new ForbiddenClientException(); } // Get the group GroupDao groupDao = new GroupDao(); Group group = groupDao.getActiveByName(groupName); if (group == null) { throw new NotFoundException(); } // Build the response JsonObjectBuilder response = Json.createObjectBuilder() .add("name", group.getName()); // Get the parent if (group.getParentId() != null) { Group parentGroup = groupDao.getActiveById(group.getParentId()); response.add("parent", parentGroup.getName()); } // Members JsonArrayBuilder members = Json.createArrayBuilder(); UserDao userDao = new UserDao(); List userDtoList = userDao.findByCriteria(new UserCriteria().setGroupId(group.getId()), new SortCriteria(1, true)); for (UserDto userDto : userDtoList) { members.add(userDto.getUsername()); } response.add("members", members); return Response.ok().entity(response.build()).build(); } }