mirror of
https://github.com/sismics/docs.git
synced 2024-11-22 14:07:55 +01:00
Closes #306: Prevent deleting/renaming users/groups used in route models
This commit is contained in:
parent
cea0d4887d
commit
58bc374e64
@ -4,6 +4,7 @@ import com.google.common.base.Joiner;
|
|||||||
import com.sismics.docs.core.constant.AuditLogType;
|
import com.sismics.docs.core.constant.AuditLogType;
|
||||||
import com.sismics.docs.core.dao.criteria.RouteModelCriteria;
|
import com.sismics.docs.core.dao.criteria.RouteModelCriteria;
|
||||||
import com.sismics.docs.core.dao.dto.RouteModelDto;
|
import com.sismics.docs.core.dao.dto.RouteModelDto;
|
||||||
|
import com.sismics.docs.core.model.jpa.File;
|
||||||
import com.sismics.docs.core.model.jpa.RouteModel;
|
import com.sismics.docs.core.model.jpa.RouteModel;
|
||||||
import com.sismics.docs.core.util.AuditLogUtil;
|
import com.sismics.docs.core.util.AuditLogUtil;
|
||||||
import com.sismics.docs.core.util.SecurityUtil;
|
import com.sismics.docs.core.util.SecurityUtil;
|
||||||
@ -88,6 +89,18 @@ public class RouteModelDao {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the list of all route models.
|
||||||
|
*
|
||||||
|
* @return List of route models
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public List<RouteModel> findAll() {
|
||||||
|
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||||
|
Query q = em.createQuery("select r from RouteModel r where r.deleteDate is null");
|
||||||
|
return q.getResultList();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes a route model.
|
* Deletes a route model.
|
||||||
*
|
*
|
||||||
|
@ -6,6 +6,7 @@ import com.sismics.docs.core.constant.AclType;
|
|||||||
import com.sismics.docs.core.constant.PermType;
|
import com.sismics.docs.core.constant.PermType;
|
||||||
import com.sismics.docs.core.dao.AclDao;
|
import com.sismics.docs.core.dao.AclDao;
|
||||||
import com.sismics.docs.core.dao.DocumentDao;
|
import com.sismics.docs.core.dao.DocumentDao;
|
||||||
|
import com.sismics.docs.core.dao.RouteModelDao;
|
||||||
import com.sismics.docs.core.dao.UserDao;
|
import com.sismics.docs.core.dao.UserDao;
|
||||||
import com.sismics.docs.core.dao.criteria.UserCriteria;
|
import com.sismics.docs.core.dao.criteria.UserCriteria;
|
||||||
import com.sismics.docs.core.dao.dto.RouteStepDto;
|
import com.sismics.docs.core.dao.dto.RouteStepDto;
|
||||||
@ -15,8 +16,14 @@ import com.sismics.docs.core.event.RouteStepValidateEvent;
|
|||||||
import com.sismics.docs.core.model.context.AppContext;
|
import com.sismics.docs.core.model.context.AppContext;
|
||||||
import com.sismics.docs.core.model.jpa.Acl;
|
import com.sismics.docs.core.model.jpa.Acl;
|
||||||
import com.sismics.docs.core.model.jpa.Document;
|
import com.sismics.docs.core.model.jpa.Document;
|
||||||
|
import com.sismics.docs.core.model.jpa.RouteModel;
|
||||||
import com.sismics.util.context.ThreadLocalContext;
|
import com.sismics.util.context.ThreadLocalContext;
|
||||||
|
|
||||||
|
import javax.json.Json;
|
||||||
|
import javax.json.JsonArray;
|
||||||
|
import javax.json.JsonObject;
|
||||||
|
import javax.json.JsonReader;
|
||||||
|
import java.io.StringReader;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -87,4 +94,31 @@ public class RoutingUtil {
|
|||||||
AppContext.getInstance().getMailEventBus().post(routeStepValidateEvent);
|
AppContext.getInstance().getMailEventBus().post(routeStepValidateEvent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the first route model name matching a target type and name.
|
||||||
|
*
|
||||||
|
* @param targetType Target type
|
||||||
|
* @param targetName Target name
|
||||||
|
* @return Route model name or null if none is matching
|
||||||
|
*/
|
||||||
|
public static String findRouteModelNameByTargetName(AclTargetType targetType, String targetName) {
|
||||||
|
RouteModelDao routeModelDao = new RouteModelDao();
|
||||||
|
List<RouteModel> routeModelList = routeModelDao.findAll();
|
||||||
|
for (RouteModel routeModel : routeModelList) {
|
||||||
|
try (JsonReader reader = Json.createReader(new StringReader(routeModel.getSteps()))) {
|
||||||
|
JsonArray stepsJson = reader.readArray();
|
||||||
|
for (int order = 0; order < stepsJson.size(); order++) {
|
||||||
|
JsonObject step = stepsJson.getJsonObject(order);
|
||||||
|
JsonObject target = step.getJsonObject("target");
|
||||||
|
AclTargetType routeTargetType = AclTargetType.valueOf(target.getString("type"));
|
||||||
|
String routeTargetName = target.getString("name");
|
||||||
|
if (targetType == routeTargetType && targetName.equals(routeTargetName)) {
|
||||||
|
return routeModel.getName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package com.sismics.docs.rest.resource;
|
|||||||
|
|
||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
|
import com.sismics.docs.core.constant.AclTargetType;
|
||||||
import com.sismics.docs.core.dao.GroupDao;
|
import com.sismics.docs.core.dao.GroupDao;
|
||||||
import com.sismics.docs.core.dao.RoleBaseFunctionDao;
|
import com.sismics.docs.core.dao.RoleBaseFunctionDao;
|
||||||
import com.sismics.docs.core.dao.UserDao;
|
import com.sismics.docs.core.dao.UserDao;
|
||||||
@ -12,6 +13,7 @@ import com.sismics.docs.core.dao.dto.UserDto;
|
|||||||
import com.sismics.docs.core.model.jpa.Group;
|
import com.sismics.docs.core.model.jpa.Group;
|
||||||
import com.sismics.docs.core.model.jpa.User;
|
import com.sismics.docs.core.model.jpa.User;
|
||||||
import com.sismics.docs.core.model.jpa.UserGroup;
|
import com.sismics.docs.core.model.jpa.UserGroup;
|
||||||
|
import com.sismics.docs.core.util.RoutingUtil;
|
||||||
import com.sismics.docs.core.util.jpa.SortCriteria;
|
import com.sismics.docs.core.util.jpa.SortCriteria;
|
||||||
import com.sismics.docs.rest.constant.BaseFunction;
|
import com.sismics.docs.rest.constant.BaseFunction;
|
||||||
import com.sismics.rest.exception.ClientException;
|
import com.sismics.rest.exception.ClientException;
|
||||||
@ -148,6 +150,14 @@ public class GroupResource extends BaseResource {
|
|||||||
}
|
}
|
||||||
parentId = parentGroup.getId();
|
parentId = parentGroup.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check that this group is not used in any workflow in case of renaming
|
||||||
|
if (!name.equals(groupName)) {
|
||||||
|
String routeModelName = RoutingUtil.findRouteModelNameByTargetName(AclTargetType.GROUP, groupName);
|
||||||
|
if (routeModelName != null) {
|
||||||
|
throw new ClientException("GroupUsedInRouteModel", routeModelName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Update the group
|
// Update the group
|
||||||
groupDao.update(group.setName(name)
|
groupDao.update(group.setName(name)
|
||||||
@ -197,7 +207,13 @@ public class GroupResource extends BaseResource {
|
|||||||
throw new ClientException("ForbiddenError", "The administrators group cannot be deleted");
|
throw new ClientException("ForbiddenError", "The administrators group cannot be deleted");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check that this group is not used in any workflow
|
||||||
|
String routeModelName = RoutingUtil.findRouteModelNameByTargetName(AclTargetType.GROUP, groupName);
|
||||||
|
if (routeModelName != null) {
|
||||||
|
throw new ClientException("GroupUsedInRouteModel", routeModelName);
|
||||||
|
}
|
||||||
|
|
||||||
// Delete the group
|
// Delete the group
|
||||||
groupDao.delete(group.getId(), principal.getId());
|
groupDao.delete(group.getId(), principal.getId());
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ package com.sismics.docs.rest.resource;
|
|||||||
|
|
||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
|
import com.sismics.docs.core.constant.AclTargetType;
|
||||||
import com.sismics.docs.core.constant.ConfigType;
|
import com.sismics.docs.core.constant.ConfigType;
|
||||||
import com.sismics.docs.core.constant.Constants;
|
import com.sismics.docs.core.constant.Constants;
|
||||||
import com.sismics.docs.core.dao.*;
|
import com.sismics.docs.core.dao.*;
|
||||||
@ -15,6 +16,7 @@ import com.sismics.docs.core.event.PasswordLostEvent;
|
|||||||
import com.sismics.docs.core.model.context.AppContext;
|
import com.sismics.docs.core.model.context.AppContext;
|
||||||
import com.sismics.docs.core.model.jpa.*;
|
import com.sismics.docs.core.model.jpa.*;
|
||||||
import com.sismics.docs.core.util.ConfigUtil;
|
import com.sismics.docs.core.util.ConfigUtil;
|
||||||
|
import com.sismics.docs.core.util.RoutingUtil;
|
||||||
import com.sismics.docs.core.util.authentication.AuthenticationUtil;
|
import com.sismics.docs.core.util.authentication.AuthenticationUtil;
|
||||||
import com.sismics.docs.core.util.jpa.SortCriteria;
|
import com.sismics.docs.core.util.jpa.SortCriteria;
|
||||||
import com.sismics.docs.rest.constant.BaseFunction;
|
import com.sismics.docs.rest.constant.BaseFunction;
|
||||||
@ -449,6 +451,12 @@ public class UserResource extends BaseResource {
|
|||||||
if (hasBaseFunction(BaseFunction.ADMIN) || principal.isGuest()) {
|
if (hasBaseFunction(BaseFunction.ADMIN) || principal.isGuest()) {
|
||||||
throw new ClientException("ForbiddenError", "This user cannot be deleted");
|
throw new ClientException("ForbiddenError", "This user cannot be deleted");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check that this user is not used in any workflow
|
||||||
|
String routeModelName = RoutingUtil.findRouteModelNameByTargetName(AclTargetType.USER, principal.getName());
|
||||||
|
if (routeModelName != null) {
|
||||||
|
throw new ClientException("UserUsedInRouteModel", routeModelName);
|
||||||
|
}
|
||||||
|
|
||||||
// Find linked data
|
// Find linked data
|
||||||
DocumentDao documentDao = new DocumentDao();
|
DocumentDao documentDao = new DocumentDao();
|
||||||
@ -512,7 +520,7 @@ public class UserResource extends BaseResource {
|
|||||||
throw new ClientException("ForbiddenError", "The guest user cannot be deleted");
|
throw new ClientException("ForbiddenError", "The guest user cannot be deleted");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the user exists
|
// Check that the user exists
|
||||||
UserDao userDao = new UserDao();
|
UserDao userDao = new UserDao();
|
||||||
User user = userDao.getActiveByUsername(username);
|
User user = userDao.getActiveByUsername(username);
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
@ -525,6 +533,12 @@ public class UserResource extends BaseResource {
|
|||||||
if (baseFunctionSet.contains(BaseFunction.ADMIN.name())) {
|
if (baseFunctionSet.contains(BaseFunction.ADMIN.name())) {
|
||||||
throw new ClientException("ForbiddenError", "The admin user cannot be deleted");
|
throw new ClientException("ForbiddenError", "The admin user cannot be deleted");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check that this user is not used in any workflow
|
||||||
|
String routeModelName = RoutingUtil.findRouteModelNameByTargetName(AclTargetType.USER, username);
|
||||||
|
if (routeModelName != null) {
|
||||||
|
throw new ClientException("UserUsedInRouteModel", routeModelName);
|
||||||
|
}
|
||||||
|
|
||||||
// Find linked data
|
// Find linked data
|
||||||
DocumentDao documentDao = new DocumentDao();
|
DocumentDao documentDao = new DocumentDao();
|
||||||
|
@ -46,12 +46,17 @@ angular.module('docs').controller('SettingsGroupEdit', function($scope, $dialog,
|
|||||||
$state.go('settings.group.edit', { name: group.name });
|
$state.go('settings.group.edit', { name: group.name });
|
||||||
}
|
}
|
||||||
}, function (e) {
|
}, function (e) {
|
||||||
if (e.data.type === 'GroupAlreadyExists') {
|
if (e.data.type === 'GroupAlreadyExists') {
|
||||||
var title = $translate.instant('settings.group.edit.edit_group_failed_title');
|
var title = $translate.instant('settings.group.edit.edit_group_failed_title');
|
||||||
var msg = $translate.instant('settings.group.edit.edit_group_failed_message');
|
var msg = $translate.instant('settings.group.edit.edit_group_failed_message');
|
||||||
var btns = [{result: 'ok', label: $translate.instant('ok'), cssClass: 'btn-primary'}];
|
var btns = [{result: 'ok', label: $translate.instant('ok'), cssClass: 'btn-primary'}];
|
||||||
$dialog.messageBox(title, msg, btns);
|
$dialog.messageBox(title, msg, btns);
|
||||||
}
|
} else if (e.data.type === 'GroupUsedInRouteModel') {
|
||||||
|
var title = $translate.instant('settings.group.edit.group_used_title');
|
||||||
|
var msg = $translate.instant('settings.group.edit.group_used_message', { name: e.data.message });
|
||||||
|
var btns = [{result: 'ok', label: $translate.instant('ok'), cssClass: 'btn-primary'}];
|
||||||
|
$dialog.messageBox(title, msg, btns);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -71,8 +76,13 @@ angular.module('docs').controller('SettingsGroupEdit', function($scope, $dialog,
|
|||||||
Restangular.one('group', $stateParams.name).remove().then(function() {
|
Restangular.one('group', $stateParams.name).remove().then(function() {
|
||||||
$scope.loadGroups();
|
$scope.loadGroups();
|
||||||
$state.go('settings.group');
|
$state.go('settings.group');
|
||||||
}, function() {
|
}, function(e) {
|
||||||
$state.go('settings.group');
|
if (e.data.type === 'GroupUsedInRouteModel') {
|
||||||
|
var title = $translate.instant('settings.group.edit.group_used_title');
|
||||||
|
var msg = $translate.instant('settings.group.edit.group_used_message', { name: e.data.message });
|
||||||
|
var btns = [{result: 'ok', label: $translate.instant('ok'), cssClass: 'btn-primary'}];
|
||||||
|
$dialog.messageBox(title, msg, btns);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -70,8 +70,13 @@ angular.module('docs').controller('SettingsUserEdit', function($scope, $dialog,
|
|||||||
Restangular.one('user', $stateParams.username).remove().then(function () {
|
Restangular.one('user', $stateParams.username).remove().then(function () {
|
||||||
$scope.loadUsers();
|
$scope.loadUsers();
|
||||||
$state.go('settings.user');
|
$state.go('settings.user');
|
||||||
}, function() {
|
}, function(e) {
|
||||||
$state.go('settings.user');
|
if (e.data.type === 'UserUsedInRouteModel') {
|
||||||
|
var title = $translate.instant('settings.user.edit.user_used_title');
|
||||||
|
var msg = $translate.instant('settings.user.edit.user_used_message', { name: e.data.message });
|
||||||
|
var btns = [{result: 'ok', label: $translate.instant('ok'), cssClass: 'btn-primary'}];
|
||||||
|
$dialog.messageBox(title, msg, btns);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -288,6 +288,8 @@
|
|||||||
"edit": {
|
"edit": {
|
||||||
"delete_user_title": "Delete user",
|
"delete_user_title": "Delete user",
|
||||||
"delete_user_message": "Do you really want to delete this user? All associated documents, files and tags will be deleted",
|
"delete_user_message": "Do you really want to delete this user? All associated documents, files and tags will be deleted",
|
||||||
|
"user_used_title": "User in use",
|
||||||
|
"user_used_message": "This user is used in the workflow \"{{ name }}\"",
|
||||||
"edit_user_failed_title": "User already exists",
|
"edit_user_failed_title": "User already exists",
|
||||||
"edit_user_failed_message": "This username is already taken by another user",
|
"edit_user_failed_message": "This username is already taken by another user",
|
||||||
"edit_user_title": "Edit \"{{ username }}\"",
|
"edit_user_title": "Edit \"{{ username }}\"",
|
||||||
@ -363,6 +365,8 @@
|
|||||||
"delete_group_message": "Do you really want to delete this group?",
|
"delete_group_message": "Do you really want to delete this group?",
|
||||||
"edit_group_failed_title": "Group already exists",
|
"edit_group_failed_title": "Group already exists",
|
||||||
"edit_group_failed_message": "This group name is already taken by another group",
|
"edit_group_failed_message": "This group name is already taken by another group",
|
||||||
|
"group_used_title": "Group in use",
|
||||||
|
"group_used_message": "This group is used in the workflow \"{{ name }}\"",
|
||||||
"edit_group_title": "Edit \"{{ name }}\"",
|
"edit_group_title": "Edit \"{{ name }}\"",
|
||||||
"add_group_title": "Add a group",
|
"add_group_title": "Add a group",
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
|
@ -325,7 +325,7 @@
|
|||||||
"type_approve": "Approbation",
|
"type_approve": "Approbation",
|
||||||
"type_validate": "Validation",
|
"type_validate": "Validation",
|
||||||
"target": "Assigné à",
|
"target": "Assigné à",
|
||||||
"target_help": "<strong>Approbation :</strong> Accepter ou rejeter l'étape de workflow<br/><strong>Validation :</strong> Examiner et poursuivre le workflow",
|
"target_help": "<strong>Approbation :</strong> Accepter ou rejeter l\\'étape de workflow<br/><strong>Validation :</strong> Examiner et poursuivre le workflow",
|
||||||
"add_step": "Ajouter une étape de workflow",
|
"add_step": "Ajouter une étape de workflow",
|
||||||
"actions": "Qu'est-ce qui se passe après?",
|
"actions": "Qu'est-ce qui se passe après?",
|
||||||
"remove_action": "Supprimer l'action",
|
"remove_action": "Supprimer l'action",
|
||||||
|
Loading…
Reference in New Issue
Block a user