Closes #252: route model permissions

This commit is contained in:
Benjamin Gamard 2018-10-28 17:03:21 +01:00
parent dc5a157dad
commit 4910dfd527
11 changed files with 128 additions and 35 deletions

View File

@ -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()) {

View File

@ -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;
}
}

View File

@ -23,11 +23,6 @@ public class TagCriteria {
*/
private String documentId;
/**
* Tag name.
*/
private String name;
public String getId() {
return id;
}

View File

@ -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';

View File

@ -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();
}
}

View File

@ -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) {

View File

@ -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();
}
});

View File

@ -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": {

View File

@ -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">

View File

@ -607,6 +607,10 @@ input[readonly].share-link {
margin-bottom: 19px;
}
.mt-30 {
margin-top: 30px;
}
.ml-10 {
margin-left: 10px;
}

View File

@ -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()