mirror of
https://github.com/sismics/docs.git
synced 2024-12-22 11:23:48 +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.dao.criteria.RouteModelCriteria;
|
||||
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.util.AuditLogUtil;
|
||||
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.
|
||||
*
|
||||
|
@ -6,6 +6,7 @@ import com.sismics.docs.core.constant.AclType;
|
||||
import com.sismics.docs.core.constant.PermType;
|
||||
import com.sismics.docs.core.dao.AclDao;
|
||||
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.criteria.UserCriteria;
|
||||
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.jpa.Acl;
|
||||
import com.sismics.docs.core.model.jpa.Document;
|
||||
import com.sismics.docs.core.model.jpa.RouteModel;
|
||||
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;
|
||||
|
||||
/**
|
||||
@ -87,4 +94,31 @@ public class RoutingUtil {
|
||||
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.collect.Sets;
|
||||
import com.sismics.docs.core.constant.AclTargetType;
|
||||
import com.sismics.docs.core.dao.GroupDao;
|
||||
import com.sismics.docs.core.dao.RoleBaseFunctionDao;
|
||||
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.User;
|
||||
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.rest.constant.BaseFunction;
|
||||
import com.sismics.rest.exception.ClientException;
|
||||
@ -148,6 +150,14 @@ public class GroupResource extends BaseResource {
|
||||
}
|
||||
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
|
||||
groupDao.update(group.setName(name)
|
||||
@ -197,7 +207,13 @@ public class GroupResource extends BaseResource {
|
||||
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
|
||||
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.collect.Sets;
|
||||
import com.sismics.docs.core.constant.AclTargetType;
|
||||
import com.sismics.docs.core.constant.ConfigType;
|
||||
import com.sismics.docs.core.constant.Constants;
|
||||
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.jpa.*;
|
||||
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.jpa.SortCriteria;
|
||||
import com.sismics.docs.rest.constant.BaseFunction;
|
||||
@ -449,6 +451,12 @@ public class UserResource extends BaseResource {
|
||||
if (hasBaseFunction(BaseFunction.ADMIN) || principal.isGuest()) {
|
||||
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
|
||||
DocumentDao documentDao = new DocumentDao();
|
||||
@ -512,7 +520,7 @@ public class UserResource extends BaseResource {
|
||||
throw new ClientException("ForbiddenError", "The guest user cannot be deleted");
|
||||
}
|
||||
|
||||
// Check if the user exists
|
||||
// Check that the user exists
|
||||
UserDao userDao = new UserDao();
|
||||
User user = userDao.getActiveByUsername(username);
|
||||
if (user == null) {
|
||||
@ -525,6 +533,12 @@ public class UserResource extends BaseResource {
|
||||
if (baseFunctionSet.contains(BaseFunction.ADMIN.name())) {
|
||||
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
|
||||
DocumentDao documentDao = new DocumentDao();
|
||||
|
@ -46,12 +46,17 @@ angular.module('docs').controller('SettingsGroupEdit', function($scope, $dialog,
|
||||
$state.go('settings.group.edit', { name: group.name });
|
||||
}
|
||||
}, function (e) {
|
||||
if (e.data.type === 'GroupAlreadyExists') {
|
||||
var title = $translate.instant('settings.group.edit.edit_group_failed_title');
|
||||
var msg = $translate.instant('settings.group.edit.edit_group_failed_message');
|
||||
var btns = [{result: 'ok', label: $translate.instant('ok'), cssClass: 'btn-primary'}];
|
||||
$dialog.messageBox(title, msg, btns);
|
||||
}
|
||||
if (e.data.type === 'GroupAlreadyExists') {
|
||||
var title = $translate.instant('settings.group.edit.edit_group_failed_title');
|
||||
var msg = $translate.instant('settings.group.edit.edit_group_failed_message');
|
||||
var btns = [{result: 'ok', label: $translate.instant('ok'), cssClass: 'btn-primary'}];
|
||||
$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() {
|
||||
$scope.loadGroups();
|
||||
$state.go('settings.group');
|
||||
}, function() {
|
||||
$state.go('settings.group');
|
||||
}, function(e) {
|
||||
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 () {
|
||||
$scope.loadUsers();
|
||||
$state.go('settings.user');
|
||||
}, function() {
|
||||
$state.go('settings.user');
|
||||
}, function(e) {
|
||||
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": {
|
||||
"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",
|
||||
"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_message": "This username is already taken by another user",
|
||||
"edit_user_title": "Edit \"{{ username }}\"",
|
||||
@ -363,6 +365,8 @@
|
||||
"delete_group_message": "Do you really want to delete this group?",
|
||||
"edit_group_failed_title": "Group already exists",
|
||||
"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 }}\"",
|
||||
"add_group_title": "Add a group",
|
||||
"name": "Name",
|
||||
|
@ -325,7 +325,7 @@
|
||||
"type_approve": "Approbation",
|
||||
"type_validate": "Validation",
|
||||
"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",
|
||||
"actions": "Qu'est-ce qui se passe après?",
|
||||
"remove_action": "Supprimer l'action",
|
||||
|
Loading…
Reference in New Issue
Block a user