873 lines
31 KiB
Java
873 lines
31 KiB
Java
package li.strolch.privilege.handler;
|
|
|
|
import li.strolch.privilege.base.PrivilegeConflictResolution;
|
|
import li.strolch.privilege.base.PrivilegeException;
|
|
import li.strolch.privilege.base.PrivilegeModelException;
|
|
import li.strolch.privilege.model.*;
|
|
import li.strolch.privilege.model.internal.PasswordCrypt;
|
|
import li.strolch.privilege.model.internal.Role;
|
|
import li.strolch.privilege.model.internal.User;
|
|
import li.strolch.privilege.model.internal.UserHistory;
|
|
import li.strolch.privilege.policy.PrivilegePolicy;
|
|
import li.strolch.utils.collections.Tuple;
|
|
|
|
import java.text.MessageFormat;
|
|
import java.time.ZonedDateTime;
|
|
import java.util.*;
|
|
import java.util.stream.Collectors;
|
|
import java.util.stream.Stream;
|
|
|
|
import static java.text.MessageFormat.format;
|
|
import static li.strolch.utils.helper.StringHelper.*;
|
|
|
|
public class PrivilegeCrudHandler {
|
|
|
|
private final DefaultPrivilegeHandler privilegeHandler;
|
|
private final PersistenceHandler persistenceHandler;
|
|
private final Map<String, Class<PrivilegePolicy>> policyMap;
|
|
private final PrivilegeConflictResolution privilegeConflictResolution;
|
|
|
|
public PrivilegeCrudHandler(DefaultPrivilegeHandler privilegeHandler, Map<String, Class<PrivilegePolicy>> policyMap,
|
|
PrivilegeConflictResolution privilegeConflictResolution) {
|
|
this.privilegeHandler = privilegeHandler;
|
|
this.persistenceHandler = privilegeHandler.persistenceHandler;
|
|
this.policyMap = policyMap;
|
|
this.privilegeConflictResolution = privilegeConflictResolution;
|
|
}
|
|
|
|
public RoleRep getRole(Certificate certificate, String roleName) {
|
|
|
|
// validate user actually has this type of privilege
|
|
PrivilegeContext prvCtx = this.privilegeHandler.validate(certificate);
|
|
prvCtx.assertHasPrivilege(DefaultPrivilegeHandler.PRIVILEGE_GET_ROLE);
|
|
|
|
Role role = this.persistenceHandler.getRole(roleName);
|
|
if (role == null)
|
|
return null;
|
|
|
|
prvCtx.validateAction(
|
|
new SimpleRestrictable(DefaultPrivilegeHandler.PRIVILEGE_GET_ROLE, new Tuple(null, role)));
|
|
|
|
return role.asRoleRep();
|
|
}
|
|
|
|
public UserRep getUser(Certificate certificate, String username) {
|
|
|
|
// validate user actually has this type of privilege
|
|
PrivilegeContext prvCtx = this.privilegeHandler.validate(certificate);
|
|
prvCtx.assertHasPrivilege(DefaultPrivilegeHandler.PRIVILEGE_GET_USER);
|
|
|
|
User user = this.persistenceHandler.getUser(username);
|
|
if (user == null)
|
|
return null;
|
|
|
|
prvCtx.validateAction(
|
|
new SimpleRestrictable(DefaultPrivilegeHandler.PRIVILEGE_GET_USER, new Tuple(null, user)));
|
|
return user.asUserRep();
|
|
}
|
|
|
|
public Map<String, String> getPolicyDefs(Certificate certificate) {
|
|
|
|
// validate user actually has this type of privilege
|
|
PrivilegeContext prvCtx = this.privilegeHandler.validate(certificate);
|
|
prvCtx.validateAction(new SimpleRestrictable(DefaultPrivilegeHandler.PRIVILEGE_ACTION,
|
|
DefaultPrivilegeHandler.PRIVILEGE_ACTION_GET_POLICIES));
|
|
|
|
Map<String, String> policyDef = new HashMap<>(this.policyMap.size());
|
|
for (Map.Entry<String, Class<PrivilegePolicy>> entry : this.policyMap.entrySet()) {
|
|
policyDef.put(entry.getKey(), entry.getValue().getName());
|
|
}
|
|
return policyDef;
|
|
}
|
|
|
|
public List<RoleRep> getRoles(Certificate certificate) {
|
|
|
|
// validate user actually has this type of privilege
|
|
PrivilegeContext prvCtx = this.privilegeHandler.validate(certificate);
|
|
prvCtx.assertHasPrivilege(DefaultPrivilegeHandler.PRIVILEGE_GET_ROLE);
|
|
|
|
Stream<Role> rolesStream = this.persistenceHandler.getAllRoles().stream();
|
|
|
|
// validate access to each role
|
|
rolesStream = rolesStream.filter(role -> prvCtx.hasPrivilege(
|
|
new SimpleRestrictable(DefaultPrivilegeHandler.PRIVILEGE_GET_ROLE, new Tuple(null, role))));
|
|
|
|
return rolesStream.map(Role::asRoleRep).collect(Collectors.toList());
|
|
}
|
|
|
|
public List<UserRep> getUsers(Certificate certificate) {
|
|
|
|
// validate user actually has this type of privilege
|
|
PrivilegeContext prvCtx = this.privilegeHandler.validate(certificate);
|
|
prvCtx.assertHasPrivilege(DefaultPrivilegeHandler.PRIVILEGE_GET_USER);
|
|
|
|
Stream<User> usersStream = this.persistenceHandler.getAllUsers().stream();
|
|
|
|
// validate access to each user
|
|
usersStream = usersStream.filter(user -> prvCtx.hasPrivilege(
|
|
new SimpleRestrictable(DefaultPrivilegeHandler.PRIVILEGE_GET_USER, new Tuple(null, user))));
|
|
|
|
return usersStream.map(User::asUserRep).collect(Collectors.toList());
|
|
}
|
|
|
|
public List<UserRep> queryUsers(Certificate certificate, UserRep selectorRep) {
|
|
|
|
// validate user actually has this type of privilege
|
|
PrivilegeContext prvCtx = this.privilegeHandler.validate(certificate);
|
|
prvCtx.assertHasPrivilege(DefaultPrivilegeHandler.PRIVILEGE_GET_USER);
|
|
|
|
String selUserId = selectorRep.getUserId();
|
|
String selUsername = selectorRep.getUsername();
|
|
String selFirstName = selectorRep.getFirstname();
|
|
String selLastName = selectorRep.getLastname();
|
|
UserState selUserState = selectorRep.getUserState();
|
|
Locale selLocale = selectorRep.getLocale();
|
|
Set<String> selGroups = selectorRep.getGroups();
|
|
Set<String> selRoles = selectorRep.getRoles();
|
|
Map<String, String> selPropertyMap = selectorRep.getProperties();
|
|
|
|
List<UserRep> result = new ArrayList<>();
|
|
List<User> allUsers = this.persistenceHandler.getAllUsers();
|
|
for (User user : allUsers) {
|
|
|
|
if (!prvCtx.hasPrivilege(
|
|
new SimpleRestrictable(DefaultPrivilegeHandler.PRIVILEGE_GET_USER, new Tuple(null, user))))
|
|
continue;
|
|
|
|
// selections
|
|
boolean userIdSelected;
|
|
boolean usernameSelected;
|
|
boolean firstNameSelected;
|
|
boolean lastNameSelected;
|
|
boolean userStateSelected;
|
|
boolean localeSelected;
|
|
boolean roleSelected;
|
|
boolean groupSelected;
|
|
boolean propertySelected;
|
|
|
|
// userId
|
|
userIdSelected = isEmpty(selUserId) || selUserId.equals(user.getUserId());
|
|
|
|
// username
|
|
usernameSelected = isEmpty(selUsername) || selUsername.equals(user.getUsername());
|
|
|
|
// firstname
|
|
firstNameSelected = isEmpty(selFirstName) || selFirstName.equals(user.getFirstname());
|
|
|
|
// lastname
|
|
lastNameSelected = isEmpty(selLastName) || selLastName.equals(user.getLastname());
|
|
|
|
// user state
|
|
userStateSelected = selUserState == null || selUserState.equals(user.getUserState());
|
|
|
|
// locale
|
|
localeSelected = selLocale == null || selLocale.equals(user.getLocale());
|
|
|
|
// groups
|
|
groupSelected = isSelectedByGroup(selGroups, user.getGroups());
|
|
|
|
// roles
|
|
roleSelected = isSelectedByRole(selRoles, user.getRoles());
|
|
|
|
// properties
|
|
propertySelected = isSelectedByProperty(selPropertyMap, user.getProperties());
|
|
|
|
boolean selected = userIdSelected
|
|
&& usernameSelected
|
|
&& firstNameSelected
|
|
&& lastNameSelected
|
|
&& userStateSelected
|
|
&& localeSelected
|
|
&& groupSelected
|
|
&& roleSelected
|
|
&& propertySelected;
|
|
|
|
if (selected)
|
|
result.add(user.asUserRep());
|
|
}
|
|
|
|
result.sort(Comparator.comparing(UserRep::getUsername));
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Checks if the given properties contains values which are contained in the selectionMap. If the selectionMap is
|
|
* null or empty, then true is returned. If a key/value pair from the selectionMap is not in the properties, then
|
|
* false is returned
|
|
*
|
|
* @param selectionMap the map defining the expected properties
|
|
* @param properties the properties which must be a sub set of selectionMap to have this method return true
|
|
*
|
|
* @return If the selectionMap is null or empty, then true is returned. If a key/value pair from the selectionMap is
|
|
* not in the properties, then false is returned
|
|
*/
|
|
boolean isSelectedByProperty(Map<String, String> selectionMap, Map<String, String> properties) {
|
|
|
|
if (selectionMap == null)
|
|
return true;
|
|
|
|
if (selectionMap.isEmpty() && properties.isEmpty())
|
|
return true;
|
|
|
|
for (String selKey : selectionMap.keySet()) {
|
|
|
|
String value = properties.get(selKey);
|
|
if (value == null || !value.equals(selectionMap.get(selKey)))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Checks if the given roles contains the given selectionRoles, if this is the case, or selectionRoles is null or
|
|
* empty, then true is returned, otherwise false
|
|
*
|
|
* @param selectionRoles the required roles
|
|
* @param roles the roles to check if they contain the selectionRoles
|
|
*
|
|
* @return Checks if the given roles contains the given selectionRoles, if this is the case, or selectionRoles is
|
|
* null or empty, then true is returned, otherwise false
|
|
*/
|
|
boolean isSelectedByRole(Set<String> selectionRoles, Set<String> roles) {
|
|
return selectionRoles == null || roles.containsAll(selectionRoles);
|
|
}
|
|
|
|
/**
|
|
* Checks if the given groups contains the given selectionGroups, if this is the case, or selectionGroups is null or
|
|
* empty, then true is returned, otherwise false
|
|
*
|
|
* @param selectionGroups the required groups
|
|
* @param groups the groups to check if they contain the selectionGroups
|
|
*
|
|
* @return Checks if the given groups contains the given selectionGroups, if this is the case, or selectionGroups is
|
|
* null or empty, then true is returned, otherwise false
|
|
*/
|
|
boolean isSelectedByGroup(Set<String> selectionGroups, Set<String> groups) {
|
|
return selectionGroups == null || groups.containsAll(selectionGroups);
|
|
}
|
|
|
|
public UserRep addUser(Certificate certificate, UserRep userRepParam, char[] password) {
|
|
try {
|
|
|
|
// validate user actually has this type of privilege
|
|
PrivilegeContext prvCtx = this.privilegeHandler.validate(certificate);
|
|
prvCtx.assertHasPrivilege(DefaultPrivilegeHandler.PRIVILEGE_ADD_USER);
|
|
|
|
// make sure userId is not set
|
|
if (isNotEmpty(userRepParam.getUserId()))
|
|
throw new PrivilegeModelException("UserId can not be set when adding a new user!");
|
|
|
|
UserRep userRep = userRepParam.getCopy();
|
|
|
|
// set userId
|
|
userRep.setUserId(getUniqueId());
|
|
|
|
// first validate user
|
|
userRep.validate();
|
|
|
|
validateGroupsExist(userRep);
|
|
validateRolesExist(userRep);
|
|
|
|
// validate user does not already exist
|
|
if (this.persistenceHandler.getUser(userRep.getUsername()) != null) {
|
|
String msg = "User {0} can not be added as it already exists!";
|
|
throw new PrivilegeModelException(MessageFormat.format(msg, userRep.getUsername()));
|
|
}
|
|
|
|
UserHistory history = UserHistory.EMPTY;
|
|
PasswordCrypt passwordCrypt = null;
|
|
if (password != null) {
|
|
|
|
// validate password meets basic requirements
|
|
this.privilegeHandler.validatePassword(certificate.getLocale(), password);
|
|
|
|
// get new salt for user
|
|
byte[] salt = this.privilegeHandler.getEncryptionHandler().nextSalt();
|
|
|
|
// hash password
|
|
passwordCrypt = this.privilegeHandler.getEncryptionHandler().hashPassword(password, salt);
|
|
|
|
history = history.withLastPasswordChange(ZonedDateTime.now());
|
|
}
|
|
|
|
// create new user
|
|
User newUser = createUser(userRep, history, passwordCrypt, false);
|
|
|
|
// detect privilege conflicts
|
|
assertNoPrivilegeConflict(newUser);
|
|
|
|
// validate this user may create such a user
|
|
prvCtx.validateAction(
|
|
new SimpleRestrictable(DefaultPrivilegeHandler.PRIVILEGE_ADD_USER, new Tuple(null, newUser)));
|
|
|
|
// delegate to persistence handler
|
|
this.persistenceHandler.addUser(newUser);
|
|
this.privilegeHandler.persistModelAsync();
|
|
|
|
DefaultPrivilegeHandler.logger.info("Created new user " + newUser.getUsername());
|
|
|
|
return newUser.asUserRep();
|
|
|
|
} finally {
|
|
clearPassword(password);
|
|
}
|
|
}
|
|
|
|
public void addOrUpdateUsers(Certificate certificate, List<UserRep> userReps) throws PrivilegeException {
|
|
|
|
// validate user actually has this type of privilege
|
|
PrivilegeContext prvCtx = this.privilegeHandler.validate(certificate);
|
|
prvCtx.assertHasPrivilege(DefaultPrivilegeHandler.PRIVILEGE_ADD_USER);
|
|
|
|
List<User> toCreate = new ArrayList<>();
|
|
List<User> toUpdate = new ArrayList<>();
|
|
|
|
for (UserRep e : userReps) {
|
|
UserRep userRep = e.getCopy();
|
|
|
|
User user;
|
|
User existingUser = this.persistenceHandler.getUser(userRep.getUsername());
|
|
|
|
if (existingUser == null) {
|
|
|
|
// add user
|
|
|
|
// make sure userId is not set
|
|
if (isNotEmpty(userRep.getUserId()))
|
|
throw new PrivilegeModelException("UserId can not be set when adding a new user!");
|
|
|
|
// set userId
|
|
userRep.setUserId(getUniqueId());
|
|
|
|
// first validate user
|
|
userRep.validate();
|
|
|
|
validateGroupsExist(userRep);
|
|
validateRolesExist(userRep);
|
|
|
|
// create new user
|
|
user = createUser(userRep, UserHistory.EMPTY, null, false);
|
|
|
|
// detect privilege conflicts
|
|
assertNoPrivilegeConflict(user);
|
|
|
|
// validate this user may create such a user
|
|
prvCtx.validateAction(
|
|
new SimpleRestrictable(DefaultPrivilegeHandler.PRIVILEGE_ADD_USER, new Tuple(null, user)));
|
|
|
|
toCreate.add(user);
|
|
DefaultPrivilegeHandler.logger.info("Creating new user " + user.getUsername());
|
|
|
|
} else {
|
|
|
|
// update user
|
|
|
|
if (userRep.getUserId() == null)
|
|
userRep.setUserId(existingUser.getUserId());
|
|
|
|
user = createUser(userRep, existingUser.getHistory(), existingUser.getPasswordCrypt(),
|
|
existingUser.isPasswordChangeRequested());
|
|
|
|
// detect privilege conflicts
|
|
assertNoPrivilegeConflict(user);
|
|
|
|
// validate this user may modify this user
|
|
prvCtx.validateAction(new SimpleRestrictable(DefaultPrivilegeHandler.PRIVILEGE_MODIFY_USER,
|
|
new Tuple(existingUser, user)));
|
|
|
|
toUpdate.add(user);
|
|
DefaultPrivilegeHandler.logger.info("Updating existing user " + user.getUsername());
|
|
}
|
|
}
|
|
|
|
// delegate to persistence handler
|
|
toCreate.forEach(this.persistenceHandler::addUser);
|
|
for (User user : toUpdate) {
|
|
this.persistenceHandler.replaceUser(user);
|
|
this.privilegeHandler.updateExistingSessionsForUser(user, false);
|
|
}
|
|
this.privilegeHandler.persistModelAsync();
|
|
|
|
DefaultPrivilegeHandler.logger.info("Created " + toCreate.size() + " users");
|
|
DefaultPrivilegeHandler.logger.info("Updated " + toUpdate.size() + " users");
|
|
}
|
|
|
|
private void assertNoPrivilegeConflict(User user) {
|
|
if (!this.privilegeConflictResolution.isStrict())
|
|
return;
|
|
Map<String, String> privilegeNames = new HashMap<>();
|
|
List<String> conflicts = detectPrivilegeConflicts(privilegeNames, user);
|
|
if (conflicts.isEmpty())
|
|
return;
|
|
String msg = String.join("\n", conflicts);
|
|
throw new PrivilegeModelException(msg);
|
|
}
|
|
|
|
private void assertNoPrivilegeConflict(Role role) {
|
|
if (!this.privilegeConflictResolution.isStrict())
|
|
return;
|
|
|
|
Map<String, String> privilegeNames = new HashMap<>();
|
|
for (String privilegeName : role.getPrivilegeNames()) {
|
|
privilegeNames.put(privilegeName, role.getName());
|
|
}
|
|
|
|
List<String> conflicts = new ArrayList<>();
|
|
List<User> users = this.persistenceHandler.getAllUsers();
|
|
for (User user : users) {
|
|
if (user.hasRole(role.getName()))
|
|
conflicts.addAll(detectPrivilegeConflicts(privilegeNames, user));
|
|
}
|
|
|
|
if (!conflicts.isEmpty()) {
|
|
String msg = String.join("\n", conflicts);
|
|
throw new PrivilegeModelException(msg);
|
|
}
|
|
}
|
|
|
|
public List<String> detectPrivilegeConflicts(Map<String, String> privilegeNames, User user) {
|
|
List<String> conflicts = new ArrayList<>();
|
|
|
|
Set<String> userRoles = user.getRoles();
|
|
for (String roleName : userRoles) {
|
|
Role role = this.persistenceHandler.getRole(roleName);
|
|
if (role == null)
|
|
throw new IllegalStateException("Role " + roleName + " does not exist for user " + user.getUsername());
|
|
for (String privilegeName : role.getPrivilegeNames()) {
|
|
String roleOrigin = privilegeNames.get(privilegeName);
|
|
if (roleOrigin == null) {
|
|
privilegeNames.put(privilegeName, roleName);
|
|
} else if (!roleOrigin.equals(roleName)) {
|
|
String msg = "User {0} has conflicts for privilege {1} on roles {2} and {3}";
|
|
msg = format(msg, user.getUsername(), privilegeName, roleOrigin, roleName);
|
|
conflicts.add(msg);
|
|
}
|
|
}
|
|
}
|
|
|
|
return conflicts;
|
|
}
|
|
|
|
private void validateGroupsExist(UserRep userRep) {
|
|
for (String group : userRep.getGroups()) {
|
|
if (this.persistenceHandler.getGroup(group) == null) {
|
|
String msg = "Can not add/update user {0} as group {1} does not exist!";
|
|
msg = MessageFormat.format(msg, userRep.getUsername(), group);
|
|
throw new PrivilegeModelException(msg);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void validateRolesExist(UserRep userRep) {
|
|
for (String role : userRep.getRoles()) {
|
|
if (this.persistenceHandler.getRole(role) == null) {
|
|
String msg = "Can not add/update user {0} as role {1} does not exist!";
|
|
msg = MessageFormat.format(msg, userRep.getUsername(), role);
|
|
throw new PrivilegeModelException(msg);
|
|
}
|
|
}
|
|
}
|
|
|
|
User createUser(UserRep userRep, UserHistory history, PasswordCrypt passwordCrypt,
|
|
boolean passwordChangeRequested) {
|
|
String userId = userRep.getUserId();
|
|
String userName = userRep.getUsername();
|
|
String firstName = userRep.getFirstname();
|
|
String lastName = userRep.getLastname();
|
|
UserState state = userRep.getUserState();
|
|
Set<String> groups = userRep.getGroups();
|
|
Set<String> roles = userRep.getRoles();
|
|
Locale locale = userRep.getLocale();
|
|
Map<String, String> properties = userRep.getProperties();
|
|
return new User(userId, userName, passwordCrypt, firstName, lastName, state, groups, roles, locale, properties,
|
|
passwordChangeRequested, history);
|
|
}
|
|
|
|
public UserRep updateUser(Certificate certificate, UserRep userRep, char[] password) throws PrivilegeException {
|
|
try {
|
|
|
|
// validate user actually has this type of privilege
|
|
PrivilegeContext prvCtx = this.privilegeHandler.validate(certificate);
|
|
prvCtx.assertHasPrivilege(DefaultPrivilegeHandler.PRIVILEGE_MODIFY_USER);
|
|
|
|
// first validate user
|
|
userRep.validate();
|
|
|
|
validateGroupsExist(userRep);
|
|
validateRolesExist(userRep);
|
|
|
|
// validate user exists
|
|
User existingUser = this.persistenceHandler.getUser(userRep.getUsername());
|
|
if (existingUser == null) {
|
|
String msg = "User {0} does not exist!";
|
|
throw new PrivilegeModelException(MessageFormat.format(msg, userRep.getUsername()));
|
|
}
|
|
|
|
// validate same userId
|
|
if (!existingUser.getUserId().equals(userRep.getUserId())) {
|
|
String msg = "UserId of existing user {0} does not match userRep {1}";
|
|
msg = MessageFormat.format(msg, existingUser.getUserId(), userRep.getUserId());
|
|
throw new PrivilegeModelException(MessageFormat.format(msg, userRep.getUsername()));
|
|
}
|
|
|
|
UserHistory history = existingUser.getHistory();
|
|
PasswordCrypt passwordCrypt;
|
|
if (password == null) {
|
|
passwordCrypt = existingUser.getPasswordCrypt();
|
|
} else {
|
|
|
|
// validate password meets basic requirements
|
|
this.privilegeHandler.validatePassword(certificate.getLocale(), password);
|
|
|
|
// get new salt for user
|
|
byte[] salt = this.privilegeHandler.getEncryptionHandler().nextSalt();
|
|
|
|
// hash password
|
|
passwordCrypt = this.privilegeHandler.getEncryptionHandler().hashPassword(password, salt);
|
|
|
|
history = history.withLastPasswordChange(ZonedDateTime.now());
|
|
}
|
|
|
|
User newUser = createUser(userRep, history, passwordCrypt, existingUser.isPasswordChangeRequested());
|
|
|
|
// detect privilege conflicts
|
|
assertNoPrivilegeConflict(newUser);
|
|
|
|
// validate this user may modify this user
|
|
prvCtx.validateAction(new SimpleRestrictable(DefaultPrivilegeHandler.PRIVILEGE_MODIFY_USER,
|
|
new Tuple(existingUser, newUser)));
|
|
|
|
// delegate to persistence handler
|
|
this.persistenceHandler.replaceUser(newUser);
|
|
this.privilegeHandler.updateExistingSessionsForUser(newUser, true);
|
|
|
|
DefaultPrivilegeHandler.logger.info("Replaced user " + newUser.getUsername());
|
|
|
|
return newUser.asUserRep();
|
|
|
|
} finally {
|
|
clearPassword(password);
|
|
}
|
|
}
|
|
|
|
public UserRep removeUser(Certificate certificate, String username) {
|
|
|
|
// validate user actually has this type of privilege
|
|
PrivilegeContext prvCtx = this.privilegeHandler.validate(certificate);
|
|
prvCtx.assertHasPrivilege(DefaultPrivilegeHandler.PRIVILEGE_REMOVE_USER);
|
|
|
|
// validate user exists
|
|
User existingUser = this.persistenceHandler.getUser(username);
|
|
if (existingUser == null) {
|
|
String msg = "Can not remove User {0} because user does not exist!";
|
|
throw new PrivilegeModelException(MessageFormat.format(msg, username));
|
|
}
|
|
|
|
// validate this user may remove this user
|
|
prvCtx.validateAction(
|
|
new SimpleRestrictable(DefaultPrivilegeHandler.PRIVILEGE_REMOVE_USER, new Tuple(null, existingUser)));
|
|
|
|
// delegate user removal to persistence handler
|
|
this.privilegeHandler.invalidateSessionsFor(existingUser);
|
|
this.persistenceHandler.removeUser(username);
|
|
|
|
DefaultPrivilegeHandler.logger.info("Removed user " + username);
|
|
|
|
return existingUser.asUserRep();
|
|
}
|
|
|
|
public UserRep setUserLocale(Certificate certificate, String username, Locale locale) {
|
|
|
|
// validate user actually has this type of privilege
|
|
PrivilegeContext prvCtx = this.privilegeHandler.validate(certificate);
|
|
prvCtx.assertHasPrivilege(DefaultPrivilegeHandler.PRIVILEGE_SET_USER_LOCALE);
|
|
|
|
// get User
|
|
User existingUser = this.persistenceHandler.getUser(username);
|
|
if (existingUser == null)
|
|
throw new PrivilegeModelException(MessageFormat.format("User {0} does not exist!", username));
|
|
|
|
// create new user
|
|
User newUser = new User(existingUser.getUserId(), existingUser.getUsername(), existingUser.getPasswordCrypt(),
|
|
existingUser.getFirstname(), existingUser.getLastname(), existingUser.getUserState(),
|
|
existingUser.getGroups(), existingUser.getRoles(), locale, existingUser.getProperties(),
|
|
existingUser.isPasswordChangeRequested(), existingUser.getHistory());
|
|
|
|
// if the user is not setting their own locale, then make sure this user may set this user's locale
|
|
if (!certificate.getUsername().equals(username)) {
|
|
prvCtx.validateAction(new SimpleRestrictable(DefaultPrivilegeHandler.PRIVILEGE_SET_USER_LOCALE,
|
|
new Tuple(existingUser, newUser)));
|
|
}
|
|
|
|
// delegate user replacement to persistence handler
|
|
this.persistenceHandler.replaceUser(newUser);
|
|
this.privilegeHandler.persistModelAsync();
|
|
|
|
DefaultPrivilegeHandler.logger.info("Set locale to " + locale + " for " + newUser.getUsername());
|
|
|
|
return newUser.asUserRep();
|
|
}
|
|
|
|
public void requirePasswordChange(Certificate certificate, String username) throws PrivilegeException {
|
|
|
|
// validate user actually has this type of privilege
|
|
PrivilegeContext prvCtx = this.privilegeHandler.validate(certificate);
|
|
prvCtx.assertHasPrivilege(DefaultPrivilegeHandler.PRIVILEGE_REQUIRE_PASSWORD_CHANGE);
|
|
|
|
// get User
|
|
User existingUser = this.persistenceHandler.getUser(username);
|
|
if (existingUser == null)
|
|
throw new PrivilegeModelException(MessageFormat.format("User {0} does not exist!", username));
|
|
|
|
if (existingUser.getUserState().isRemote())
|
|
throw new PrivilegeModelException(
|
|
MessageFormat.format("User {0} is remote and can not set password!", username));
|
|
|
|
// create new user
|
|
User newUser = new User(existingUser.getUserId(), existingUser.getUsername(), existingUser.getPasswordCrypt(),
|
|
existingUser.getFirstname(), existingUser.getLastname(), existingUser.getUserState(),
|
|
existingUser.getGroups(), existingUser.getRoles(), existingUser.getLocale(),
|
|
existingUser.getProperties(), true, existingUser.getHistory());
|
|
|
|
// delegate user replacement to persistence handler
|
|
this.persistenceHandler.replaceUser(newUser);
|
|
this.privilegeHandler.persistModelAsync();
|
|
|
|
DefaultPrivilegeHandler.logger.info(
|
|
"Requiring user " + newUser.getUsername() + " to change their password on next login.");
|
|
}
|
|
|
|
public void setUserPassword(Certificate certificate, String username, char[] password) {
|
|
|
|
// we don't want the user to worry about whitespace
|
|
username = trimOrEmpty(username);
|
|
|
|
try {
|
|
|
|
// validate user actually has this type of privilege
|
|
PrivilegeContext prvCtx = this.privilegeHandler.validate(certificate);
|
|
prvCtx.assertHasPrivilege(DefaultPrivilegeHandler.PRIVILEGE_SET_USER_PASSWORD);
|
|
|
|
// get User
|
|
User existingUser = this.persistenceHandler.getUser(username);
|
|
if (existingUser == null)
|
|
throw new PrivilegeModelException(MessageFormat.format("User {0} does not exist!", username));
|
|
|
|
UserHistory history = existingUser.getHistory();
|
|
|
|
PasswordCrypt passwordCrypt = null;
|
|
if (password != null) {
|
|
|
|
// validate password meets basic requirements
|
|
this.privilegeHandler.validatePassword(certificate.getLocale(), password);
|
|
|
|
// get new salt for user
|
|
byte[] salt = this.privilegeHandler.getEncryptionHandler().nextSalt();
|
|
|
|
// hash password
|
|
passwordCrypt = this.privilegeHandler.getEncryptionHandler().hashPassword(password, salt);
|
|
|
|
history = history.withLastPasswordChange(ZonedDateTime.now());
|
|
}
|
|
|
|
// create new user
|
|
User newUser = new User(existingUser.getUserId(), existingUser.getUsername(), passwordCrypt,
|
|
existingUser.getFirstname(), existingUser.getLastname(), existingUser.getUserState(),
|
|
existingUser.getGroups(), existingUser.getRoles(), existingUser.getLocale(),
|
|
existingUser.getProperties(), false, history);
|
|
|
|
if (!certificate.getUsername().equals(username)) {
|
|
// check that the user may change their own password
|
|
Tuple value = new Tuple(existingUser, newUser);
|
|
prvCtx.validateAction(
|
|
new SimpleRestrictable(DefaultPrivilegeHandler.PRIVILEGE_SET_USER_PASSWORD, value));
|
|
}
|
|
|
|
// delegate user replacement to persistence handler
|
|
this.persistenceHandler.replaceUser(newUser);
|
|
this.privilegeHandler.persistModelAsync();
|
|
|
|
if (certificate.getUsage() == Usage.SET_PASSWORD)
|
|
this.privilegeHandler.invalidate(certificate);
|
|
|
|
if (password == null)
|
|
DefaultPrivilegeHandler.logger.info("Cleared password for " + newUser.getUsername());
|
|
else
|
|
DefaultPrivilegeHandler.logger.info("Updated password for " + newUser.getUsername());
|
|
|
|
} finally {
|
|
clearPassword(password);
|
|
}
|
|
}
|
|
|
|
public UserRep setUserState(Certificate certificate, String username, UserState state) {
|
|
|
|
// validate user actually has this type of privilege
|
|
PrivilegeContext prvCtx = this.privilegeHandler.validate(certificate);
|
|
prvCtx.assertHasPrivilege(DefaultPrivilegeHandler.PRIVILEGE_SET_USER_STATE);
|
|
|
|
// get User
|
|
User existingUser = this.persistenceHandler.getUser(username);
|
|
if (existingUser == null)
|
|
throw new PrivilegeModelException(MessageFormat.format("User {0} does not exist!", username));
|
|
|
|
// create new user
|
|
User newUser = new User(existingUser.getUserId(), existingUser.getUsername(), existingUser.getPasswordCrypt(),
|
|
existingUser.getFirstname(), existingUser.getLastname(), state, existingUser.getGroups(),
|
|
existingUser.getRoles(), existingUser.getLocale(), existingUser.getProperties(),
|
|
existingUser.isPasswordChangeRequested(), existingUser.getHistory());
|
|
|
|
// validate that this user may modify this user's state
|
|
prvCtx.validateAction(new SimpleRestrictable(DefaultPrivilegeHandler.PRIVILEGE_SET_USER_STATE,
|
|
new Tuple(existingUser, newUser)));
|
|
|
|
// delegate user replacement to persistence handler
|
|
this.persistenceHandler.replaceUser(newUser);
|
|
this.privilegeHandler.invalidateSessionsFor(newUser);
|
|
|
|
DefaultPrivilegeHandler.logger.info("Set state of user " + newUser.getUsername() + " to " + state);
|
|
|
|
return newUser.asUserRep();
|
|
}
|
|
|
|
public RoleRep addRole(Certificate certificate, RoleRep roleRep) {
|
|
|
|
// validate user actually has this type of privilege
|
|
PrivilegeContext prvCtx = this.privilegeHandler.validate(certificate);
|
|
prvCtx.assertHasPrivilege(DefaultPrivilegeHandler.PRIVILEGE_ADD_ROLE);
|
|
|
|
// first validate role
|
|
roleRep.validate();
|
|
|
|
// validate role does not exist
|
|
if (this.persistenceHandler.getRole(roleRep.getName()) != null) {
|
|
String msg = MessageFormat.format("Can not add role {0} as it already exists!", roleRep.getName());
|
|
throw new PrivilegeModelException(msg);
|
|
}
|
|
|
|
// create new role from RoleRep
|
|
Role newRole = Role.of(roleRep);
|
|
|
|
// validate that this user may add this new role
|
|
prvCtx.validateAction(
|
|
new SimpleRestrictable(DefaultPrivilegeHandler.PRIVILEGE_ADD_ROLE, new Tuple(null, newRole)));
|
|
|
|
// validate policy if not null
|
|
validatePolicies(newRole);
|
|
|
|
// delegate to persistence handler
|
|
this.persistenceHandler.addRole(newRole);
|
|
this.privilegeHandler.persistModelAsync();
|
|
|
|
DefaultPrivilegeHandler.logger.info("Added new role " + newRole.getName());
|
|
|
|
return newRole.asRoleRep();
|
|
}
|
|
|
|
public RoleRep replaceRole(Certificate certificate, RoleRep roleRep) {
|
|
|
|
// validate user actually has this type of privilege
|
|
PrivilegeContext prvCtx = this.privilegeHandler.validate(certificate);
|
|
prvCtx.assertHasPrivilege(DefaultPrivilegeHandler.PRIVILEGE_MODIFY_ROLE);
|
|
|
|
// first validate role
|
|
roleRep.validate();
|
|
|
|
// validate role does exist
|
|
Role existingRole = this.persistenceHandler.getRole(roleRep.getName());
|
|
if (existingRole == null) {
|
|
String msg = MessageFormat.format("Can not replace role {0} as it does not exist!", roleRep.getName());
|
|
throw new PrivilegeModelException(msg);
|
|
}
|
|
|
|
// create new role from RoleRep
|
|
Role newRole = Role.of(roleRep);
|
|
|
|
// detect privilege conflicts
|
|
assertNoPrivilegeConflict(newRole);
|
|
|
|
// validate that this user may modify this role
|
|
prvCtx.validateAction(new SimpleRestrictable(DefaultPrivilegeHandler.PRIVILEGE_MODIFY_ROLE,
|
|
new Tuple(existingRole, newRole)));
|
|
|
|
// validate policy if not null
|
|
validatePolicies(newRole);
|
|
|
|
// delegate to persistence handler
|
|
this.persistenceHandler.replaceRole(newRole);
|
|
this.privilegeHandler.persistModelAsync();
|
|
|
|
// update any existing certificates with new role
|
|
this.privilegeHandler.updateExistingSessionsWithNewRole(newRole);
|
|
|
|
DefaultPrivilegeHandler.logger.info("Replaced role " + newRole.getName());
|
|
|
|
return newRole.asRoleRep();
|
|
}
|
|
|
|
public RoleRep removeRole(Certificate certificate, String roleName) {
|
|
|
|
// validate user actually has this type of privilege
|
|
PrivilegeContext prvCtx = this.privilegeHandler.validate(certificate);
|
|
prvCtx.assertHasPrivilege(DefaultPrivilegeHandler.PRIVILEGE_REMOVE_ROLE);
|
|
|
|
// validate no user is using this role
|
|
Set<String> roles = new HashSet<>(Collections.singletonList(roleName));
|
|
UserRep selector = new UserRep(null, null, null, null, null, null, roles, null, null, null);
|
|
List<UserRep> usersWithRole = this.privilegeHandler.queryUsers(certificate, selector);
|
|
if (!usersWithRole.isEmpty()) {
|
|
String usersS = usersWithRole.stream().map(UserRep::getUsername).collect(Collectors.joining(", "));
|
|
String msg = "The role {0} can not be removed as the following {1} user have the role assigned: {2}";
|
|
msg = MessageFormat.format(msg, roleName, usersWithRole.size(), usersS);
|
|
throw new PrivilegeModelException(msg);
|
|
}
|
|
|
|
// validate role exists
|
|
Role existingRole = this.persistenceHandler.getRole(roleName);
|
|
if (existingRole == null) {
|
|
String msg = "Can not remove Role {0} because role does not exist!";
|
|
throw new PrivilegeModelException(MessageFormat.format(msg, roleName));
|
|
}
|
|
|
|
// validate that this user may remove this role
|
|
prvCtx.validateAction(
|
|
new SimpleRestrictable(DefaultPrivilegeHandler.PRIVILEGE_REMOVE_ROLE, new Tuple(null, existingRole)));
|
|
|
|
// delegate role removal to persistence handler
|
|
this.persistenceHandler.removeRole(roleName);
|
|
this.privilegeHandler.persistModelAsync();
|
|
|
|
DefaultPrivilegeHandler.logger.info("Removed role " + roleName);
|
|
|
|
return existingRole.asRoleRep();
|
|
}
|
|
|
|
/**
|
|
* Validates that the policies which are not null on the privileges of the role exist
|
|
*
|
|
* @param role the role for which the policies are to be checked
|
|
*/
|
|
public void validatePolicies(Role role) {
|
|
for (String privilegeName : role.getPrivilegeNames()) {
|
|
Privilege privilege = role.getPrivilege(privilegeName);
|
|
String policy = privilege.getPolicy();
|
|
if (policy != null && !this.policyMap.containsKey(policy)) {
|
|
String msg = "Policy {0} for Privilege {1} does not exist on role {2}";
|
|
msg = format(msg, policy, privilege.getName(), role);
|
|
throw new PrivilegeModelException(msg);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Passwords should not be kept as strings, as string are immutable, this method thus clears the char array so that
|
|
* the password is not in memory anymore
|
|
*
|
|
* @param password the char array containing the passwort which is to be set to zeroes
|
|
*/
|
|
static void clearPassword(char[] password) {
|
|
if (password != null)
|
|
Arrays.fill(password, (char) 0);
|
|
}
|
|
} |