1
0
mirror of https://github.com/sismics/docs.git synced 2025-12-27 00:22:33 +00:00

#18: Group resource, groups handling in ACL, groups returned in users

This commit is contained in:
jendib
2016-03-19 19:41:28 +01:00
parent 43a1575187
commit a5ce5bf9ec
27 changed files with 725 additions and 133 deletions

View File

@@ -1,6 +1,7 @@
package com.sismics.docs.rest.resource;
import java.text.MessageFormat;
import java.util.List;
import javax.json.Json;
import javax.json.JsonArrayBuilder;
@@ -19,14 +20,16 @@ import com.sismics.docs.core.constant.AclTargetType;
import com.sismics.docs.core.constant.PermType;
import com.sismics.docs.core.dao.jpa.AclDao;
import com.sismics.docs.core.dao.jpa.DocumentDao;
import com.sismics.docs.core.dao.jpa.GroupDao;
import com.sismics.docs.core.dao.jpa.UserDao;
import com.sismics.docs.core.dao.jpa.criteria.GroupCriteria;
import com.sismics.docs.core.dao.jpa.criteria.UserCriteria;
import com.sismics.docs.core.dao.jpa.dto.GroupDto;
import com.sismics.docs.core.dao.jpa.dto.UserDto;
import com.sismics.docs.core.model.jpa.Acl;
import com.sismics.docs.core.model.jpa.Document;
import com.sismics.docs.core.model.jpa.Group;
import com.sismics.docs.core.model.jpa.User;
import com.sismics.docs.core.util.jpa.PaginatedList;
import com.sismics.docs.core.util.jpa.PaginatedLists;
import com.sismics.docs.core.util.jpa.SortCriteria;
import com.sismics.rest.exception.ClientException;
import com.sismics.rest.exception.ForbiddenClientException;
@@ -42,27 +45,52 @@ public class AclResource extends BaseResource {
/**
* Add an ACL.
*
* @param sourceId Source ID
* @param permStr Permission
* @param targetName Target name
* @param type ACL type
* @return Response
*/
@PUT
public Response add(@FormParam("source") String sourceId,
@FormParam("perm") String permStr,
@FormParam("username") String username) {
@FormParam("target") String targetName,
@FormParam("type") String typeStr) {
if (!authenticate()) {
throw new ForbiddenClientException();
}
// TODO Allow group input
// Validate input
ValidationUtil.validateRequired(sourceId, "source");
PermType perm = PermType.valueOf(ValidationUtil.validateLength(permStr, "perm", 1, 30, false));
username = ValidationUtil.validateLength(username, "username", 1, 50, false);
AclTargetType type = AclTargetType.valueOf(ValidationUtil.validateLength(typeStr, "type", 1, 10, false));
targetName = ValidationUtil.validateLength(targetName, "target", 1, 50, false);
// Validate the target user
UserDao userDao = new UserDao();
User user = userDao.getActiveByUsername(username);
if (user == null) {
throw new ClientException("UserNotFound", MessageFormat.format("User not found: {0}", username));
// Search user or group
String targetId = null;
switch (type) {
case USER:
UserDao userDao = new UserDao();
User user = userDao.getActiveByUsername(targetName);
if (user != null) {
targetId = user.getId();
}
break;
case GROUP:
GroupDao groupDao = new GroupDao();
Group group = groupDao.getActiveByName(targetName);
if (group != null) {
targetId = group.getId();
}
break;
case SHARE:
// Share must use the Share REST resource
break;
}
// Does a target has been found?
if (targetId == null) {
throw new ClientException("InvalidTarget", MessageFormat.format("This target does not exist: {0}", targetName));
}
// Check permission on the source by the principal
@@ -75,7 +103,7 @@ public class AclResource extends BaseResource {
Acl acl = new Acl();
acl.setSourceId(sourceId);
acl.setPerm(perm);
acl.setTargetId(user.getId());
acl.setTargetId(targetId);
// Avoid duplicates
if (!aclDao.checkPermission(acl.getSourceId(), acl.getPerm(), Lists.newArrayList(acl.getTargetId()))) {
@@ -85,8 +113,8 @@ public class AclResource extends BaseResource {
JsonObjectBuilder response = Json.createObjectBuilder()
.add("perm", acl.getPerm().name())
.add("id", acl.getTargetId())
.add("name", user.getUsername())
.add("type", AclTargetType.USER.name());
.add("name", targetName)
.add("type", type.name());
return Response.ok().entity(response.build()).build();
}
@@ -96,7 +124,9 @@ public class AclResource extends BaseResource {
/**
* Deletes an ACL.
*
* @param id ACL ID
* @param sourceId Source ID
* @param permStr Permission
* @param targetId Target ID
* @return Response
*/
@DELETE
@@ -155,20 +185,25 @@ public class AclResource extends BaseResource {
// Search users
UserDao userDao = new UserDao();
JsonArrayBuilder users = Json.createArrayBuilder();
PaginatedList<UserDto> paginatedList = PaginatedLists.create();
SortCriteria sortCriteria = new SortCriteria(1, true);
userDao.findByCriteria(paginatedList, new UserCriteria().setSearch(search), sortCriteria);
for (UserDto userDto : paginatedList.getResultList()) {
List<UserDto> userDtoList = userDao.findByCriteria(new UserCriteria().setSearch(search), sortCriteria);
for (UserDto userDto : userDtoList) {
users.add(Json.createObjectBuilder()
.add("username", userDto.getUsername()));
.add("name", userDto.getUsername()));
}
// TODO Returns groups too
// Search groups
GroupDao groupDao = new GroupDao();
JsonArrayBuilder groups = Json.createArrayBuilder();
List<GroupDto> groupDtoList = groupDao.findByCriteria(new GroupCriteria().setSearch(search), sortCriteria);
for (GroupDto groupDto : groupDtoList) {
groups.add(Json.createObjectBuilder()
.add("name", groupDto.getName()));
}
JsonObjectBuilder response = Json.createObjectBuilder()
.add("users", users);
.add("users", users)
.add("groups", groups);
return Response.ok().entity(response.build()).build();
}
}

View File

@@ -87,7 +87,7 @@ public abstract class BaseResource {
* @return List of ACL target ID
*/
protected List<String> getTargetIdList(String shareId) {
List<String> targetIdList = Lists.newArrayList(principal.getGroupIdList());
List<String> targetIdList = Lists.newArrayList(principal.getGroupIdSet());
if (principal.getId() != null) {
targetIdList.add(principal.getId());
}

View File

@@ -149,7 +149,7 @@ public class DocumentResource extends BaseResource {
if (!principal.isAnonymous()
&& (aclDto.getTargetId().equals(principal.getId())
|| principal.getGroupIdList().contains(aclDto.getTargetId()))
|| principal.getGroupIdSet().contains(aclDto.getTargetId()))
&& aclDto.getPerm() == PermType.WRITE) {
// The document is writable for the current user
writable = true;

View File

@@ -1,17 +1,26 @@
package com.sismics.docs.rest.resource;
import java.text.MessageFormat;
import java.util.List;
import javax.json.Json;
import javax.json.JsonObjectBuilder;
import javax.ws.rs.DELETE;
import javax.ws.rs.FormParam;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import com.google.common.base.Strings;
import com.sismics.docs.core.dao.jpa.GroupDao;
import com.sismics.docs.core.dao.jpa.UserDao;
import com.sismics.docs.core.dao.jpa.criteria.GroupCriteria;
import com.sismics.docs.core.dao.jpa.dto.GroupDto;
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.rest.constant.BaseFunction;
import com.sismics.rest.exception.ClientException;
import com.sismics.rest.exception.ForbiddenClientException;
@@ -39,10 +48,11 @@ public class GroupResource extends BaseResource {
// Validate input
name = ValidationUtil.validateLength(name, "name", 1, 50, false);
ValidationUtil.validateAlphanumeric(name, "name");
// Avoid duplicates
GroupDao groupDao = new GroupDao();
Group existingGroup = groupDao.getByName(name);
Group existingGroup = groupDao.getActiveByName(name);
if (existingGroup != null) {
throw new ClientException("GroupAlreadyExists", MessageFormat.format("This group already exists: {0}", name));
}
@@ -50,9 +60,9 @@ public class GroupResource extends BaseResource {
// Validate parent
String parentId = null;
if (!Strings.isNullOrEmpty(parentName)) {
Group parentGroup = groupDao.getByName(parentName);
Group parentGroup = groupDao.getActiveByName(parentName);
if (parentGroup == null) {
throw new ClientException("ParentGroupNotFound", MessageFormat.format("This group doest not exists: {0}", parentName));
throw new ClientException("ParentGroupNotFound", MessageFormat.format("This group does not exists: {0}", parentName));
}
parentId = parentGroup.getId();
}
@@ -67,4 +77,104 @@ public class GroupResource extends BaseResource {
.add("status", "ok");
return Response.ok().entity(response.build()).build();
}
/**
* Add a user to a group.
*
* @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) {
return Response.status(Status.NOT_FOUND).build();
}
// Get the user
UserDao userDao = new UserDao();
User user = userDao.getActiveByUsername(username);
if (user == null) {
return Response.status(Status.NOT_FOUND).build();
}
// Avoid duplicates
List<GroupDto> 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.
*
* @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) {
return Response.status(Status.NOT_FOUND).build();
}
// Get the user
UserDao userDao = new UserDao();
User user = userDao.getActiveByUsername(username);
if (user == null) {
return Response.status(Status.NOT_FOUND).build();
}
// 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();
}
}

View File

@@ -37,6 +37,7 @@ public class ShareResource extends BaseResource {
* Add a share to a document.
*
* @param documentId Document ID
* @param name Share name
* @return Response
*/
@PUT

View File

@@ -29,9 +29,12 @@ import com.sismics.docs.core.constant.Constants;
import com.sismics.docs.core.dao.jpa.AuthenticationTokenDao;
import com.sismics.docs.core.dao.jpa.DocumentDao;
import com.sismics.docs.core.dao.jpa.FileDao;
import com.sismics.docs.core.dao.jpa.GroupDao;
import com.sismics.docs.core.dao.jpa.RoleBaseFunctionDao;
import com.sismics.docs.core.dao.jpa.UserDao;
import com.sismics.docs.core.dao.jpa.criteria.GroupCriteria;
import com.sismics.docs.core.dao.jpa.criteria.UserCriteria;
import com.sismics.docs.core.dao.jpa.dto.GroupDto;
import com.sismics.docs.core.dao.jpa.dto.UserDto;
import com.sismics.docs.core.event.DocumentDeletedAsyncEvent;
import com.sismics.docs.core.event.FileDeletedAsyncEvent;
@@ -41,8 +44,6 @@ import com.sismics.docs.core.model.jpa.Document;
import com.sismics.docs.core.model.jpa.File;
import com.sismics.docs.core.model.jpa.User;
import com.sismics.docs.core.util.EncryptionUtil;
import com.sismics.docs.core.util.jpa.PaginatedList;
import com.sismics.docs.core.util.jpa.PaginatedLists;
import com.sismics.docs.core.util.jpa.SortCriteria;
import com.sismics.docs.rest.constant.BaseFunction;
import com.sismics.rest.exception.ClientException;
@@ -299,14 +300,7 @@ public class UserResource extends BaseResource {
}
// Get the value of the session token
String authToken = null;
if (request.getCookies() != null) {
for (Cookie cookie : request.getCookies()) {
if (TokenBasedSecurityFilter.COOKIE_NAME.equals(cookie.getName())) {
authToken = cookie.getValue();
}
}
}
String authToken = getAuthToken();
AuthenticationTokenDao authenticationTokenDao = new AuthenticationTokenDao();
AuthenticationToken authenticationToken = null;
@@ -457,18 +451,39 @@ public class UserResource extends BaseResource {
response.add("is_default_password", Constants.DEFAULT_ADMIN_PASSWORD.equals(adminUser.getPassword()));
}
} else {
// Update the last connection date
String authToken = getAuthToken();
AuthenticationTokenDao authenticationTokenDao = new AuthenticationTokenDao();
authenticationTokenDao.updateLastConnectionDate(authToken);
// Build the response
response.add("anonymous", false);
UserDao userDao = new UserDao();
GroupDao groupDao = new GroupDao();
User user = userDao.getById(principal.getId());
List<GroupDto> groupDtoList = groupDao.findByCriteria(new GroupCriteria()
.setUserId(user.getId())
.setRecursive(true), null);
response.add("username", user.getUsername())
.add("email", user.getEmail())
.add("storage_quota", user.getStorageQuota())
.add("storage_current", user.getStorageCurrent());
// Base functions
JsonArrayBuilder baseFunctions = Json.createArrayBuilder();
for (String baseFunction : ((UserPrincipal) principal).getBaseFunctionSet()) {
baseFunctions.add(baseFunction);
}
// Groups
JsonArrayBuilder groups = Json.createArrayBuilder();
for (GroupDto groupDto : groupDtoList) {
groups.add(groupDto.getName());
}
response.add("base_functions", baseFunctions)
.add("groups", groups)
.add("is_default_password", hasBaseFunction(BaseFunction.ADMIN) && Constants.DEFAULT_ADMIN_PASSWORD.equals(user.getPassword()));
}
@@ -495,8 +510,19 @@ public class UserResource extends BaseResource {
throw new ClientException("UserNotFound", "The user doesn't exist");
}
// Groups
GroupDao groupDao = new GroupDao();
List<GroupDto> groupDtoList = groupDao.findByCriteria(
new GroupCriteria().setUserId(user.getId()),
new SortCriteria(1, true));
JsonArrayBuilder groups = Json.createArrayBuilder();
for (GroupDto groupDto : groupDtoList) {
groups.add(groupDto.getName());
}
JsonObjectBuilder response = Json.createObjectBuilder()
.add("username", user.getUsername())
.add("groups", groups)
.add("email", user.getEmail())
.add("storage_quota", user.getStorageQuota())
.add("storage_current", user.getStorageCurrent());
@@ -515,8 +541,6 @@ public class UserResource extends BaseResource {
@GET
@Path("list")
public Response list(
@QueryParam("limit") Integer limit,
@QueryParam("offset") Integer offset,
@QueryParam("sort_column") Integer sortColumn,
@QueryParam("asc") Boolean asc) {
if (!authenticate()) {
@@ -524,12 +548,11 @@ public class UserResource extends BaseResource {
}
JsonArrayBuilder users = Json.createArrayBuilder();
PaginatedList<UserDto> paginatedList = PaginatedLists.create(limit, offset);
SortCriteria sortCriteria = new SortCriteria(sortColumn, asc);
UserDao userDao = new UserDao();
userDao.findByCriteria(paginatedList, new UserCriteria(), sortCriteria);
for (UserDto userDto : paginatedList.getResultList()) {
List<UserDto> userDtoList = userDao.findByCriteria(new UserCriteria(), sortCriteria);
for (UserDto userDto : userDtoList) {
users.add(Json.createObjectBuilder()
.add("id", userDto.getId())
.add("username", userDto.getUsername())
@@ -540,7 +563,6 @@ public class UserResource extends BaseResource {
}
JsonObjectBuilder response = Json.createObjectBuilder()
.add("total", paginatedList.getResultCount())
.add("users", users);
return Response.ok().entity(response.build()).build();
}
@@ -558,14 +580,7 @@ public class UserResource extends BaseResource {
}
// Get the value of the session token
String authToken = null;
if (request.getCookies() != null) {
for (Cookie cookie : request.getCookies()) {
if (TokenBasedSecurityFilter.COOKIE_NAME.equals(cookie.getName())) {
authToken = cookie.getValue();
}
}
}
String authToken = getAuthToken();
JsonArrayBuilder sessions = Json.createArrayBuilder();
AuthenticationTokenDao authenticationTokenDao = new AuthenticationTokenDao();
@@ -600,14 +615,7 @@ public class UserResource extends BaseResource {
}
// Get the value of the session token
String authToken = null;
if (request.getCookies() != null) {
for (Cookie cookie : request.getCookies()) {
if (TokenBasedSecurityFilter.COOKIE_NAME.equals(cookie.getName())) {
authToken = cookie.getValue();
}
}
}
String authToken = getAuthToken();
// Remove other tokens
AuthenticationTokenDao authenticationTokenDao = new AuthenticationTokenDao();
@@ -618,4 +626,20 @@ public class UserResource extends BaseResource {
.add("status", "ok");
return Response.ok().entity(response.build()).build();
}
/**
* Returns the authentication token value.
*
* @return Token value
*/
private String getAuthToken() {
if (request.getCookies() != null) {
for (Cookie cookie : request.getCookies()) {
if (TokenBasedSecurityFilter.COOKIE_NAME.equals(cookie.getName())) {
return cookie.getValue();
}
}
}
return null;
}
}

View File

@@ -74,7 +74,7 @@ angular.module('docs').controller('DocumentViewPermissions', function ($scope, $
.get({
search: $viewValue
}).then(function(data) {
deferred.resolve(_.pluck(data.users, 'username'), true);
deferred.resolve(_.pluck(data.users, 'name'), true);
});
return deferred.promise;
};

View File

@@ -8,7 +8,7 @@ angular.module('docs').controller('SettingsUser', function($scope, $state, Resta
* Load users from server.
*/
$scope.loadUsers = function() {
Restangular.one('user/list').get({ limit: 100 }).then(function(data) {
Restangular.one('user/list').get().then(function(data) {
$scope.users = data.users;
});
};

View File

@@ -22,10 +22,10 @@
<form name="aclForm" class="form-horizontal">
<div class="form-group">
<label class=" col-sm-2 control-label" for="inputTarget">User</label>
<label class=" col-sm-2 control-label" for="inputTarget">For</label>
<div class="col-sm-3">
<input required ng-maxlength="50" class="form-control" type="text" id="inputTarget"
placeholder="Type a username" name="username" ng-model="acl.username" autocomplete="off"
placeholder="Type a username" name="username" ng-model="acl.target" autocomplete="off"
typeahead="username for username in getTargetAclTypeahead($viewValue) | filter: $viewValue"
typeahead-wait-ms="200" />
</div>