#18: ACL check for groups

This commit is contained in:
jendib 2016-03-15 22:44:50 +01:00
parent 6012cdd9a5
commit de3f055323
13 changed files with 87 additions and 72 deletions

View File

@ -105,12 +105,12 @@ public class AclDao {
* @param targetId ACL target entity ID
* @return True if the document is accessible
*/
public boolean checkPermission(String sourceId, PermType perm, String targetId) {
public boolean checkPermission(String sourceId, PermType perm, List<String> targetIdList) {
EntityManager em = ThreadLocalContext.get().getEntityManager();
Query q = em.createQuery("select a from Acl a where a.sourceId = :sourceId and a.perm = :perm and a.targetId = :targetId and a.deleteDate is null");
Query q = em.createQuery("select a from Acl a where a.sourceId = :sourceId and a.perm = :perm and a.targetId in (:targetIdList) and a.deleteDate is null");
q.setParameter("sourceId", sourceId);
q.setParameter("perm", perm);
q.setParameter("targetId", targetId);
q.setParameter("targetIdList", targetIdList);
// We have a matching permission
if (q.getResultList().size() > 0) {

View File

@ -90,20 +90,20 @@ public class DocumentDao {
* @param userId User ID
* @return Document
*/
public DocumentDto getDocument(String id, PermType perm, String userId) {
public DocumentDto getDocument(String id, PermType perm, List<String> targetIdList) {
EntityManager em = ThreadLocalContext.get().getEntityManager();
StringBuilder sb = new StringBuilder("select d.DOC_ID_C, d.DOC_TITLE_C, d.DOC_DESCRIPTION_C, d.DOC_SUBJECT_C, d.DOC_IDENTIFIER_C, d.DOC_PUBLISHER_C, d.DOC_FORMAT_C, d.DOC_SOURCE_C, d.DOC_TYPE_C, d.DOC_COVERAGE_C, d.DOC_RIGHTS_C, d.DOC_CREATEDATE_D, d.DOC_LANGUAGE_C, ");
sb.append(" (select count(s.SHA_ID_C) from T_SHARE s, T_ACL ac where ac.ACL_SOURCEID_C = d.DOC_ID_C and ac.ACL_TARGETID_C = s.SHA_ID_C and ac.ACL_DELETEDATE_D is null and s.SHA_DELETEDATE_D is null), ");
sb.append(" (select count(f.FIL_ID_C) from T_FILE f where f.FIL_DELETEDATE_D is null and f.FIL_IDDOC_C = d.DOC_ID_C), ");
sb.append(" u.USE_USERNAME_C ");
sb.append(" from T_DOCUMENT d, T_USER u ");
sb.append(" join T_ACL a on a.ACL_SOURCEID_C = d.DOC_ID_C and a.ACL_TARGETID_C = :userId and a.ACL_PERM_C = :perm and a.ACL_DELETEDATE_D is null ");
sb.append(" join T_ACL a on a.ACL_SOURCEID_C = d.DOC_ID_C and a.ACL_TARGETID_C in (:targetIdList) and a.ACL_PERM_C = :perm and a.ACL_DELETEDATE_D is null ");
sb.append(" where d.DOC_IDUSER_C = u.USE_ID_C and d.DOC_ID_C = :id and d.DOC_DELETEDATE_D is null ");
Query q = em.createNativeQuery(sb.toString());
q.setParameter("id", id);
q.setParameter("perm", perm.name());
q.setParameter("userId", userId);
q.setParameter("targetIdList", targetIdList);
Object[] o = null;
try {
@ -212,10 +212,10 @@ public class DocumentDao {
sb.append(" from T_DOCUMENT d ");
// Adds search criteria
if (criteria.getUserId() != null) {
if (criteria.getTargetIdList() != null) {
// Read permission is enough for searching
sb.append(" join T_ACL a on a.ACL_SOURCEID_C = d.DOC_ID_C and a.ACL_TARGETID_C = :userId and a.ACL_PERM_C = 'READ' and a.ACL_DELETEDATE_D is null ");
parameterMap.put("userId", criteria.getUserId());
sb.append(" join T_ACL a on a.ACL_SOURCEID_C = d.DOC_ID_C and a.ACL_TARGETID_C in (:targetIdList) and a.ACL_PERM_C = 'READ' and a.ACL_DELETEDATE_D is null ");
parameterMap.put("targetIdList", criteria.getTargetIdList());
}
if (!Strings.isNullOrEmpty(criteria.getSearch()) || !Strings.isNullOrEmpty(criteria.getFullSearch())) {
LuceneDao luceneDao = new LuceneDao();

View File

@ -11,9 +11,9 @@ import java.util.List;
*/
public class DocumentCriteria {
/**
* User ID.
* ACL target ID list.
*/
private String userId;
private List<String> targetIdList;
/**
* Search query.
@ -55,12 +55,12 @@ public class DocumentCriteria {
*/
private String creatorId;
public String getUserId() {
return userId;
public List<String> getTargetIdList() {
return targetIdList;
}
public void setUserId(String userId) {
this.userId = userId;
public void setTargetIdList(List<String> targetIdList) {
this.targetIdList = targetIdList;
}
public String getSearch() {

View File

@ -1,7 +1,11 @@
package com.sismics.security;
import java.util.List;
import org.joda.time.DateTimeZone;
import com.google.common.collect.Lists;
/**
* Anonymous principal.
*
@ -47,12 +51,12 @@ public class AnonymousPrincipal implements IPrincipal {
return null;
}
/**
* Setter of dateTimeZone.
*
* @param dateTimeZone dateTimeZone
*/
public void setDateTimeZone(DateTimeZone dateTimeZone) {
this.dateTimeZone = dateTimeZone;
}
@Override
public List<String> getGroupIdList() {
return Lists.newArrayList();
}
}

View File

@ -1,6 +1,7 @@
package com.sismics.security;
import java.security.Principal;
import java.util.List;
import org.joda.time.DateTimeZone;
@ -24,6 +25,14 @@ public interface IPrincipal extends Principal {
*/
public String getId();
/**
* Returns the list of group ID of the connected user,
* or an empty list if the user is anonymous.
*
* @return List of group ID
*/
public List<String> getGroupIdList();
/**
* Returns the timezone of the principal.
*

View File

@ -1,9 +1,12 @@
package com.sismics.security;
import java.util.List;
import java.util.Set;
import org.joda.time.DateTimeZone;
import jersey.repackaged.com.google.common.collect.Lists;
/**
* Authenticated users principal.
*
@ -56,11 +59,6 @@ public class UserPrincipal implements IPrincipal {
return id;
}
/**
* Setter of id.
*
* @param id id
*/
public void setId(String id) {
this.id = id;
}
@ -70,11 +68,6 @@ public class UserPrincipal implements IPrincipal {
return name;
}
/**
* Setter of name.
*
* @param name name
*/
public void setName(String name) {
this.name = name;
}
@ -84,11 +77,6 @@ public class UserPrincipal implements IPrincipal {
return dateTimeZone;
}
/**
* Setter of dateTimeZone.
*
* @param dateTimeZone dateTimeZone
*/
public void setDateTimeZone(DateTimeZone dateTimeZone) {
this.dateTimeZone = dateTimeZone;
}
@ -98,31 +86,21 @@ public class UserPrincipal implements IPrincipal {
return email;
}
/**
* Setter of email.
*
* @param email email
*/
public void setEmail(String email) {
this.email = email;
}
/**
* Getter of baseFunctionSet.
*
* @return baseFunctionSet
*/
public Set<String> getBaseFunctionSet() {
return baseFunctionSet;
}
/**
* Setter of baseFunctionSet.
*
* @param baseFunctionSet baseFunctionSet
*/
public void setBaseFunctionSet(Set<String> baseFunctionSet) {
this.baseFunctionSet = baseFunctionSet;
}
@Override
public List<String> getGroupIdList() {
// TODO Real groups
return Lists.newArrayList("members");
}
}

View File

@ -14,6 +14,7 @@ import javax.ws.rs.PathParam;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Response;
import com.google.common.collect.Lists;
import com.sismics.docs.core.constant.AclTargetType;
import com.sismics.docs.core.constant.PermType;
import com.sismics.docs.core.dao.jpa.AclDao;
@ -51,6 +52,7 @@ public class AclResource extends BaseResource {
throw new ForbiddenClientException();
}
// TODO Allow group input
// Validate input
ValidationUtil.validateRequired(sourceId, "source");
PermType perm = PermType.valueOf(ValidationUtil.validateLength(permStr, "perm", 1, 30, false));
@ -65,7 +67,7 @@ public class AclResource extends BaseResource {
// Check permission on the source by the principal
AclDao aclDao = new AclDao();
if (!aclDao.checkPermission(sourceId, PermType.WRITE, principal.getId())) {
if (!aclDao.checkPermission(sourceId, PermType.WRITE, getTargetIdList(null))) {
throw new ForbiddenClientException();
}
@ -76,7 +78,7 @@ public class AclResource extends BaseResource {
acl.setTargetId(user.getId());
// Avoid duplicates
if (!aclDao.checkPermission(acl.getSourceId(), acl.getPerm(), acl.getTargetId())) {
if (!aclDao.checkPermission(acl.getSourceId(), acl.getPerm(), Lists.newArrayList(acl.getTargetId()))) {
aclDao.create(acl, principal.getId());
// Returns the ACL
@ -114,7 +116,7 @@ public class AclResource extends BaseResource {
// Check permission on the source by the principal
AclDao aclDao = new AclDao();
if (!aclDao.checkPermission(sourceId, PermType.WRITE, principal.getId())) {
if (!aclDao.checkPermission(sourceId, PermType.WRITE, getTargetIdList(null))) {
throw new ForbiddenClientException();
}
@ -163,6 +165,8 @@ public class AclResource extends BaseResource {
.add("username", userDto.getUsername()));
}
// TODO Returns groups too
JsonObjectBuilder response = Json.createObjectBuilder()
.add("users", users);
return Response.ok().entity(response.build()).build();

View File

@ -49,7 +49,7 @@ public class AuditLogResource extends BaseResource {
} else {
// Check ACL on the document
AclDao aclDao = new AclDao();
if (!aclDao.checkPermission(documentId, PermType.READ, principal.getId())) {
if (!aclDao.checkPermission(documentId, PermType.READ, getTargetIdList(null))) {
return Response.status(Status.NOT_FOUND).build();
}
criteria.setDocumentId(documentId);

View File

@ -1,12 +1,14 @@
package com.sismics.docs.rest.resource;
import java.security.Principal;
import java.util.List;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import com.google.common.collect.Lists;
import com.sismics.docs.rest.constant.BaseFunction;
import com.sismics.rest.exception.ForbiddenClientException;
import com.sismics.security.IPrincipal;
@ -77,4 +79,21 @@ public abstract class BaseResource {
Set<String> baseFunctionSet = ((UserPrincipal) principal).getBaseFunctionSet();
return baseFunctionSet != null && baseFunctionSet.contains(baseFunction.name());
}
/**
* Returns a list of ACL target ID.
*
* @param shareId Share ID (optional)
* @return List of ACL target ID
*/
protected List<String> getTargetIdList(String shareId) {
List<String> targetIdList = Lists.newArrayList(principal.getGroupIdList());
if (principal.getId() != null) {
targetIdList.add(principal.getId());
}
if (shareId != null) {
targetIdList.add(shareId);
}
return targetIdList;
}
}

View File

@ -51,7 +51,7 @@ public class CommentResource extends BaseResource {
// Read access on doc gives access to write comments
DocumentDao documentDao = new DocumentDao();
if (documentDao.getDocument(documentId, PermType.READ, principal.getId()) == null) {
if (documentDao.getDocument(documentId, PermType.READ, getTargetIdList(null)) == null) {
return Response.status(Status.NOT_FOUND).build();
}
@ -97,7 +97,7 @@ public class CommentResource extends BaseResource {
if (!comment.getUserId().equals(principal.getId())) {
// Get the associated document
DocumentDao documentDao = new DocumentDao();
if (documentDao.getDocument(comment.getDocumentId(), PermType.WRITE, principal.getId()) == null) {
if (documentDao.getDocument(comment.getDocumentId(), PermType.WRITE, getTargetIdList(null)) == null) {
return Response.status(Status.NOT_FOUND).build();
}
}
@ -125,7 +125,7 @@ public class CommentResource extends BaseResource {
// Read access on doc gives access to read comments
DocumentDao documentDao = new DocumentDao();
if (documentDao.getDocument(documentId, PermType.READ, shareId == null ? principal.getId() : shareId) == null) {
if (documentDao.getDocument(documentId, PermType.READ, getTargetIdList(shareId)) == null) {
return Response.status(Status.NOT_FOUND).build();
}

View File

@ -94,7 +94,7 @@ public class DocumentResource extends BaseResource {
DocumentDao documentDao = new DocumentDao();
AclDao aclDao = new AclDao();
DocumentDto documentDto = documentDao.getDocument(documentId, PermType.READ, shareId == null ? principal.getId() : shareId);
DocumentDto documentDto = documentDao.getDocument(documentId, PermType.READ, getTargetIdList(shareId));
if (documentDto == null) {
return Response.status(Status.NOT_FOUND).build();
}
@ -148,7 +148,8 @@ public class DocumentResource extends BaseResource {
.add("type", aclDto.getTargetType()));
if (!principal.isAnonymous()
&& aclDto.getTargetId().equals(principal.getId())
&& (aclDto.getTargetId().equals(principal.getId())
|| principal.getGroupIdList().contains(aclDto.getTargetId()))
&& aclDto.getPerm() == PermType.WRITE) {
// The document is writable for the current user
writable = true;
@ -205,7 +206,7 @@ public class DocumentResource extends BaseResource {
// Get document and check read permission
DocumentDao documentDao = new DocumentDao();
final DocumentDto documentDto = documentDao.getDocument(documentId, PermType.READ, shareId == null ? principal.getId() : shareId);
final DocumentDto documentDto = documentDao.getDocument(documentId, PermType.READ, getTargetIdList(shareId));
if (documentDto == null) {
return Response.status(Status.NOT_FOUND).build();
}
@ -268,7 +269,7 @@ public class DocumentResource extends BaseResource {
PaginatedList<DocumentDto> paginatedList = PaginatedLists.create(limit, offset);
SortCriteria sortCriteria = new SortCriteria(sortColumn, asc);
DocumentCriteria documentCriteria = parseSearchQuery(search);
documentCriteria.setUserId(principal.getId());
documentCriteria.setTargetIdList(getTargetIdList(null));
try {
documentDao.findByCriteria(paginatedList, documentCriteria, sortCriteria);
} catch (Exception e) {
@ -564,7 +565,7 @@ public class DocumentResource extends BaseResource {
// Check write permission
AclDao aclDao = new AclDao();
if (!aclDao.checkPermission(id, PermType.WRITE, principal.getId())) {
if (!aclDao.checkPermission(id, PermType.WRITE, getTargetIdList(null))) {
throw new ForbiddenClientException();
}
@ -676,7 +677,7 @@ public class DocumentResource extends BaseResource {
// Get the document
DocumentDao documentDao = new DocumentDao();
FileDao fileDao = new FileDao();
DocumentDto documentDto = documentDao.getDocument(id, PermType.WRITE, principal.getId());
DocumentDto documentDto = documentDao.getDocument(id, PermType.WRITE, getTargetIdList(null));
if (documentDto == null) {
return Response.status(Status.NOT_FOUND).build();
}

View File

@ -98,7 +98,7 @@ public class FileResource extends BaseResource {
documentId = null;
} else {
DocumentDao documentDao = new DocumentDao();
documentDto = documentDao.getDocument(documentId, PermType.WRITE, principal.getId());
documentDto = documentDao.getDocument(documentId, PermType.WRITE, getTargetIdList(null));
if (documentDto == null) {
return Response.status(Status.NOT_FOUND).build();
}
@ -213,7 +213,7 @@ public class FileResource extends BaseResource {
DocumentDao documentDao = new DocumentDao();
FileDao fileDao = new FileDao();
File file = fileDao.getFile(id, principal.getId());
DocumentDto documentDto = documentDao.getDocument(documentId, PermType.WRITE, principal.getId());
DocumentDto documentDto = documentDao.getDocument(documentId, PermType.WRITE, getTargetIdList(null));
if (file == null || documentDto == null) {
return Response.status(Status.NOT_FOUND).build();
}
@ -276,7 +276,7 @@ public class FileResource extends BaseResource {
// Get the document
DocumentDao documentDao = new DocumentDao();
if (documentDao.getDocument(documentId, PermType.WRITE, principal.getId()) == null) {
if (documentDao.getDocument(documentId, PermType.WRITE, getTargetIdList(null)) == null) {
return Response.status(Status.NOT_FOUND).build();
}
@ -312,7 +312,7 @@ public class FileResource extends BaseResource {
// Check document visibility
if (documentId != null) {
AclDao aclDao = new AclDao();
if (!aclDao.checkPermission(documentId, PermType.READ, shareId == null ? principal.getId() : shareId)) {
if (!aclDao.checkPermission(documentId, PermType.READ, getTargetIdList(shareId))) {
return Response.status(Status.NOT_FOUND).build();
}
} else if (!authenticated) {
@ -370,7 +370,7 @@ public class FileResource extends BaseResource {
// But not ours
throw new ForbiddenClientException();
}
} else if ((documentDto = documentDao.getDocument(file.getDocumentId(), PermType.WRITE, principal.getId())) == null) {
} else if ((documentDto = documentDao.getDocument(file.getDocumentId(), PermType.WRITE, getTargetIdList(null))) == null) {
return Response.status(Status.NOT_FOUND).build();
}
@ -445,7 +445,7 @@ public class FileResource extends BaseResource {
} else {
// Check document accessibility
AclDao aclDao = new AclDao();
if (!aclDao.checkPermission(file.getDocumentId(), PermType.READ, shareId == null ? principal.getId() : shareId)) {
if (!aclDao.checkPermission(file.getDocumentId(), PermType.READ, getTargetIdList(shareId))) {
throw new ForbiddenClientException();
}
}
@ -519,7 +519,7 @@ public class FileResource extends BaseResource {
// Get the document
DocumentDao documentDao = new DocumentDao();
DocumentDto documentDto = documentDao.getDocument(documentId, PermType.READ, shareId == null ? principal.getId() : shareId);
DocumentDto documentDto = documentDao.getDocument(documentId, PermType.READ, getTargetIdList(shareId));
if (documentDto == null) {
return Response.status(Status.NOT_FOUND).build();
}

View File

@ -53,7 +53,7 @@ public class ShareResource extends BaseResource {
// Get the document
DocumentDao documentDao = new DocumentDao();
if (documentDao.getDocument(documentId, PermType.WRITE, principal.getId()) == null) {
if (documentDao.getDocument(documentId, PermType.WRITE, getTargetIdList(null)) == null) {
return Response.status(Status.NOT_FOUND).build();
}
@ -102,7 +102,7 @@ public class ShareResource extends BaseResource {
}
Acl acl = aclList.get(0);
if (!aclDao.checkPermission(acl.getSourceId(), PermType.WRITE, principal.getId())) {
if (!aclDao.checkPermission(acl.getSourceId(), PermType.WRITE, getTargetIdList(null))) {
throw new ClientException("DocumentNotFound", MessageFormat.format("Document not found: {0}", acl.getSourceId()));
}