diff --git a/docs-core/src/main/java/com/sismics/docs/core/constant/AclType.java b/docs-core/src/main/java/com/sismics/docs/core/constant/AclType.java new file mode 100644 index 00000000..507b2b82 --- /dev/null +++ b/docs-core/src/main/java/com/sismics/docs/core/constant/AclType.java @@ -0,0 +1,18 @@ +package com.sismics.docs.core.constant; + +/** + * ACL type. + * + * @author bgamard + */ +public enum AclType { + /** + * User created ACL. + */ + USER, + + /** + * ACL created by the routing module. + */ + ROUTING +} diff --git a/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/AclDao.java b/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/AclDao.java index 7ecbf61c..7ed02650 100644 --- a/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/AclDao.java +++ b/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/AclDao.java @@ -1,14 +1,7 @@ package com.sismics.docs.core.dao.jpa; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.UUID; - -import javax.persistence.EntityManager; -import javax.persistence.Query; - import com.sismics.docs.core.constant.AclTargetType; +import com.sismics.docs.core.constant.AclType; import com.sismics.docs.core.constant.AuditLogType; import com.sismics.docs.core.constant.PermType; import com.sismics.docs.core.dao.jpa.dto.AclDto; @@ -16,6 +9,13 @@ import com.sismics.docs.core.model.jpa.Acl; import com.sismics.docs.core.util.AuditLogUtil; import com.sismics.util.context.ThreadLocalContext; +import javax.persistence.EntityManager; +import javax.persistence.Query; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.UUID; + /** * ACL DAO. * @@ -54,7 +54,7 @@ public class AclDao { EntityManager em = ThreadLocalContext.get().getEntityManager(); Query q = em.createQuery("select a from Acl a where a.targetId = :targetId and a.deleteDate is null"); q.setParameter("targetId", targetId); - + return q.getResultList(); } @@ -65,7 +65,7 @@ public class AclDao { * @return ACL DTO list */ @SuppressWarnings("unchecked") - public List getBySourceId(String sourceId) { + public List getBySourceId(String sourceId, AclType type) { EntityManager em = ThreadLocalContext.get().getEntityManager(); StringBuilder sb = new StringBuilder("select a.ACL_ID_C, a.ACL_PERM_C, a.ACL_TARGETID_C, "); sb.append(" u.USE_USERNAME_C, s.SHA_ID_C, s.SHA_NAME_C, g.GRP_NAME_C "); @@ -73,11 +73,12 @@ public class AclDao { sb.append(" left join T_USER u on u.USE_ID_C = a.ACL_TARGETID_C "); sb.append(" left join T_SHARE s on s.SHA_ID_C = a.ACL_TARGETID_C "); sb.append(" left join T_GROUP g on g.GRP_ID_C = a.ACL_TARGETID_C "); - sb.append(" where a.ACL_DELETEDATE_D is null and a.ACL_SOURCEID_C = :sourceId "); + sb.append(" where a.ACL_DELETEDATE_D is null and a.ACL_SOURCEID_C = :sourceId and a.ACL_TYPE_C = :type "); // Perform the query Query q = em.createNativeQuery(sb.toString()); q.setParameter("sourceId", sourceId); + q.setParameter("type", type.name()); List l = q.getResultList(); // Assemble results @@ -141,26 +142,29 @@ public class AclDao { * @param perm Permission * @param targetId Target ID * @param userId User ID + * @param type Type */ @SuppressWarnings("unchecked") - public void delete(String sourceId, PermType perm, String targetId, String userId) { + public void delete(String sourceId, PermType perm, String targetId, String userId, AclType type) { EntityManager em = ThreadLocalContext.get().getEntityManager(); // Create audit log - Query q = em.createQuery("from Acl a where a.sourceId = :sourceId and a.perm = :perm and a.targetId = :targetId"); + Query q = em.createQuery("from Acl a where a.sourceId = :sourceId and a.perm = :perm and a.targetId = :targetId and a.type = :type"); q.setParameter("sourceId", sourceId); q.setParameter("perm", perm); q.setParameter("targetId", targetId); + q.setParameter("type", type); List aclList = q.getResultList(); for (Acl acl : aclList) { AuditLogUtil.create(acl, AuditLogType.DELETE, userId); } // Soft delete the ACLs - q = em.createQuery("update Acl a set a.deleteDate = :dateNow where a.sourceId = :sourceId and a.perm = :perm and a.targetId = :targetId"); + q = em.createQuery("update Acl a set a.deleteDate = :dateNow where a.sourceId = :sourceId and a.perm = :perm and a.targetId = :targetId and a.type = :type"); q.setParameter("sourceId", sourceId); q.setParameter("perm", perm); q.setParameter("targetId", targetId); + q.setParameter("type", type); q.setParameter("dateNow", new Date()); q.executeUpdate(); } diff --git a/docs-core/src/main/java/com/sismics/docs/core/model/jpa/Acl.java b/docs-core/src/main/java/com/sismics/docs/core/model/jpa/Acl.java index 06d9eabc..ba5baeec 100644 --- a/docs-core/src/main/java/com/sismics/docs/core/model/jpa/Acl.java +++ b/docs-core/src/main/java/com/sismics/docs/core/model/jpa/Acl.java @@ -1,6 +1,7 @@ package com.sismics.docs.core.model.jpa; import com.google.common.base.MoreObjects; +import com.sismics.docs.core.constant.AclType; import com.sismics.docs.core.constant.PermType; import javax.persistence.*; @@ -28,7 +29,12 @@ public class Acl implements Loggable { @Enumerated(EnumType.STRING) private PermType perm; - // TODO Add ACL type enum: USER, ROUTING + /** + * ACL type. + */ + @Column(name = "ACL_TYPE_C", length = 30, nullable = false) + @Enumerated(EnumType.STRING) + private AclType type; /** * ACL source ID. @@ -79,7 +85,16 @@ public class Acl implements Loggable { public void setTargetId(String targetId) { this.targetId = targetId; } - + + public AclType getType() { + return type; + } + + public Acl setType(AclType type) { + this.type = type; + return this; + } + @Override public Date getDeleteDate() { return deleteDate; @@ -96,6 +111,7 @@ public class Acl implements Loggable { .add("perm", perm) .add("sourceId", sourceId) .add("targetId", targetId) + .add("type", type) .toString(); } diff --git a/docs-core/src/main/java/com/sismics/docs/core/util/RoutingUtil.java b/docs-core/src/main/java/com/sismics/docs/core/util/RoutingUtil.java index 45c8c23c..3c4cdf99 100644 --- a/docs-core/src/main/java/com/sismics/docs/core/util/RoutingUtil.java +++ b/docs-core/src/main/java/com/sismics/docs/core/util/RoutingUtil.java @@ -1,7 +1,11 @@ package com.sismics.docs.core.util; +import com.sismics.docs.core.constant.AclType; +import com.sismics.docs.core.constant.PermType; +import com.sismics.docs.core.dao.jpa.AclDao; import com.sismics.docs.core.dao.jpa.RouteDao; import com.sismics.docs.core.dao.jpa.RouteStepDao; +import com.sismics.docs.core.model.jpa.Acl; import com.sismics.docs.core.model.jpa.Route; import com.sismics.docs.core.model.jpa.RouteStep; @@ -38,4 +42,29 @@ public class RoutingUtil { return null; } + + /** + * Update routing ACLs according to the current route step. + * + * @param sourceId Source ID + * @param currentStep Current route step + * @param previousStep Previous route step + * @param userId User ID + */ + public static void updateAcl(String sourceId, RouteStep currentStep, RouteStep previousStep, String userId) { + AclDao aclDao = new AclDao(); + + if (previousStep != null) { + // Remove the previous ACL + aclDao.delete(sourceId, PermType.READ, previousStep.getTargetId(), userId, AclType.ROUTING); + } + + // Create a temporary READ ACL + Acl acl = new Acl(); + acl.setPerm(PermType.READ); + acl.setType(AclType.ROUTING); + acl.setSourceId(sourceId); + acl.setTargetId(currentStep.getTargetId()); + aclDao.create(acl, userId); + } } diff --git a/docs-core/src/main/resources/db/update/dbupdate-015-0.sql b/docs-core/src/main/resources/db/update/dbupdate-015-0.sql index 11ea1fd4..82210515 100644 --- a/docs-core/src/main/resources/db/update/dbupdate-015-0.sql +++ b/docs-core/src/main/resources/db/update/dbupdate-015-0.sql @@ -1,6 +1,7 @@ create table T_ROUTE_MODEL ( RTM_ID_C varchar(36) not null, RTM_NAME_C varchar(50) not null, RTM_STEPS_C varchar(5000) not null, RTM_CREATEDATE_D datetime not null, RTM_DELETEDATE_D datetime, primary key (RTM_ID_C) ); create cached table T_ROUTE ( RTE_ID_C varchar(36) not null, RTE_IDDOCUMENT_C varchar(36) not null, RTE_NAME_C varchar(50) not null, RTE_CREATEDATE_D datetime not null, RTE_DELETEDATE_D datetime, primary key (RTE_ID_C) ); -create cached table T_ROUTE_STEP ( RTP_ID_C varchar(36) not null, RTP_IDROUTE_C varchar(36) not null, RTP_NAME_C varchar(200) not null, RTP_TYPE_C varchar(50) not null, RTP_TRANSITION_C varchar(50), RTP_COMMENT_C varchar(500), RTP_IDTARGET_C varchar(36) not null, RTP_ORDER_N int not null, RTP_CREATEDATE_D datetime not null, RTP_ENDDATE_D datetime, RTP_DELETEDATE_D datetime, primary key (RTP_ID_C) );; +create cached table T_ROUTE_STEP ( RTP_ID_C varchar(36) not null, RTP_IDROUTE_C varchar(36) not null, RTP_NAME_C varchar(200) not null, RTP_TYPE_C varchar(50) not null, RTP_TRANSITION_C varchar(50), RTP_COMMENT_C varchar(500), RTP_IDTARGET_C varchar(36) not null, RTP_ORDER_N int not null, RTP_CREATEDATE_D datetime not null, RTP_ENDDATE_D datetime, RTP_DELETEDATE_D datetime, primary key (RTP_ID_C) ); +alter table T_ACL add column ACL_TYPE_C varchar(30) not null default 'USER'; alter table T_ROUTE add constraint FK_RTE_IDDOCUMENT_C foreign key (RTE_IDDOCUMENT_C) references T_DOCUMENT (DOC_ID_C) on delete restrict on update restrict; alter table T_ROUTE_STEP add constraint FK_RTP_IDROUTE_C foreign key (RTP_IDROUTE_C) references T_ROUTE (RTE_ID_C) on delete restrict on update restrict; diff --git a/docs-web-common/src/main/java/com/sismics/rest/util/AclUtil.java b/docs-web-common/src/main/java/com/sismics/rest/util/AclUtil.java index e9de8794..74e73012 100644 --- a/docs-web-common/src/main/java/com/sismics/rest/util/AclUtil.java +++ b/docs-web-common/src/main/java/com/sismics/rest/util/AclUtil.java @@ -1,5 +1,6 @@ package com.sismics.rest.util; +import com.sismics.docs.core.constant.AclType; import com.sismics.docs.core.constant.PermType; import com.sismics.docs.core.dao.jpa.AclDao; import com.sismics.docs.core.dao.jpa.dto.AclDto; @@ -24,7 +25,7 @@ public class AclUtil { */ public static void addAcls(JsonObjectBuilder json, String sourceId, List targetIdList) { AclDao aclDao = new AclDao(); - List aclDtoList = aclDao.getBySourceId(sourceId); + List aclDtoList = aclDao.getBySourceId(sourceId, AclType.USER); JsonArrayBuilder aclList = Json.createArrayBuilder(); for (AclDto aclDto : aclDtoList) { aclList.add(Json.createObjectBuilder() diff --git a/docs-web/src/main/java/com/sismics/docs/rest/resource/AclResource.java b/docs-web/src/main/java/com/sismics/docs/rest/resource/AclResource.java index 2a5bc933..298d25fe 100644 --- a/docs-web/src/main/java/com/sismics/docs/rest/resource/AclResource.java +++ b/docs-web/src/main/java/com/sismics/docs/rest/resource/AclResource.java @@ -2,6 +2,7 @@ package com.sismics.docs.rest.resource; import com.google.common.collect.Lists; import com.sismics.docs.core.constant.AclTargetType; +import com.sismics.docs.core.constant.AclType; import com.sismics.docs.core.constant.PermType; import com.sismics.docs.core.dao.jpa.*; import com.sismics.docs.core.dao.jpa.criteria.GroupCriteria; @@ -90,6 +91,7 @@ public class AclResource extends BaseResource { acl.setSourceId(sourceId); acl.setPerm(perm); acl.setTargetId(targetId); + acl.setType(AclType.USER); // Avoid duplicates if (!aclDao.checkPermission(acl.getSourceId(), acl.getPerm(), Lists.newArrayList(acl.getTargetId()))) { @@ -164,7 +166,7 @@ public class AclResource extends BaseResource { } // Delete the ACL - aclDao.delete(sourceId, perm, targetId, principal.getId()); + aclDao.delete(sourceId, perm, targetId, principal.getId(), AclType.USER); // Always return OK JsonObjectBuilder response = Json.createObjectBuilder() diff --git a/docs-web/src/main/java/com/sismics/docs/rest/resource/DocumentResource.java b/docs-web/src/main/java/com/sismics/docs/rest/resource/DocumentResource.java index 33d399ba..dfab01fe 100644 --- a/docs-web/src/main/java/com/sismics/docs/rest/resource/DocumentResource.java +++ b/docs-web/src/main/java/com/sismics/docs/rest/resource/DocumentResource.java @@ -2,6 +2,7 @@ package com.sismics.docs.rest.resource; import com.google.common.base.Joiner; import com.google.common.base.Strings; +import com.sismics.docs.core.constant.AclType; import com.sismics.docs.core.constant.Constants; import com.sismics.docs.core.constant.PermType; import com.sismics.docs.core.dao.jpa.*; @@ -173,7 +174,7 @@ public class DocumentResource extends BaseResource { JsonArrayBuilder aclList = Json.createArrayBuilder(); for (TagDto tagDto : tagDtoList) { AclDao aclDao = new AclDao(); - List aclDtoList = aclDao.getBySourceId(tagDto.getId()); + List aclDtoList = aclDao.getBySourceId(tagDto.getId(), AclType.USER); for (AclDto aclDto : aclDtoList) { aclList.add(Json.createObjectBuilder() .add("perm", aclDto.getPerm().name()) @@ -618,6 +619,7 @@ public class DocumentResource extends BaseResource { AclDao aclDao = new AclDao(); Acl acl = new Acl(); acl.setPerm(PermType.READ); + acl.setType(AclType.USER); acl.setSourceId(documentId); acl.setTargetId(principal.getId()); aclDao.create(acl, principal.getId()); @@ -625,6 +627,7 @@ public class DocumentResource extends BaseResource { // Create write ACL acl = new Acl(); acl.setPerm(PermType.WRITE); + acl.setType(AclType.USER); acl.setSourceId(documentId); acl.setTargetId(principal.getId()); aclDao.create(acl, principal.getId()); diff --git a/docs-web/src/main/java/com/sismics/docs/rest/resource/RouteResource.java b/docs-web/src/main/java/com/sismics/docs/rest/resource/RouteResource.java index 56a78a5d..379352e5 100644 --- a/docs-web/src/main/java/com/sismics/docs/rest/resource/RouteResource.java +++ b/docs-web/src/main/java/com/sismics/docs/rest/resource/RouteResource.java @@ -10,6 +10,7 @@ import com.sismics.docs.core.dao.jpa.RouteStepDao; import com.sismics.docs.core.model.jpa.Route; import com.sismics.docs.core.model.jpa.RouteModel; import com.sismics.docs.core.model.jpa.RouteStep; +import com.sismics.docs.core.util.RoutingUtil; import com.sismics.docs.core.util.SecurityUtil; import com.sismics.rest.exception.ClientException; import com.sismics.rest.exception.ForbiddenClientException; @@ -97,6 +98,12 @@ public class RouteResource extends BaseResource { } routeStepDao.create(routeStep); + + if (i == 0) { + // Initialize ACL on the first step + RoutingUtil.updateAcl(documentId, routeStep, null, principal.getId()); + // TODO Send an email to the targetId users + } } } diff --git a/docs-web/src/main/java/com/sismics/docs/rest/resource/ShareResource.java b/docs-web/src/main/java/com/sismics/docs/rest/resource/ShareResource.java index c50b9094..cc3d28c6 100644 --- a/docs-web/src/main/java/com/sismics/docs/rest/resource/ShareResource.java +++ b/docs-web/src/main/java/com/sismics/docs/rest/resource/ShareResource.java @@ -2,6 +2,7 @@ package com.sismics.docs.rest.resource; import com.sismics.docs.core.constant.AclTargetType; +import com.sismics.docs.core.constant.AclType; import com.sismics.docs.core.constant.PermType; import com.sismics.docs.core.dao.jpa.AclDao; import com.sismics.docs.core.dao.jpa.ShareDao; @@ -76,6 +77,7 @@ public class ShareResource extends BaseResource { Acl acl = new Acl(); acl.setSourceId(documentId); acl.setPerm(PermType.READ); + acl.setType(AclType.USER); acl.setTargetId(share.getId()); aclDao.create(acl, principal.getId()); diff --git a/docs-web/src/main/java/com/sismics/docs/rest/resource/TagResource.java b/docs-web/src/main/java/com/sismics/docs/rest/resource/TagResource.java index 5f310fda..e7459645 100644 --- a/docs-web/src/main/java/com/sismics/docs/rest/resource/TagResource.java +++ b/docs-web/src/main/java/com/sismics/docs/rest/resource/TagResource.java @@ -1,6 +1,7 @@ package com.sismics.docs.rest.resource; import com.google.common.collect.Sets; +import com.sismics.docs.core.constant.AclType; import com.sismics.docs.core.constant.PermType; import com.sismics.docs.core.dao.jpa.AclDao; import com.sismics.docs.core.dao.jpa.TagDao; @@ -205,6 +206,7 @@ public class TagResource extends BaseResource { AclDao aclDao = new AclDao(); Acl acl = new Acl(); acl.setPerm(PermType.READ); + acl.setType(AclType.USER); acl.setSourceId(id); acl.setTargetId(principal.getId()); aclDao.create(acl, principal.getId()); @@ -212,6 +214,7 @@ public class TagResource extends BaseResource { // Create write ACL acl = new Acl(); acl.setPerm(PermType.WRITE); + acl.setType(AclType.USER); acl.setSourceId(id); acl.setTargetId(principal.getId()); aclDao.create(acl, principal.getId()); diff --git a/docs-web/src/test/java/com/sismics/docs/rest/TestRouteResource.java b/docs-web/src/test/java/com/sismics/docs/rest/TestRouteResource.java index bbe95519..5464f51c 100644 --- a/docs-web/src/test/java/com/sismics/docs/rest/TestRouteResource.java +++ b/docs-web/src/test/java/com/sismics/docs/rest/TestRouteResource.java @@ -57,16 +57,6 @@ public class TestRouteResource extends BaseJerseyTest { .param("documentId", document1Id) .param("routeModelId", routeModels.getJsonObject(0).getString("id"))), JsonObject.class); - // Add an ACL READ for admin with route1 - // TODO Remove me when ACLs are automatically added on route step targets - target().path("/acl").request() - .cookie(TokenBasedSecurityFilter.COOKIE_NAME, route1Token) - .put(Entity.form(new Form() - .param("source", document1Id) - .param("perm", "READ") - .param("target", "admin") - .param("type", "USER")), JsonObject.class); - // Get document 1 as route1 json = target().path("/document/" + document1Id).request() .cookie(TokenBasedSecurityFilter.COOKIE_NAME, route1Token)