mirror of
https://github.com/sismics/docs.git
synced 2024-11-25 15:17:57 +01:00
Closes #252: route model permissions
This commit is contained in:
parent
dc5a157dad
commit
4910dfd527
@ -6,6 +6,7 @@ import com.sismics.docs.core.dao.criteria.RouteModelCriteria;
|
||||
import com.sismics.docs.core.dao.dto.RouteModelDto;
|
||||
import com.sismics.docs.core.model.jpa.RouteModel;
|
||||
import com.sismics.docs.core.util.AuditLogUtil;
|
||||
import com.sismics.docs.core.util.SecurityUtil;
|
||||
import com.sismics.docs.core.util.jpa.QueryParam;
|
||||
import com.sismics.docs.core.util.jpa.QueryUtil;
|
||||
import com.sismics.docs.core.util.jpa.SortCriteria;
|
||||
@ -124,6 +125,12 @@ public class RouteModelDao {
|
||||
sb.append(" from T_ROUTE_MODEL rm ");
|
||||
|
||||
// Add search criterias
|
||||
if (criteria.getTargetIdList() != null && !SecurityUtil.skipAclCheck(criteria.getTargetIdList())) {
|
||||
sb.append(" left join T_ACL a on a.ACL_TARGETID_C in (:targetIdList) and a.ACL_SOURCEID_C = rm.RTM_ID_C and a.ACL_PERM_C = 'READ' and a.ACL_DELETEDATE_D is null ");
|
||||
criteriaList.add("a.ACL_ID_C is not null");
|
||||
parameterMap.put("targetIdList", criteria.getTargetIdList());
|
||||
}
|
||||
|
||||
criteriaList.add("rm.RTM_DELETEDATE_D is null");
|
||||
|
||||
if (!criteriaList.isEmpty()) {
|
||||
|
@ -1,10 +1,25 @@
|
||||
package com.sismics.docs.core.dao.criteria;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Route model criteria.
|
||||
*
|
||||
* @author bgamard
|
||||
*/
|
||||
public class RouteModelCriteria {
|
||||
/**
|
||||
* ACL target ID list.
|
||||
*/
|
||||
private List<String> targetIdList;
|
||||
|
||||
public List<String> getTargetIdList() {
|
||||
return targetIdList;
|
||||
}
|
||||
|
||||
public RouteModelCriteria setTargetIdList(List<String> targetIdList) {
|
||||
this.targetIdList = targetIdList;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
@ -23,11 +23,6 @@ public class TagCriteria {
|
||||
*/
|
||||
private String documentId;
|
||||
|
||||
/**
|
||||
* Tag name.
|
||||
*/
|
||||
private String name;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
@ -7,5 +7,7 @@ alter table T_ROUTE_STEP add constraint FK_RTP_IDROUTE_C foreign key (RTP_IDROUT
|
||||
alter table T_ROUTE_STEP add constraint FK_RTP_IDVALIDATORUSER_C foreign key (RTP_IDVALIDATORUSER_C) references T_USER (USE_ID_C) on delete restrict on update restrict;
|
||||
|
||||
insert into T_ROUTE_MODEL (RTM_ID_C, RTM_NAME_C, RTM_STEPS_C, RTM_CREATEDATE_D) values ('default-document-review', 'Document review', '[{"type":"VALIDATE","target":{"name":"administrators","type":"GROUP"},"name":"Check the document''s metadata"},{"type":"VALIDATE","target":{"name":"administrators","type":"GROUP"},"name":"Add relevant files to the document"},{"type":"APPROVE","target":{"name":"administrators","type":"GROUP"},"name":"Approve the document"}]', now());
|
||||
insert into T_ACL (ACL_ID_C, ACL_PERM_C, ACL_SOURCEID_C, ACL_TARGETID_C) values ('acl-admin-default-route-read', 'READ', 'default-document-review', 'administrators');
|
||||
insert into T_ACL (ACL_ID_C, ACL_PERM_C, ACL_SOURCEID_C, ACL_TARGETID_C) values ('acl-admin-default-route-write', 'WRITE', 'default-document-review', 'administrators');
|
||||
|
||||
update T_CONFIG set CFG_VALUE_C = '15' where CFG_ID_C = 'DB_VERSION';
|
||||
|
@ -1,16 +1,14 @@
|
||||
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.ActionType;
|
||||
import com.sismics.docs.core.constant.RouteStepTransition;
|
||||
import com.sismics.docs.core.constant.RouteStepType;
|
||||
import com.sismics.docs.core.constant.*;
|
||||
import com.sismics.docs.core.dao.AclDao;
|
||||
import com.sismics.docs.core.dao.GroupDao;
|
||||
import com.sismics.docs.core.dao.RouteModelDao;
|
||||
import com.sismics.docs.core.dao.TagDao;
|
||||
import com.sismics.docs.core.dao.UserDao;
|
||||
import com.sismics.docs.core.dao.criteria.RouteModelCriteria;
|
||||
import com.sismics.docs.core.dao.dto.RouteModelDto;
|
||||
import com.sismics.docs.core.model.jpa.Acl;
|
||||
import com.sismics.docs.core.model.jpa.Group;
|
||||
import com.sismics.docs.core.model.jpa.RouteModel;
|
||||
import com.sismics.docs.core.model.jpa.User;
|
||||
@ -19,6 +17,7 @@ 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.AclUtil;
|
||||
import com.sismics.rest.util.ValidationUtil;
|
||||
|
||||
import javax.json.*;
|
||||
@ -64,7 +63,7 @@ public class RouteModelResource extends BaseResource {
|
||||
SortCriteria sortCriteria = new SortCriteria(sortColumn, asc);
|
||||
|
||||
RouteModelDao routeModelDao = new RouteModelDao();
|
||||
List<RouteModelDto> routeModelDtoList = routeModelDao.findByCriteria(new RouteModelCriteria(), sortCriteria);
|
||||
List<RouteModelDto> routeModelDtoList = routeModelDao.findByCriteria(new RouteModelCriteria().setTargetIdList(getTargetIdList(null)), sortCriteria);
|
||||
for (RouteModelDto routeModelDto : routeModelDtoList) {
|
||||
routeModels.add(Json.createObjectBuilder()
|
||||
.add("id", routeModelDto.getId())
|
||||
@ -111,6 +110,23 @@ public class RouteModelResource extends BaseResource {
|
||||
.setName(name)
|
||||
.setSteps(steps), principal.getId());
|
||||
|
||||
// Create read ACL
|
||||
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());
|
||||
|
||||
// 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());
|
||||
|
||||
// Always return OK
|
||||
JsonObjectBuilder response = Json.createObjectBuilder()
|
||||
.add("id", id);
|
||||
@ -125,7 +141,6 @@ public class RouteModelResource extends BaseResource {
|
||||
private void validateRouteModelSteps(String steps) {
|
||||
UserDao userDao = new UserDao();
|
||||
GroupDao groupDao = new GroupDao();
|
||||
TagDao tagDao = new TagDao();
|
||||
|
||||
try (JsonReader reader = Json.createReader(new StringReader(steps))) {
|
||||
JsonArray stepsJson = reader.readArray();
|
||||
@ -374,6 +389,9 @@ public class RouteModelResource extends BaseResource {
|
||||
.add("create_date", routeModel.getCreateDate().getTime())
|
||||
.add("steps", routeModel.getSteps());
|
||||
|
||||
// Add ACL
|
||||
AclUtil.addAcls(response, id, getTargetIdList(null));
|
||||
|
||||
return Response.ok().entity(response.build()).build();
|
||||
}
|
||||
}
|
||||
|
@ -70,6 +70,11 @@ public class RouteResource extends BaseResource {
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
// Check permission on this route model
|
||||
if (!aclDao.checkPermission(routeModelId, PermType.READ, getTargetIdList(null))) {
|
||||
throw new ForbiddenClientException();
|
||||
}
|
||||
|
||||
// Avoid creating 2 running routes on the same document
|
||||
RouteStepDao routeStepDao = new RouteStepDao();
|
||||
if (routeStepDao.getCurrentStep(documentId) != null) {
|
||||
|
@ -59,26 +59,6 @@ angular.module('docs').controller('SettingsWorkflowEdit', function($scope, $dial
|
||||
return $stateParams.id;
|
||||
};
|
||||
|
||||
/**
|
||||
* In edit mode, load the current workflow.
|
||||
*/
|
||||
if ($scope.isEdit()) {
|
||||
Restangular.one('routemodel', $stateParams.id).get().then(function (data) {
|
||||
$scope.workflow = data;
|
||||
$scope.workflow.steps = JSON.parse(data.steps);
|
||||
_.each($scope.workflow.steps, function (step) {
|
||||
if (!step.transitions) {
|
||||
// Patch for old route models
|
||||
$scope.updateTransitions(step);
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
$scope.workflow = {
|
||||
steps: []
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the current workflow.
|
||||
*/
|
||||
@ -188,4 +168,25 @@ angular.module('docs').controller('SettingsWorkflowEdit', function($scope, $dial
|
||||
Restangular.one('tag/list').get().then(function(data) {
|
||||
$scope.tags = data.tags;
|
||||
});
|
||||
|
||||
/**
|
||||
* In edit mode, load the current workflow.
|
||||
*/
|
||||
if ($scope.isEdit()) {
|
||||
Restangular.one('routemodel', $stateParams.id).get().then(function (data) {
|
||||
$scope.workflow = data;
|
||||
$scope.workflow.steps = JSON.parse(data.steps);
|
||||
_.each($scope.workflow.steps, function (step) {
|
||||
if (!step.transitions) {
|
||||
// Patch for old route models
|
||||
$scope.updateTransitions(step);
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
$scope.workflow = {
|
||||
steps: []
|
||||
};
|
||||
$scope.addStep();
|
||||
}
|
||||
});
|
@ -311,7 +311,8 @@
|
||||
"target_help": "<strong>Approve:</strong> Accept or reject the review<br/><strong>Validate:</strong> Review and continue the workflow",
|
||||
"add_step": "Add a workflow step",
|
||||
"actions": "What happens after?",
|
||||
"remove_action": "Remove action"
|
||||
"remove_action": "Remove action",
|
||||
"acl_info": "Only users and groups defined here will be able to start this workflow on a document"
|
||||
}
|
||||
},
|
||||
"security": {
|
||||
|
@ -136,6 +136,16 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-default mt-30" ng-show="isEdit()">
|
||||
<div class="panel-body">
|
||||
<p translate="settings.workflow.edit.acl_info"></p>
|
||||
<acl-edit source="workflow.id"
|
||||
acls="workflow.acls"
|
||||
writable="workflow.writable"
|
||||
creator="workflow.creator"></acl-edit>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="clearfix form-group mt-10">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<button type="submit" class="btn btn-primary" ng-click="edit()" ng-disabled="!editWorkflowForm.$valid">
|
||||
|
@ -607,6 +607,10 @@ input[readonly].share-link {
|
||||
margin-bottom: 19px;
|
||||
}
|
||||
|
||||
.mt-30 {
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.ml-10 {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
@ -24,6 +24,10 @@ public class TestRouteModelResource extends BaseJerseyTest {
|
||||
// Login admin
|
||||
String adminToken = clientUtil.login("admin", "admin", false);
|
||||
|
||||
// Login routeModel1
|
||||
clientUtil.createUser("routeModel1");
|
||||
String routeModel1Token = clientUtil.login("routeModel1");
|
||||
|
||||
// Create a tag
|
||||
JsonObject json = target().path("/tag").request()
|
||||
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, adminToken)
|
||||
@ -32,7 +36,7 @@ public class TestRouteModelResource extends BaseJerseyTest {
|
||||
.param("color", "#ff0000")), JsonObject.class);
|
||||
String tagRouteId = json.getString("id");
|
||||
|
||||
// Get all route models
|
||||
// Get all route models with admin
|
||||
json = target().path("/routemodel")
|
||||
.queryParam("sort_column", "2")
|
||||
.queryParam("asc", "false")
|
||||
@ -50,7 +54,7 @@ public class TestRouteModelResource extends BaseJerseyTest {
|
||||
.param("steps", "[{\"type\":\"VALIDATE\",\"transitions\":[{\"name\":\"VALIDATED\",\"actions\":[]}],\"target\":{\"name\":\"administrators\",\"type\":\"GROUP\"},\"name\":\"Check the document's metadata\"}]")), JsonObject.class);
|
||||
String routeModelId = json.getString("id");
|
||||
|
||||
// Get all route models
|
||||
// Get all route models with admin
|
||||
json = target().path("/routemodel")
|
||||
.queryParam("sort_column", "2")
|
||||
.queryParam("asc", "false")
|
||||
@ -62,6 +66,35 @@ public class TestRouteModelResource extends BaseJerseyTest {
|
||||
Assert.assertEquals(routeModelId, routeModels.getJsonObject(0).getString("id"));
|
||||
Assert.assertEquals("Workflow validation 1", routeModels.getJsonObject(0).getString("name"));
|
||||
|
||||
// Get all route models with routeModel1
|
||||
json = target().path("/routemodel")
|
||||
.queryParam("sort_column", "2")
|
||||
.queryParam("asc", "false")
|
||||
.request()
|
||||
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, routeModel1Token)
|
||||
.get(JsonObject.class);
|
||||
routeModels = json.getJsonArray("routemodels");
|
||||
Assert.assertEquals(0, routeModels.size());
|
||||
|
||||
// Add an ACL READ for routeModel1 with admin
|
||||
json = target().path("/acl").request()
|
||||
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, adminToken)
|
||||
.put(Entity.form(new Form()
|
||||
.param("source", routeModelId)
|
||||
.param("perm", "READ")
|
||||
.param("target", "routeModel1")
|
||||
.param("type", "USER")), JsonObject.class);
|
||||
|
||||
// Get all route models with routeModel1
|
||||
json = target().path("/routemodel")
|
||||
.queryParam("sort_column", "2")
|
||||
.queryParam("asc", "false")
|
||||
.request()
|
||||
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, routeModel1Token)
|
||||
.get(JsonObject.class);
|
||||
routeModels = json.getJsonArray("routemodels");
|
||||
Assert.assertEquals(1, routeModels.size());
|
||||
|
||||
// Get the route model
|
||||
json = target().path("/routemodel/" + routeModelId)
|
||||
.request()
|
||||
@ -70,6 +103,8 @@ public class TestRouteModelResource extends BaseJerseyTest {
|
||||
Assert.assertEquals(routeModelId, json.getString("id"));
|
||||
Assert.assertEquals("Workflow validation 1", json.getString("name"));
|
||||
Assert.assertEquals("[{\"type\":\"VALIDATE\",\"transitions\":[{\"name\":\"VALIDATED\",\"actions\":[]}],\"target\":{\"name\":\"administrators\",\"type\":\"GROUP\"},\"name\":\"Check the document's metadata\"}]", json.getString("steps"));
|
||||
JsonArray acls = json.getJsonArray("acls");
|
||||
Assert.assertEquals(3, acls.size());// 2 for admin, 1 for routeModel1
|
||||
|
||||
// Update the route model with actions
|
||||
target().path("/routemodel/" + routeModelId).request()
|
||||
|
Loading…
Reference in New Issue
Block a user