2345 lines
82 KiB
Java
2345 lines
82 KiB
Java
/*
|
|
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
package li.strolch.privilege.handler;
|
|
|
|
import static java.text.MessageFormat.format;
|
|
import static li.strolch.utils.helper.ExceptionHelper.getRootCause;
|
|
import static li.strolch.utils.helper.StringHelper.*;
|
|
|
|
import javax.crypto.SecretKey;
|
|
import java.io.File;
|
|
import java.io.InputStream;
|
|
import java.io.OutputStream;
|
|
import java.nio.file.Files;
|
|
import java.time.ZonedDateTime;
|
|
import java.util.*;
|
|
import java.util.Map.Entry;
|
|
import java.util.stream.Collectors;
|
|
import java.util.stream.Stream;
|
|
|
|
import li.strolch.privilege.base.*;
|
|
import li.strolch.privilege.model.*;
|
|
import li.strolch.privilege.model.internal.*;
|
|
import li.strolch.privilege.policy.PrivilegePolicy;
|
|
import li.strolch.privilege.xml.CertificateStubsDomWriter;
|
|
import li.strolch.privilege.xml.CertificateStubsSaxReader;
|
|
import li.strolch.privilege.xml.CertificateStubsSaxReader.CertificateStub;
|
|
import li.strolch.utils.collections.Tuple;
|
|
import li.strolch.utils.dbc.DBC;
|
|
import li.strolch.utils.helper.AesCryptoHelper;
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.LoggerFactory;
|
|
import org.xml.sax.SAXParseException;
|
|
|
|
/**
|
|
* <p>
|
|
* This is default implementation of the {@link PrivilegeHandler}
|
|
* </p>
|
|
* <p>
|
|
* The following list describes implementation details:
|
|
* <ul>
|
|
* <li>any methods which change the model are first validated by checking if the certificate has the appropriate
|
|
* privilege</li>
|
|
* <li>all model requests are delegated to the configured {@link PrivilegeHandler}, except for the session id to
|
|
* {@link Certificate} map, no model data is kept in this implementation. This also means that to return the
|
|
* representation objects, for every new model query, a new representation object is created</li>
|
|
* <li>when creating new users, or editing users then a null password is understood as no password set</li>
|
|
* <li>Password requirements are simple: Non null and non empty/length 0</li>
|
|
* </ul>
|
|
*
|
|
* @author Robert von Burg <eitch@eitchnet.ch>
|
|
*/
|
|
public class DefaultPrivilegeHandler implements PrivilegeHandler {
|
|
|
|
protected static final Logger logger = LoggerFactory.getLogger(DefaultPrivilegeHandler.class);
|
|
public static final String SOURCE_UNKNOWN = "unknown";
|
|
|
|
/**
|
|
* Map keeping a reference to all active sessions
|
|
*/
|
|
protected Map<String, PrivilegeContext> privilegeContextMap;
|
|
|
|
/**
|
|
* Map of {@link PrivilegePolicy} classes
|
|
*/
|
|
protected Map<String, Class<PrivilegePolicy>> policyMap;
|
|
|
|
/**
|
|
* The persistence handler is used for getting objects and saving changes
|
|
*/
|
|
protected PersistenceHandler persistenceHandler;
|
|
|
|
/**
|
|
* The encryption handler is used for generating hashes and tokens
|
|
*/
|
|
protected EncryptionHandler encryptionHandler;
|
|
|
|
/**
|
|
* The password strength handler is used for validating the strength of a password when being set
|
|
*/
|
|
protected PasswordStrengthHandler passwordStrengthHandler;
|
|
|
|
/**
|
|
* The Single Sign On Handler
|
|
*/
|
|
protected SingleSignOnHandler ssoHandler;
|
|
|
|
/**
|
|
* The {@link UserChallengeHandler} is used to challenge a user which tries to authenticate and/or change their
|
|
* password
|
|
*/
|
|
protected UserChallengeHandler userChallengeHandler;
|
|
|
|
/**
|
|
* flag to define if already initialized
|
|
*/
|
|
protected boolean initialized;
|
|
|
|
/**
|
|
* flag to define if a persist should be performed after a user changes their own data
|
|
*/
|
|
protected boolean autoPersistOnUserChangesData;
|
|
|
|
/**
|
|
* flag to define if sessions should be persisted
|
|
*/
|
|
protected boolean persistSessions;
|
|
|
|
/**
|
|
* Path to sessions file for persistence
|
|
*/
|
|
protected File persistSessionsPath;
|
|
|
|
/**
|
|
* Secret key
|
|
*/
|
|
protected SecretKey secretKey;
|
|
|
|
/**
|
|
* flag if session refreshing is allowed
|
|
*/
|
|
protected boolean allowSessionRefresh;
|
|
|
|
protected PrivilegeConflictResolution privilegeConflictResolution;
|
|
private String identifier;
|
|
|
|
private Map<String, String> parameterMap;
|
|
|
|
@Override
|
|
public SingleSignOnHandler getSsoHandler() {
|
|
return this.ssoHandler;
|
|
}
|
|
|
|
@Override
|
|
public UserChallengeHandler getUserChallengeHandler() {
|
|
return this.userChallengeHandler;
|
|
}
|
|
|
|
@Override
|
|
public PersistenceHandler getPersistenceHandler() {
|
|
return this.persistenceHandler;
|
|
}
|
|
|
|
@Override
|
|
public Map<String, String> getParameterMap() {
|
|
return this.parameterMap;
|
|
}
|
|
|
|
@Override
|
|
public boolean isRefreshAllowed() {
|
|
return this.allowSessionRefresh;
|
|
}
|
|
|
|
@Override
|
|
public boolean isPersistOnUserDataChanged() {
|
|
return this.autoPersistOnUserChangesData;
|
|
}
|
|
|
|
@Override
|
|
public EncryptionHandler getEncryptionHandler() throws PrivilegeException {
|
|
return this.encryptionHandler;
|
|
}
|
|
|
|
@Override
|
|
public RoleRep getRole(Certificate certificate, String roleName) {
|
|
|
|
// validate user actually has this type of privilege
|
|
PrivilegeContext prvCtx = validate(certificate);
|
|
prvCtx.assertHasPrivilege(PRIVILEGE_GET_ROLE);
|
|
|
|
Role role = this.persistenceHandler.getRole(roleName);
|
|
if (role == null)
|
|
return null;
|
|
|
|
prvCtx.validateAction(new SimpleRestrictable(PRIVILEGE_GET_ROLE, new Tuple(null, role)));
|
|
|
|
return role.asRoleRep();
|
|
}
|
|
|
|
@Override
|
|
public UserRep getUser(Certificate certificate, String username) {
|
|
|
|
// validate user actually has this type of privilege
|
|
PrivilegeContext prvCtx = validate(certificate);
|
|
prvCtx.assertHasPrivilege(PRIVILEGE_GET_USER);
|
|
|
|
User user = this.persistenceHandler.getUser(username);
|
|
if (user == null)
|
|
return null;
|
|
|
|
prvCtx.validateAction(new SimpleRestrictable(PRIVILEGE_GET_USER, new Tuple(null, user)));
|
|
return user.asUserRep();
|
|
}
|
|
|
|
@Override
|
|
public Map<String, String> getPolicyDefs(Certificate certificate) {
|
|
|
|
// validate user actually has this type of privilege
|
|
PrivilegeContext prvCtx = validate(certificate);
|
|
prvCtx.validateAction(new SimpleRestrictable(PRIVILEGE_ACTION, PRIVILEGE_ACTION_GET_POLICIES));
|
|
|
|
Map<String, String> policyDef = new HashMap<>(this.policyMap.size());
|
|
for (Entry<String, Class<PrivilegePolicy>> entry : this.policyMap.entrySet()) {
|
|
policyDef.put(entry.getKey(), entry.getValue().getName());
|
|
}
|
|
return policyDef;
|
|
}
|
|
|
|
@Override
|
|
public List<Certificate> getCertificates(Certificate certificate) {
|
|
|
|
// validate user actually has this type of privilege
|
|
PrivilegeContext prvCtx = validate(certificate);
|
|
prvCtx.validateAction(new SimpleRestrictable(PRIVILEGE_ACTION, PRIVILEGE_ACTION_GET_CERTIFICATES));
|
|
|
|
return this.privilegeContextMap.values()
|
|
.stream()
|
|
.map(PrivilegeContext::getCertificate)
|
|
.collect(Collectors.toList());
|
|
}
|
|
|
|
@Override
|
|
public List<RoleRep> getRoles(Certificate certificate) {
|
|
|
|
// validate user actually has this type of privilege
|
|
PrivilegeContext prvCtx = validate(certificate);
|
|
prvCtx.assertHasPrivilege(PRIVILEGE_GET_ROLE);
|
|
|
|
Stream<Role> rolesStream = this.persistenceHandler.getAllRoles().stream();
|
|
|
|
// validate access to each role
|
|
rolesStream = rolesStream.filter(
|
|
role -> prvCtx.hasPrivilege(new SimpleRestrictable(PRIVILEGE_GET_ROLE, new Tuple(null, role))));
|
|
|
|
return rolesStream.map(Role::asRoleRep).collect(Collectors.toList());
|
|
}
|
|
|
|
@Override
|
|
public List<UserRep> getUsers(Certificate certificate) {
|
|
|
|
// validate user actually has this type of privilege
|
|
PrivilegeContext prvCtx = validate(certificate);
|
|
prvCtx.assertHasPrivilege(PRIVILEGE_GET_USER);
|
|
|
|
Stream<User> usersStream = this.persistenceHandler.getAllUsers().stream();
|
|
|
|
// validate access to each user
|
|
usersStream = usersStream.filter(
|
|
user -> prvCtx.hasPrivilege(new SimpleRestrictable(PRIVILEGE_GET_USER, new Tuple(null, user))));
|
|
|
|
return usersStream.map(User::asUserRep).collect(Collectors.toList());
|
|
}
|
|
|
|
@Override
|
|
public List<UserRep> queryUsers(Certificate certificate, UserRep selectorRep) {
|
|
|
|
// validate user actually has this type of privilege
|
|
PrivilegeContext prvCtx = validate(certificate);
|
|
prvCtx.assertHasPrivilege(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> 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(PRIVILEGE_GET_USER, new Tuple(null, user))))
|
|
continue;
|
|
|
|
// selections
|
|
boolean userIdSelected;
|
|
boolean usernameSelected;
|
|
boolean firstNameSelected;
|
|
boolean lastNameSelected;
|
|
boolean userStateSelected;
|
|
boolean localeSelected;
|
|
boolean roleSelected;
|
|
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());
|
|
|
|
// roles
|
|
roleSelected = isSelectedByRole(selRoles, user.getRoles());
|
|
|
|
// properties
|
|
propertySelected = isSelectedByProperty(selPropertyMap, user.getProperties());
|
|
|
|
boolean selected =
|
|
userIdSelected && usernameSelected && firstNameSelected && lastNameSelected && userStateSelected
|
|
&& localeSelected && roleSelected && propertySelected;
|
|
|
|
if (selected)
|
|
result.add(user.asUserRep());
|
|
}
|
|
|
|
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
|
|
*/
|
|
private 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
|
|
*/
|
|
private boolean isSelectedByRole(Set<String> selectionRoles, Set<String> roles) {
|
|
return selectionRoles == null || roles.containsAll(selectionRoles);
|
|
}
|
|
|
|
@Override
|
|
public UserRep addUser(Certificate certificate, UserRep userRepParam, char[] password) {
|
|
try {
|
|
|
|
// validate user actually has this type of privilege
|
|
PrivilegeContext prvCtx = validate(certificate);
|
|
prvCtx.assertHasPrivilege(PRIVILEGE_ADD_USER);
|
|
|
|
// make sure userId is not set
|
|
if (isNotEmpty(userRepParam.getUserId())) {
|
|
String msg = "UserId can not be set when adding a new user!";
|
|
throw new PrivilegeModelException(format(msg, userRepParam.getUsername()));
|
|
}
|
|
|
|
UserRep userRep = userRepParam.clone();
|
|
|
|
// set userId
|
|
userRep.setUserId(getUniqueId());
|
|
|
|
// first validate user
|
|
userRep.validate();
|
|
|
|
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(format(msg, userRep.getUsername()));
|
|
}
|
|
|
|
UserHistory history = new UserHistory();
|
|
byte[] passwordHash = null;
|
|
byte[] salt = null;
|
|
if (password != null) {
|
|
|
|
// validate password meets basic requirements
|
|
validatePassword(certificate.getLocale(), password);
|
|
|
|
// get new salt for user
|
|
salt = this.encryptionHandler.nextSalt();
|
|
|
|
// hash password
|
|
passwordHash = this.encryptionHandler.hashPassword(password, salt);
|
|
|
|
history.setLastPasswordChange(ZonedDateTime.now());
|
|
}
|
|
|
|
// create new user
|
|
User newUser = createUser(userRep, history, passwordHash, salt, false);
|
|
|
|
// detect privilege conflicts
|
|
assertNoPrivilegeConflict(newUser);
|
|
|
|
// validate this user may create such a user
|
|
prvCtx.validateAction(new SimpleRestrictable(PRIVILEGE_ADD_USER, new Tuple(null, newUser)));
|
|
|
|
// delegate to persistence handler
|
|
this.persistenceHandler.addUser(newUser);
|
|
|
|
logger.info("Created new user " + newUser.getUsername());
|
|
|
|
return newUser.asUserRep();
|
|
|
|
} finally {
|
|
clearPassword(password);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void addOrUpdateUsers(Certificate certificate, List<UserRep> userReps) throws PrivilegeException {
|
|
|
|
// validate user actually has this type of privilege
|
|
PrivilegeContext prvCtx = validate(certificate);
|
|
prvCtx.assertHasPrivilege(PRIVILEGE_ADD_USER);
|
|
|
|
List<User> toCreate = new ArrayList<>();
|
|
List<User> toUpdate = new ArrayList<>();
|
|
|
|
for (UserRep e : userReps) {
|
|
UserRep userRep = e.clone();
|
|
|
|
User user;
|
|
User existingUser = this.persistenceHandler.getUser(userRep.getUsername());
|
|
|
|
if (existingUser == null) {
|
|
|
|
// add user
|
|
|
|
// make sure userId is not set
|
|
if (isNotEmpty(userRep.getUserId())) {
|
|
String msg = "UserId can not be set when adding a new user!";
|
|
throw new PrivilegeModelException(format(msg, userRep.getUsername()));
|
|
}
|
|
|
|
// set userId
|
|
userRep.setUserId(getUniqueId());
|
|
|
|
// first validate user
|
|
userRep.validate();
|
|
|
|
validateRolesExist(userRep);
|
|
|
|
// create new user
|
|
user = createUser(userRep, new UserHistory(), null, null, false);
|
|
|
|
// detect privilege conflicts
|
|
assertNoPrivilegeConflict(user);
|
|
|
|
// validate this user may create such a user
|
|
prvCtx.validateAction(new SimpleRestrictable(PRIVILEGE_ADD_USER, new Tuple(null, user)));
|
|
|
|
toCreate.add(user);
|
|
logger.info("Creating new user " + user.getUsername());
|
|
|
|
} else {
|
|
|
|
// update user
|
|
|
|
if (userRep.getUserId() == null)
|
|
userRep.setUserId(existingUser.getUserId());
|
|
|
|
UserHistory history = existingUser.getHistory().getClone();
|
|
byte[] passwordHash = existingUser.getPassword();
|
|
byte[] salt = existingUser.getSalt();
|
|
user = createUser(userRep, history, passwordHash, salt, existingUser.isPasswordChangeRequested());
|
|
|
|
// detect privilege conflicts
|
|
assertNoPrivilegeConflict(user);
|
|
|
|
// validate this user may modify this user
|
|
prvCtx.validateAction(new SimpleRestrictable(PRIVILEGE_MODIFY_USER, new Tuple(existingUser, user)));
|
|
|
|
toUpdate.add(user);
|
|
logger.info("Updating existing user " + user.getUsername());
|
|
}
|
|
}
|
|
|
|
// delegate to persistence handler
|
|
toCreate.forEach(user -> this.persistenceHandler.addUser(user));
|
|
toUpdate.forEach(user -> this.persistenceHandler.replaceUser(user));
|
|
|
|
logger.info("Created " + toCreate.size() + " users");
|
|
logger.info("Updated " + toUpdate.size() + " users");
|
|
}
|
|
|
|
@Override
|
|
public UserRep replaceUser(Certificate certificate, UserRep userRep, char[] password) {
|
|
try {
|
|
|
|
// validate user actually has this type of privilege
|
|
PrivilegeContext prvCtx = validate(certificate);
|
|
prvCtx.assertHasPrivilege(PRIVILEGE_MODIFY_USER);
|
|
|
|
// first validate user
|
|
userRep.validate();
|
|
|
|
validateRolesExist(userRep);
|
|
|
|
// validate user exists
|
|
User existingUser = this.persistenceHandler.getUser(userRep.getUsername());
|
|
if (existingUser == null) {
|
|
String msg = "User {0} can not be replaced as it does not exist!";
|
|
throw new PrivilegeModelException(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 = format(msg, existingUser.getUserId(), userRep.getUserId());
|
|
throw new PrivilegeModelException(format(msg, userRep.getUsername()));
|
|
}
|
|
|
|
UserHistory history = existingUser.getHistory().getClone();
|
|
byte[] passwordHash = null;
|
|
byte[] salt = null;
|
|
if (password != null) {
|
|
|
|
// validate password meets basic requirements
|
|
validatePassword(certificate.getLocale(), password);
|
|
|
|
// get new salt for user
|
|
salt = this.encryptionHandler.nextSalt();
|
|
|
|
// hash password
|
|
passwordHash = this.encryptionHandler.hashPassword(password, salt);
|
|
|
|
history.setLastPasswordChange(ZonedDateTime.now());
|
|
}
|
|
|
|
User newUser = createUser(userRep, history, passwordHash, salt, existingUser.isPasswordChangeRequested());
|
|
|
|
// detect privilege conflicts
|
|
assertNoPrivilegeConflict(newUser);
|
|
|
|
// validate this user may modify this user
|
|
prvCtx.validateAction(new SimpleRestrictable(PRIVILEGE_MODIFY_USER, new Tuple(existingUser, newUser)));
|
|
|
|
// delegate to persistence handler
|
|
this.persistenceHandler.replaceUser(newUser);
|
|
|
|
logger.info("Replaced user " + newUser.getUsername());
|
|
|
|
return newUser.asUserRep();
|
|
|
|
} finally {
|
|
clearPassword(password);
|
|
}
|
|
}
|
|
|
|
private void validateRolesExist(UserRep userRep) {
|
|
// validate all roles exist
|
|
for (String role : userRep.getRoles()) {
|
|
if (this.persistenceHandler.getRole(role) == null) {
|
|
String msg = "Can not add user {0} as role {1} does not exist!";
|
|
msg = format(msg, userRep.getUsername(), role);
|
|
throw new PrivilegeModelException(msg);
|
|
}
|
|
}
|
|
}
|
|
|
|
private User createUser(UserRep userRep, UserHistory history, byte[] passwordHash, byte[] salt,
|
|
boolean passwordChangeRequested) {
|
|
String userId = userRep.getUserId();
|
|
String userName = userRep.getUsername();
|
|
String firstName = userRep.getFirstname();
|
|
String lastName = userRep.getLastname();
|
|
UserState state = userRep.getUserState();
|
|
Set<String> roles = userRep.getRoles();
|
|
Locale locale = userRep.getLocale();
|
|
Map<String, String> properties = userRep.getProperties();
|
|
return new User(userId, userName, passwordHash, salt, this.encryptionHandler.getAlgorithm(),
|
|
this.encryptionHandler.getIterations(), this.encryptionHandler.getKeyLength(), firstName, lastName,
|
|
state, roles, locale, properties, passwordChangeRequested, history);
|
|
}
|
|
|
|
@Override
|
|
public UserRep updateUser(Certificate certificate, UserRep userRep) throws PrivilegeException {
|
|
|
|
// validate user actually has this type of privilege
|
|
PrivilegeContext prvCtx = validate(certificate);
|
|
prvCtx.assertHasPrivilege(PRIVILEGE_MODIFY_USER);
|
|
|
|
// get existing user
|
|
User existingUser = this.persistenceHandler.getUser(userRep.getUsername());
|
|
if (existingUser == null)
|
|
throw new PrivilegeModelException(format("User {0} does not exist!", userRep.getUsername())); //$NON-NLS-1$
|
|
|
|
// if nothing to do, then stop
|
|
if (isEmpty(userRep.getFirstname()) && isEmpty(userRep.getLastname()) && userRep.getLocale() == null && (
|
|
userRep.getProperties() == null || userRep.getProperties().isEmpty())) {
|
|
throw new PrivilegeModelException(
|
|
format("All updateable fields are empty for update of user {0}", //$NON-NLS-1$
|
|
userRep.getUsername()));
|
|
}
|
|
|
|
String userId = existingUser.getUserId();
|
|
String username = existingUser.getUsername();
|
|
byte[] password = existingUser.getPassword();
|
|
byte[] salt = existingUser.getSalt();
|
|
String firstName = existingUser.getFirstname();
|
|
String lastName = existingUser.getLastname();
|
|
UserState userState = existingUser.getUserState();
|
|
Set<String> roles = existingUser.getRoles();
|
|
Locale locale = existingUser.getLocale();
|
|
Map<String, String> propertyMap = existingUser.getProperties();
|
|
|
|
String hashAlgorithm = existingUser.getHashAlgorithm();
|
|
int hashIterations = existingUser.getHashIterations();
|
|
int hashKeyLength = existingUser.getHashKeyLength();
|
|
|
|
// get updated fields
|
|
if (isNotEmpty(userRep.getFirstname()))
|
|
firstName = userRep.getFirstname();
|
|
if (isNotEmpty(userRep.getLastname()))
|
|
lastName = userRep.getLastname();
|
|
if (userRep.getProperties() != null && !userRep.getProperties().isEmpty())
|
|
propertyMap = userRep.getProperties();
|
|
|
|
if (userRep.getLocale() != null)
|
|
locale = userRep.getLocale();
|
|
if (userRep.getUserState() != null)
|
|
userState = userRep.getUserState();
|
|
if (userRep.getRoles() != null && !userRep.getRoles().equals(roles))
|
|
roles = userRep.getRoles();
|
|
|
|
// create new user
|
|
User newUser = new User(userId, username, password, salt, hashAlgorithm, hashIterations, hashKeyLength,
|
|
firstName, lastName, userState, roles, locale, propertyMap, existingUser.isPasswordChangeRequested(),
|
|
existingUser.getHistory().getClone());
|
|
|
|
// detect privilege conflicts
|
|
assertNoPrivilegeConflict(newUser);
|
|
|
|
// validate this user may modify this user
|
|
prvCtx.validateAction(new SimpleRestrictable(PRIVILEGE_MODIFY_USER, new Tuple(existingUser, newUser)));
|
|
|
|
// delegate to persistence handler
|
|
this.persistenceHandler.replaceUser(newUser);
|
|
|
|
logger.info("Updated user " + newUser.getUsername());
|
|
|
|
// update any existing sessions for this user
|
|
updateExistingSessionsForUser(newUser);
|
|
|
|
return newUser.asUserRep();
|
|
}
|
|
|
|
@Override
|
|
public UserRep removeUser(Certificate certificate, String username) {
|
|
|
|
// validate user actually has this type of privilege
|
|
PrivilegeContext prvCtx = validate(certificate);
|
|
prvCtx.assertHasPrivilege(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(format(msg, username));
|
|
}
|
|
|
|
// validate this user may remove this user
|
|
prvCtx.validateAction(new SimpleRestrictable(PRIVILEGE_REMOVE_USER, new Tuple(null, existingUser)));
|
|
|
|
// delegate user removal to persistence handler
|
|
invalidSessionsFor(existingUser);
|
|
this.persistenceHandler.removeUser(username);
|
|
|
|
logger.info("Removed user " + username);
|
|
|
|
return existingUser.asUserRep();
|
|
}
|
|
|
|
@Override
|
|
public UserRep addRoleToUser(Certificate certificate, String username, String roleName) {
|
|
|
|
// validate user actually has this type of privilege
|
|
PrivilegeContext prvCtx = validate(certificate);
|
|
prvCtx.assertHasPrivilege(PRIVILEGE_ADD_ROLE_TO_USER);
|
|
|
|
// get user
|
|
User existingUser = this.persistenceHandler.getUser(username);
|
|
if (existingUser == null)
|
|
throw new PrivilegeModelException(format("User {0} does not exist!", username)); //$NON-NLS-1$
|
|
|
|
// validate that this user may add this role to this user
|
|
prvCtx.validateAction(new SimpleRestrictable(PRIVILEGE_ADD_ROLE_TO_USER, new Tuple(existingUser, roleName)));
|
|
|
|
// check that user not already has role
|
|
Set<String> currentRoles = existingUser.getRoles();
|
|
if (currentRoles.contains(roleName)) {
|
|
String msg = format("User {0} already has role {1}", username, roleName); //$NON-NLS-1$
|
|
throw new PrivilegeModelException(msg);
|
|
}
|
|
|
|
// validate that the role exists
|
|
if (this.persistenceHandler.getRole(roleName) == null) {
|
|
String msg = format("Role {0} does not exist!", roleName); //$NON-NLS-1$
|
|
throw new PrivilegeModelException(msg);
|
|
}
|
|
|
|
// create new user
|
|
Set<String> newRoles = new HashSet<>(currentRoles);
|
|
newRoles.add(roleName);
|
|
|
|
User newUser = new User(existingUser.getUserId(), existingUser.getUsername(), existingUser.getPassword(),
|
|
existingUser.getSalt(), existingUser.getHashAlgorithm(), existingUser.getHashIterations(),
|
|
existingUser.getHashKeyLength(), existingUser.getFirstname(), existingUser.getLastname(),
|
|
existingUser.getUserState(), newRoles, existingUser.getLocale(), existingUser.getProperties(),
|
|
existingUser.isPasswordChangeRequested(), existingUser.getHistory().getClone());
|
|
|
|
// detect privilege conflicts
|
|
assertNoPrivilegeConflict(newUser);
|
|
|
|
// delegate user replacement to persistence handler
|
|
this.persistenceHandler.replaceUser(newUser);
|
|
|
|
logger.info("Added role " + roleName + " to " + newUser.getUsername());
|
|
|
|
// update any existing sessions for this user
|
|
updateExistingSessionsForUser(newUser);
|
|
|
|
return newUser.asUserRep();
|
|
}
|
|
|
|
@Override
|
|
public UserRep removeRoleFromUser(Certificate certificate, String username, String roleName) {
|
|
|
|
// validate user actually has this type of privilege
|
|
PrivilegeContext prvCtx = validate(certificate);
|
|
prvCtx.assertHasPrivilege(PRIVILEGE_REMOVE_ROLE_FROM_USER);
|
|
|
|
// get User
|
|
User existingUser = this.persistenceHandler.getUser(username);
|
|
if (existingUser == null)
|
|
throw new PrivilegeModelException(format("User {0} does not exist!", username)); //$NON-NLS-1$
|
|
|
|
// validate that this user may remove this role from this user
|
|
prvCtx.validateAction(
|
|
new SimpleRestrictable(PRIVILEGE_REMOVE_ROLE_FROM_USER, new Tuple(existingUser, roleName)));
|
|
|
|
// ignore if user does not have role
|
|
Set<String> currentRoles = existingUser.getRoles();
|
|
if (!currentRoles.contains(roleName)) {
|
|
String msg = format("User {0} does not have role {1}", existingUser.getUsername(), roleName); //$NON-NLS-1$
|
|
throw new PrivilegeModelException(msg);
|
|
}
|
|
|
|
// create new user
|
|
Set<String> newRoles = new HashSet<>(currentRoles);
|
|
newRoles.remove(roleName);
|
|
User newUser = new User(existingUser.getUserId(), existingUser.getUsername(), existingUser.getPassword(),
|
|
existingUser.getSalt(), existingUser.getHashAlgorithm(), existingUser.getHashIterations(),
|
|
existingUser.getHashKeyLength(), existingUser.getFirstname(), existingUser.getLastname(),
|
|
existingUser.getUserState(), newRoles, existingUser.getLocale(), existingUser.getProperties(),
|
|
existingUser.isPasswordChangeRequested(), existingUser.getHistory().getClone());
|
|
|
|
// delegate user replacement to persistence handler
|
|
this.persistenceHandler.replaceUser(newUser);
|
|
|
|
logger.info("Removed role " + roleName + " from " + newUser.getUsername());
|
|
|
|
// update any existing sessions for this user
|
|
updateExistingSessionsForUser(newUser);
|
|
|
|
return newUser.asUserRep();
|
|
}
|
|
|
|
@Override
|
|
public UserRep setUserLocale(Certificate certificate, String username, Locale locale) {
|
|
|
|
// validate user actually has this type of privilege
|
|
PrivilegeContext prvCtx = validate(certificate);
|
|
prvCtx.assertHasPrivilege(PRIVILEGE_SET_USER_LOCALE);
|
|
|
|
// get User
|
|
User existingUser = this.persistenceHandler.getUser(username);
|
|
if (existingUser == null)
|
|
throw new PrivilegeModelException(format("User {0} does not exist!", username)); //$NON-NLS-1$
|
|
|
|
// create new user
|
|
User newUser = new User(existingUser.getUserId(), existingUser.getUsername(), existingUser.getPassword(),
|
|
existingUser.getSalt(), existingUser.getHashAlgorithm(), existingUser.getHashIterations(),
|
|
existingUser.getHashKeyLength(), existingUser.getFirstname(), existingUser.getLastname(),
|
|
existingUser.getUserState(), existingUser.getRoles(), locale, existingUser.getProperties(),
|
|
existingUser.isPasswordChangeRequested(), existingUser.getHistory().getClone());
|
|
|
|
// 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(PRIVILEGE_SET_USER_LOCALE, new Tuple(existingUser, newUser)));
|
|
}
|
|
|
|
// delegate user replacement to persistence handler
|
|
this.persistenceHandler.replaceUser(newUser);
|
|
|
|
// perform automatic persisting, if enabled
|
|
if (this.autoPersistOnUserChangesData) {
|
|
this.persistenceHandler.persist();
|
|
}
|
|
|
|
logger.info("Set locale to " + locale + " for " + newUser.getUsername());
|
|
|
|
return newUser.asUserRep();
|
|
}
|
|
|
|
@Override
|
|
public void requirePasswordChange(Certificate certificate, String username) throws PrivilegeException {
|
|
|
|
// validate user actually has this type of privilege
|
|
PrivilegeContext prvCtx = validate(certificate);
|
|
prvCtx.assertHasPrivilege(PRIVILEGE_REQUIRE_PASSWORD_CHANGE);
|
|
|
|
// get User
|
|
User existingUser = this.persistenceHandler.getUser(username);
|
|
if (existingUser == null)
|
|
throw new PrivilegeModelException(format("User {0} does not exist!", username)); //$NON-NLS-1$
|
|
|
|
if (existingUser.getUserState().isRemote())
|
|
throw new PrivilegeModelException(
|
|
format("User {0} is remote and can not set password!", username)); //$NON-NLS-1$
|
|
|
|
// create new user
|
|
User newUser = new User(existingUser.getUserId(), existingUser.getUsername(), existingUser.getPassword(),
|
|
existingUser.getSalt(), existingUser.getHashAlgorithm(), existingUser.getHashIterations(),
|
|
existingUser.getHashKeyLength(), existingUser.getFirstname(), existingUser.getLastname(),
|
|
existingUser.getUserState(), existingUser.getRoles(), existingUser.getLocale(),
|
|
existingUser.getProperties(), true, existingUser.getHistory().getClone());
|
|
|
|
// delegate user replacement to persistence handler
|
|
this.persistenceHandler.replaceUser(newUser);
|
|
|
|
// perform automatic persisting, if enabled
|
|
if (this.autoPersistOnUserChangesData) {
|
|
this.persistenceHandler.persist();
|
|
}
|
|
|
|
logger.info("Requiring user " + newUser.getUsername() + " to change their password on next login.");
|
|
}
|
|
|
|
@Override
|
|
public void setUserPassword(Certificate certificate, String username, char[] password) {
|
|
try {
|
|
|
|
// validate user actually has this type of privilege
|
|
PrivilegeContext prvCtx = validate(certificate);
|
|
prvCtx.assertHasPrivilege(PRIVILEGE_SET_USER_PASSWORD);
|
|
|
|
// get User
|
|
User existingUser = this.persistenceHandler.getUser(username);
|
|
if (existingUser == null)
|
|
throw new PrivilegeModelException(format("User {0} does not exist!", username)); //$NON-NLS-1$
|
|
|
|
UserHistory history = existingUser.getHistory().getClone();
|
|
|
|
byte[] passwordHash = null;
|
|
byte[] salt = null;
|
|
if (password != null) {
|
|
|
|
// validate password meets basic requirements
|
|
validatePassword(certificate.getLocale(), password);
|
|
|
|
// get new salt for user
|
|
salt = this.encryptionHandler.nextSalt();
|
|
|
|
// hash password
|
|
passwordHash = this.encryptionHandler.hashPassword(password, salt);
|
|
|
|
history.setLastPasswordChange(ZonedDateTime.now());
|
|
}
|
|
|
|
// create new user
|
|
User newUser = new User(existingUser.getUserId(), existingUser.getUsername(), passwordHash, salt,
|
|
this.encryptionHandler.getAlgorithm(), this.encryptionHandler.getIterations(),
|
|
this.encryptionHandler.getKeyLength(), existingUser.getFirstname(), existingUser.getLastname(),
|
|
existingUser.getUserState(), 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(PRIVILEGE_SET_USER_PASSWORD, value));
|
|
}
|
|
|
|
// delegate user replacement to persistence handler
|
|
this.persistenceHandler.replaceUser(newUser);
|
|
|
|
// perform automatic persisting, if enabled
|
|
if (this.autoPersistOnUserChangesData) {
|
|
this.persistenceHandler.persist();
|
|
}
|
|
|
|
if (certificate.getUsage() == Usage.SET_PASSWORD) {
|
|
invalidate(certificate);
|
|
}
|
|
|
|
if (password == null)
|
|
logger.info("Cleared password for " + newUser.getUsername());
|
|
else
|
|
logger.info("Updated password for " + newUser.getUsername());
|
|
|
|
} finally {
|
|
clearPassword(password);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public UserRep setUserState(Certificate certificate, String username, UserState state) {
|
|
|
|
// validate user actually has this type of privilege
|
|
PrivilegeContext prvCtx = validate(certificate);
|
|
prvCtx.assertHasPrivilege(PRIVILEGE_SET_USER_STATE);
|
|
|
|
// get User
|
|
User existingUser = this.persistenceHandler.getUser(username);
|
|
if (existingUser == null)
|
|
throw new PrivilegeModelException(format("User {0} does not exist!", username)); //$NON-NLS-1$
|
|
|
|
// create new user
|
|
User newUser = new User(existingUser.getUserId(), existingUser.getUsername(), existingUser.getPassword(),
|
|
existingUser.getSalt(), existingUser.getHashAlgorithm(), existingUser.getHashIterations(),
|
|
existingUser.getHashKeyLength(), existingUser.getFirstname(), existingUser.getLastname(), state,
|
|
existingUser.getRoles(), existingUser.getLocale(), existingUser.getProperties(),
|
|
existingUser.isPasswordChangeRequested(), existingUser.getHistory().getClone());
|
|
|
|
// validate that this user may modify this user's state
|
|
prvCtx.validateAction(new SimpleRestrictable(PRIVILEGE_SET_USER_STATE, new Tuple(existingUser, newUser)));
|
|
|
|
// delegate user replacement to persistence handler
|
|
this.persistenceHandler.replaceUser(newUser);
|
|
|
|
logger.info("Set state of user " + newUser.getUsername() + " to " + state);
|
|
|
|
return newUser.asUserRep();
|
|
}
|
|
|
|
@Override
|
|
public RoleRep addRole(Certificate certificate, RoleRep roleRep) {
|
|
|
|
// validate user actually has this type of privilege
|
|
PrivilegeContext prvCtx = validate(certificate);
|
|
prvCtx.assertHasPrivilege(PRIVILEGE_ADD_ROLE);
|
|
|
|
// first validate role
|
|
roleRep.validate();
|
|
|
|
// validate role does not exist
|
|
if (this.persistenceHandler.getRole(roleRep.getName()) != null) {
|
|
String msg = format("Can not add role {0} as it already exists!", roleRep.getName());
|
|
throw new PrivilegeModelException(msg);
|
|
}
|
|
|
|
// create new role from RoleRep
|
|
Role newRole = new Role(roleRep);
|
|
|
|
// validate that this user may add this new role
|
|
prvCtx.validateAction(new SimpleRestrictable(PRIVILEGE_ADD_ROLE, new Tuple(null, newRole)));
|
|
|
|
// validate policy if not null
|
|
validatePolicies(newRole);
|
|
|
|
// delegate to persistence handler
|
|
this.persistenceHandler.addRole(newRole);
|
|
|
|
logger.info("Added new role " + newRole.getName());
|
|
|
|
return newRole.asRoleRep();
|
|
}
|
|
|
|
@Override
|
|
public RoleRep replaceRole(Certificate certificate, RoleRep roleRep) {
|
|
|
|
// validate user actually has this type of privilege
|
|
PrivilegeContext prvCtx = validate(certificate);
|
|
prvCtx.assertHasPrivilege(PRIVILEGE_MODIFY_ROLE);
|
|
|
|
// first validate role
|
|
roleRep.validate();
|
|
|
|
// validate role does exist
|
|
Role existingRole = this.persistenceHandler.getRole(roleRep.getName());
|
|
if (existingRole == null) {
|
|
String msg = format("Can not replace role {0} as it does not exist!", roleRep.getName());
|
|
throw new PrivilegeModelException(msg);
|
|
}
|
|
|
|
// create new role from RoleRep
|
|
Role newRole = new Role(roleRep);
|
|
|
|
// detect privilege conflicts
|
|
assertNoPrivilegeConflict(newRole);
|
|
|
|
// validate that this user may modify this role
|
|
prvCtx.validateAction(new SimpleRestrictable(PRIVILEGE_MODIFY_ROLE, new Tuple(existingRole, newRole)));
|
|
|
|
// validate policy if not null
|
|
validatePolicies(newRole);
|
|
|
|
// delegate to persistence handler
|
|
this.persistenceHandler.replaceRole(newRole);
|
|
|
|
logger.info("Replaced role " + newRole.getName());
|
|
|
|
// update any existing certificates with new role
|
|
updateExistingSessionsWithNewRole(newRole);
|
|
|
|
return newRole.asRoleRep();
|
|
}
|
|
|
|
@Override
|
|
public RoleRep removeRole(Certificate certificate, String roleName) {
|
|
|
|
// validate user actually has this type of privilege
|
|
PrivilegeContext prvCtx = validate(certificate);
|
|
prvCtx.assertHasPrivilege(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, roles, null, null, null);
|
|
List<UserRep> usersWithRole = 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 = 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(format(msg, roleName));
|
|
}
|
|
|
|
// validate that this user may remove this role
|
|
prvCtx.validateAction(new SimpleRestrictable(PRIVILEGE_REMOVE_ROLE, new Tuple(null, existingRole)));
|
|
|
|
// delegate role removal to persistence handler
|
|
this.persistenceHandler.removeRole(roleName);
|
|
|
|
logger.info("Removed role " + roleName);
|
|
|
|
return existingRole.asRoleRep();
|
|
}
|
|
|
|
@Override
|
|
public RoleRep addOrReplacePrivilegeOnRole(Certificate certificate, String roleName, PrivilegeRep privilegeRep) {
|
|
|
|
// validate user actually has this type of privilege
|
|
PrivilegeContext prvCtx = validate(certificate);
|
|
prvCtx.assertHasPrivilege(PRIVILEGE_MODIFY_ROLE);
|
|
|
|
// validate PrivilegeRep
|
|
privilegeRep.validate();
|
|
|
|
// get role
|
|
Role existingRole = this.persistenceHandler.getRole(roleName);
|
|
if (existingRole == null) {
|
|
String msg = format("Role {0} does not exist!", roleName); //$NON-NLS-1$
|
|
throw new PrivilegeModelException(msg);
|
|
}
|
|
|
|
// validate that policy exists if needed
|
|
String policy = privilegeRep.getPolicy();
|
|
if (policy != null && !this.policyMap.containsKey(policy)) {
|
|
String msg = "Policy {0} for Privilege {1} does not exist"; //$NON-NLS-1$
|
|
msg = format(msg, policy, privilegeRep.getName());
|
|
throw new PrivilegeModelException(msg);
|
|
}
|
|
|
|
// create new role with the additional privilege
|
|
IPrivilege newPrivilege = new PrivilegeImpl(privilegeRep);
|
|
|
|
// copy existing privileges
|
|
Set<String> existingPrivilegeNames = existingRole.getPrivilegeNames();
|
|
Map<String, IPrivilege> privilegeMap = new HashMap<>(existingPrivilegeNames.size() + 1);
|
|
for (String name : existingPrivilegeNames) {
|
|
IPrivilege privilege = existingRole.getPrivilege(name);
|
|
privilegeMap.put(name, privilege);
|
|
}
|
|
|
|
// add new one
|
|
privilegeMap.put(newPrivilege.getName(), newPrivilege);
|
|
|
|
// create new role
|
|
Role newRole = new Role(existingRole.getName(), privilegeMap);
|
|
|
|
// detect privilege conflicts
|
|
assertNoPrivilegeConflict(newRole);
|
|
|
|
// validate that this user may modify this role
|
|
prvCtx.validateAction(new SimpleRestrictable(PRIVILEGE_MODIFY_ROLE, new Tuple(existingRole, newRole)));
|
|
|
|
// delegate role replacement to persistence handler
|
|
this.persistenceHandler.replaceRole(newRole);
|
|
|
|
logger.info("Added/replaced privilege " + privilegeRep.getName() + " to " + roleName);
|
|
|
|
// update any existing certificates with new role
|
|
updateExistingSessionsWithNewRole(newRole);
|
|
|
|
return newRole.asRoleRep();
|
|
}
|
|
|
|
@Override
|
|
public RoleRep removePrivilegeFromRole(Certificate certificate, String roleName, String privilegeName) {
|
|
|
|
// validate user actually has this type of privilege
|
|
PrivilegeContext prvCtx = validate(certificate);
|
|
prvCtx.assertHasPrivilege(PRIVILEGE_MODIFY_ROLE);
|
|
|
|
// get role
|
|
Role existingRole = this.persistenceHandler.getRole(roleName);
|
|
if (existingRole == null) {
|
|
throw new PrivilegeModelException(format("Role {0} does not exist!", roleName)); //$NON-NLS-1$
|
|
}
|
|
|
|
// ignore if role does not have privilege
|
|
if (!existingRole.hasPrivilege(privilegeName)) {
|
|
String msg = format("Role {0} does not have Privilege {1}", roleName, privilegeName); //$NON-NLS-1$
|
|
throw new PrivilegeModelException(msg);
|
|
}
|
|
|
|
// create new set of privileges with out the to removed privilege
|
|
Set<String> privilegeNames = existingRole.getPrivilegeNames();
|
|
Map<String, IPrivilege> newPrivileges = new HashMap<>(privilegeNames.size() - 1);
|
|
for (String name : privilegeNames) {
|
|
IPrivilege privilege = existingRole.getPrivilege(name);
|
|
if (!privilege.getName().equals(privilegeName))
|
|
newPrivileges.put(privilege.getName(), privilege);
|
|
}
|
|
|
|
// create new role
|
|
Role newRole = new Role(existingRole.getName(), newPrivileges);
|
|
|
|
// validate that this user may modify this role
|
|
prvCtx.validateAction(new SimpleRestrictable(PRIVILEGE_MODIFY_ROLE, new Tuple(existingRole, newRole)));
|
|
|
|
// delegate user replacement to persistence handler
|
|
this.persistenceHandler.replaceRole(newRole);
|
|
|
|
logger.info("Removed privilege " + privilegeName + " from " + roleName);
|
|
|
|
// update any existing certificates with new role
|
|
updateExistingSessionsWithNewRole(newRole);
|
|
|
|
return newRole.asRoleRep();
|
|
}
|
|
|
|
/**
|
|
* Replaces any existing {@link PrivilegeContext} for the given user by updating with the new user object
|
|
*
|
|
* @param newUser
|
|
* the new user to update with
|
|
*/
|
|
private void updateExistingSessionsForUser(User newUser) {
|
|
synchronized (this.privilegeContextMap) {
|
|
List<PrivilegeContext> ctxs = new ArrayList<>(this.privilegeContextMap.values());
|
|
for (PrivilegeContext ctx : ctxs) {
|
|
if (ctx.getUserRep().getUsername().equals(newUser.getUsername())) {
|
|
Certificate cert = ctx.getCertificate();
|
|
cert = buildCertificate(cert.getUsage(), newUser, cert.getAuthToken(), cert.getSessionId(),
|
|
cert.getSource(), cert.getLoginTime(), cert.isKeepAlive());
|
|
PrivilegeContext privilegeContext = buildPrivilegeContext(cert, newUser);
|
|
this.privilegeContextMap.put(cert.getSessionId(), privilegeContext);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Replaces any existing {@link PrivilegeContext} for users with the given role
|
|
*
|
|
* @param role
|
|
* the role to update with
|
|
*/
|
|
private void updateExistingSessionsWithNewRole(Role role) {
|
|
synchronized (this.privilegeContextMap) {
|
|
List<PrivilegeContext> ctxs = new ArrayList<>(this.privilegeContextMap.values());
|
|
for (PrivilegeContext ctx : ctxs) {
|
|
if (ctx.getUserRep().hasRole(role.getName())) {
|
|
User user = this.persistenceHandler.getUser(ctx.getUsername());
|
|
if (user == null)
|
|
continue;
|
|
|
|
Certificate cert = ctx.getCertificate();
|
|
cert = buildCertificate(cert.getUsage(), user, cert.getAuthToken(), cert.getSessionId(),
|
|
cert.getSource(), cert.getLoginTime(), cert.isKeepAlive());
|
|
PrivilegeContext privilegeContext = buildPrivilegeContext(cert, user);
|
|
this.privilegeContextMap.put(cert.getSessionId(), privilegeContext);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void invalidSessionsFor(User user) {
|
|
List<PrivilegeContext> ctxs;
|
|
synchronized (this.privilegeContextMap) {
|
|
ctxs = new ArrayList<>(this.privilegeContextMap.values());
|
|
}
|
|
|
|
for (PrivilegeContext ctx : ctxs) {
|
|
if (ctx.getUserRep().getUsername().equals(user.getUsername())) {
|
|
invalidate(ctx.getCertificate());
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void initiateChallengeFor(Usage usage, String username) {
|
|
initiateChallengeFor(usage, username, SOURCE_UNKNOWN);
|
|
}
|
|
|
|
@Override
|
|
public void initiateChallengeFor(Usage usage, String username, String source) {
|
|
DBC.PRE.assertNotEmpty("source must not be empty!", source);
|
|
|
|
// get User
|
|
User user = this.persistenceHandler.getUser(username);
|
|
if (user == null) {
|
|
throw new PrivilegeModelException(format("User {0} does not exist!", username)); //$NON-NLS-1$
|
|
}
|
|
|
|
// initiate the challenge
|
|
this.userChallengeHandler.initiateChallengeFor(usage, user, source);
|
|
|
|
logger.info(format("Initiated Challenge for {0} with usage {1}", username, usage));
|
|
}
|
|
|
|
@Override
|
|
public Certificate validateChallenge(String username, String challenge) throws PrivilegeException {
|
|
return validateChallenge(username, challenge, "unknown");
|
|
}
|
|
|
|
@Override
|
|
public Certificate validateChallenge(String username, String challenge, String source) throws PrivilegeException {
|
|
DBC.PRE.assertNotEmpty("source must not be empty!", source);
|
|
|
|
// get User
|
|
User user = this.persistenceHandler.getUser(username);
|
|
if (user == null) {
|
|
throw new PrivilegeModelException(format("User {0} does not exist!", username)); //$NON-NLS-1$
|
|
}
|
|
|
|
// validate the response
|
|
UserChallenge userChallenge = this.userChallengeHandler.validateResponse(user, challenge);
|
|
String authToken = this.encryptionHandler.nextToken();
|
|
String sessionId = UUID.randomUUID().toString();
|
|
|
|
// create a new certificate, with details of the user
|
|
Usage usage = userChallenge.getUsage();
|
|
Certificate certificate = buildCertificate(usage, user, authToken, sessionId, userChallenge.getSource(),
|
|
ZonedDateTime.now(), false);
|
|
|
|
PrivilegeContext privilegeContext = buildPrivilegeContext(certificate, user);
|
|
this.privilegeContextMap.put(sessionId, privilegeContext);
|
|
|
|
if (!source.equals("unknown") && !source.equals(userChallenge.getSource())) {
|
|
logger.warn("Challenge request and response source's are different: request: " + userChallenge.getSource()
|
|
+ " to " + source);
|
|
}
|
|
|
|
persistSessions();
|
|
|
|
logger.info(format("Challenge validated for user {0} with usage {1}", username, usage));
|
|
return certificate;
|
|
}
|
|
|
|
@Override
|
|
public Certificate authenticate(String username, char[] password, boolean keepAlive) {
|
|
return authenticate(username, password, "unknown", Usage.ANY, keepAlive);
|
|
}
|
|
|
|
@Override
|
|
public Certificate authenticate(String username, char[] password, String source, Usage usage, boolean keepAlive) {
|
|
DBC.PRE.assertNotEmpty("source must not be empty!", source);
|
|
|
|
try {
|
|
// username must be at least 2 characters in length
|
|
if (username == null || username.length() < 2) {
|
|
String msg = format("The given username ''{0}'' is shorter than 2 characters", username); //$NON-NLS-1$
|
|
throw new InvalidCredentialsException(msg);
|
|
}
|
|
|
|
// check the password
|
|
User user = checkCredentialsAndUserState(username, password);
|
|
|
|
// validate user has at least one role
|
|
Set<String> userRoles = user.getRoles();
|
|
if (userRoles.isEmpty())
|
|
throw new InvalidCredentialsException(
|
|
format("User {0} does not have any roles defined!", username)); //$NON-NLS-1$
|
|
|
|
if (user.isPasswordChangeRequested()) {
|
|
if (usage == Usage.SINGLE)
|
|
throw new IllegalStateException("Password change requested!");
|
|
usage = Usage.SET_PASSWORD;
|
|
}
|
|
|
|
// get 2 auth tokens
|
|
String authToken = this.encryptionHandler.nextToken();
|
|
|
|
// get next session id
|
|
String sessionId = UUID.randomUUID().toString();
|
|
|
|
// create a new certificate, with details of the user
|
|
Certificate certificate = buildCertificate(usage, user, authToken, sessionId, source, ZonedDateTime.now(),
|
|
keepAlive);
|
|
|
|
PrivilegeContext privilegeContext = buildPrivilegeContext(certificate, user);
|
|
this.privilegeContextMap.put(sessionId, privilegeContext);
|
|
|
|
persistSessions();
|
|
|
|
// save last login
|
|
if (user.getHistory().isFirstLoginEmpty())
|
|
user.getHistory().setFirstLogin(ZonedDateTime.now());
|
|
user.getHistory().setLastLogin(ZonedDateTime.now());
|
|
this.persistenceHandler.replaceUser(user);
|
|
if (this.autoPersistOnUserChangesData)
|
|
this.persistenceHandler.persist();
|
|
|
|
// log
|
|
logger.info(format("User {0} authenticated: {1}", username, certificate)); //$NON-NLS-1$
|
|
|
|
// return the certificate
|
|
return certificate;
|
|
|
|
} catch (PrivilegeException e) {
|
|
throw e;
|
|
} catch (RuntimeException e) {
|
|
logger.error(e.getMessage(), e);
|
|
String msg = "User {0} failed to authenticate: {1}"; //$NON-NLS-1$
|
|
msg = format(msg, username, e.getMessage());
|
|
throw new PrivilegeException(msg, e);
|
|
} finally {
|
|
clearPassword(password);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public Certificate authenticateSingleSignOn(Object data, boolean keepAlive) throws PrivilegeException {
|
|
return authenticateSingleSignOn(data, "unknown", keepAlive);
|
|
}
|
|
|
|
@Override
|
|
public Certificate authenticateSingleSignOn(Object data, String source, boolean keepAlive)
|
|
throws PrivilegeException {
|
|
DBC.PRE.assertNotEmpty("source must not be empty!", source);
|
|
if (this.ssoHandler == null)
|
|
throw new IllegalStateException("The SSO Handler is not configured!");
|
|
|
|
User user = this.ssoHandler.authenticateSingleSignOn(data);
|
|
DBC.PRE.assertEquals("SSO Users must have UserState.REMOTE!", UserState.REMOTE, user.getUserState());
|
|
user.getHistory().setLastLogin(ZonedDateTime.now());
|
|
|
|
// persist this user
|
|
User internalUser = this.persistenceHandler.getUser(user.getUsername());
|
|
if (internalUser == null) {
|
|
user.getHistory().setFirstLogin(ZonedDateTime.now());
|
|
this.persistenceHandler.addUser(user);
|
|
} else {
|
|
user.getHistory().setFirstLogin(internalUser.getHistory().getFirstLogin());
|
|
this.persistenceHandler.replaceUser(user);
|
|
}
|
|
|
|
if (this.autoPersistOnUserChangesData)
|
|
this.persistenceHandler.persist();
|
|
|
|
// get 2 auth tokens
|
|
String authToken = this.encryptionHandler.nextToken();
|
|
|
|
// get next session id
|
|
String sessionId = UUID.randomUUID().toString();
|
|
|
|
// create a new certificate, with details of the user
|
|
Certificate certificate = buildCertificate(Usage.ANY, user, authToken, sessionId, source, ZonedDateTime.now(),
|
|
keepAlive);
|
|
|
|
PrivilegeContext privilegeContext = buildPrivilegeContext(certificate, user);
|
|
this.privilegeContextMap.put(sessionId, privilegeContext);
|
|
|
|
persistSessions();
|
|
|
|
// log
|
|
logger.info(format("User {0} authenticated: {1}", user.getUsername(), certificate)); //$NON-NLS-1$
|
|
|
|
return certificate;
|
|
}
|
|
|
|
@Override
|
|
public Certificate refresh(Certificate certificate, String source) throws AccessDeniedException {
|
|
DBC.PRE.assertNotNull("certificate must not be null!", certificate);
|
|
|
|
try {
|
|
// username must be at least 2 characters in length
|
|
if (!this.allowSessionRefresh)
|
|
throw new AccessDeniedException("Refreshing of sessions not allowed!");
|
|
|
|
validate(certificate);
|
|
|
|
if (!certificate.isKeepAlive())
|
|
throw new AccessDeniedException("Refreshing of session not allowed!");
|
|
|
|
if (!certificate.getSource().equals(source)) {
|
|
logger.error("Source of existing session {} is not the same as the refresh request's source {}",
|
|
certificate.getSource(), source);
|
|
}
|
|
|
|
// check the password
|
|
User user = this.persistenceHandler.getUser(certificate.getUsername());
|
|
|
|
// get 2 auth tokens
|
|
String authToken = this.encryptionHandler.nextToken();
|
|
|
|
// get next session id
|
|
String sessionId = UUID.randomUUID().toString();
|
|
|
|
// create a new certificate, with details of the user
|
|
Certificate refreshedCert = buildCertificate(certificate.getUsage(), user, authToken, sessionId, source,
|
|
ZonedDateTime.now(), true);
|
|
|
|
PrivilegeContext privilegeContext = buildPrivilegeContext(refreshedCert, user);
|
|
this.privilegeContextMap.put(sessionId, privilegeContext);
|
|
|
|
// invalidate the previous session
|
|
invalidate(certificate);
|
|
|
|
persistSessions();
|
|
|
|
// log
|
|
logger.info(format("User {0} refreshed session: {1}", user.getUsername(), refreshedCert)); //$NON-NLS-1$
|
|
|
|
// return the certificate
|
|
return refreshedCert;
|
|
|
|
} catch (PrivilegeException e) {
|
|
throw e;
|
|
} catch (RuntimeException e) {
|
|
logger.error(e.getMessage(), e);
|
|
String msg = "User {0} failed to refresh session: {1}"; //$NON-NLS-1$
|
|
msg = format(msg, certificate.getUsername(), e.getMessage());
|
|
throw new PrivilegeException(msg, e);
|
|
}
|
|
}
|
|
|
|
private Certificate buildCertificate(Usage usage, User user, String authToken, String sessionId, String source,
|
|
ZonedDateTime loginTime, boolean keepAlive) {
|
|
DBC.PRE.assertNotEmpty("source must not be empty!", source);
|
|
Set<String> userRoles = user.getRoles();
|
|
return new Certificate(usage, sessionId, user.getUsername(), user.getFirstname(), user.getLastname(),
|
|
user.getUserState(), authToken, source, loginTime, keepAlive && this.allowSessionRefresh,
|
|
user.getLocale(), userRoles, new HashMap<>(user.getProperties()));
|
|
}
|
|
|
|
private synchronized boolean persistSessions() {
|
|
if (!this.persistSessions)
|
|
return false;
|
|
|
|
List<Certificate> sessions = new ArrayList<>(this.privilegeContextMap.values()).stream()
|
|
.map(PrivilegeContext::getCertificate)
|
|
.filter(c -> !c.getUserState().isSystem())
|
|
.collect(Collectors.toList());
|
|
|
|
try (OutputStream fout = Files.newOutputStream(this.persistSessionsPath.toPath());
|
|
OutputStream outputStream = AesCryptoHelper.wrapEncrypt(this.secretKey, fout)) {
|
|
|
|
CertificateStubsDomWriter writer = new CertificateStubsDomWriter(sessions, outputStream);
|
|
writer.write();
|
|
outputStream.flush();
|
|
|
|
} catch (Exception e) {
|
|
logger.error("Failed to persist sessions!", e);
|
|
if (this.persistSessionsPath.exists() && !this.persistSessionsPath.delete()) {
|
|
logger.error("Failed to delete sessions file after failing to write to it, at "
|
|
+ this.persistSessionsPath.getAbsolutePath());
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private void loadSessions() {
|
|
if (!this.persistSessions) {
|
|
logger.info("Persisting of sessions not enabled, so not loading!.");
|
|
return;
|
|
}
|
|
|
|
if (!this.persistSessionsPath.exists()) {
|
|
logger.info("Sessions file does not exist");
|
|
return;
|
|
}
|
|
|
|
if (!this.persistSessionsPath.isFile())
|
|
throw new PrivilegeModelException(
|
|
"Sessions data file is not a file but exists at " + this.persistSessionsPath.getAbsolutePath());
|
|
|
|
List<CertificateStub> certificateStubs;
|
|
try (InputStream fin = Files.newInputStream(this.persistSessionsPath.toPath());
|
|
InputStream inputStream = AesCryptoHelper.wrapDecrypt(this.secretKey, fin)) {
|
|
|
|
CertificateStubsSaxReader reader = new CertificateStubsSaxReader(inputStream);
|
|
certificateStubs = reader.read();
|
|
|
|
} catch (Exception e) {
|
|
if (getRootCause(e) instanceof SAXParseException)
|
|
logger.error("Failed to load sessions: " + getRootCause(e).getMessage());
|
|
else
|
|
logger.error("Failed to load sessions!", e);
|
|
if (!this.persistSessionsPath.delete())
|
|
logger.error("Failed to delete session file at " + this.persistSessionsPath.getAbsolutePath());
|
|
return;
|
|
}
|
|
|
|
if (certificateStubs.isEmpty()) {
|
|
logger.info("No persisted sessions exist to be loaded.");
|
|
return;
|
|
}
|
|
|
|
for (CertificateStub stub : certificateStubs) {
|
|
Usage usage = stub.getUsage();
|
|
String username = stub.getUsername();
|
|
String sessionId = stub.getSessionId();
|
|
String authToken = stub.getAuthToken();
|
|
String source = stub.getSource();
|
|
User user = this.persistenceHandler.getUser(username);
|
|
if (user == null) {
|
|
logger.error("Ignoring session data for missing user " + username);
|
|
continue;
|
|
}
|
|
|
|
if (user.getUserState() == UserState.DISABLED || user.getUserState() == UserState.EXPIRED) {
|
|
logger.error("Ignoring session data for disabled/expired user " + username);
|
|
continue;
|
|
}
|
|
|
|
Set<String> userRoles = user.getRoles();
|
|
if (userRoles.isEmpty()) {
|
|
logger.error("Ignoring session data for user " + username + " which has not roles defined!");
|
|
continue;
|
|
}
|
|
|
|
// create a new certificate, with details of the user
|
|
Certificate certificate = buildCertificate(usage, user, authToken, sessionId, source, stub.getLoginTime(),
|
|
stub.isKeepAlive());
|
|
certificate.setLocale(stub.getLocale());
|
|
certificate.setLastAccess(stub.getLastAccess());
|
|
|
|
PrivilegeContext privilegeContext = buildPrivilegeContext(certificate, user);
|
|
this.privilegeContextMap.put(sessionId, privilegeContext);
|
|
}
|
|
|
|
logger.info("Loaded " + this.privilegeContextMap.size() + " sessions.");
|
|
}
|
|
|
|
/**
|
|
* Checks the credentials and validates that the user may log in.
|
|
*
|
|
* @param username
|
|
* the username of the {@link User} to check against
|
|
* @param password
|
|
* the password of this user
|
|
*
|
|
* @return the {@link User} if the credentials are valid and the user may login
|
|
*
|
|
* @throws AccessDeniedException
|
|
* if anything is wrong with the credentials or the user state
|
|
* @throws InvalidCredentialsException
|
|
* if the given credentials are invalid, the user does not exist, or has no password set
|
|
*/
|
|
protected synchronized User checkCredentialsAndUserState(String username, char[] password)
|
|
throws InvalidCredentialsException, AccessDeniedException {
|
|
|
|
// and validate the password
|
|
if (password == null || password.length < 3)
|
|
throw new InvalidCredentialsException("Password is invalid!");
|
|
|
|
// get user object
|
|
User user = this.persistenceHandler.getUser(username);
|
|
// no user means no authentication
|
|
if (user == null) {
|
|
String msg = format("There is no user defined with the username {0}", username); //$NON-NLS-1$
|
|
throw new InvalidCredentialsException(msg);
|
|
}
|
|
|
|
// make sure not a system user - they may not login in
|
|
if (user.getUserState() == UserState.SYSTEM) {
|
|
String msg = "User {0} is a system user and may not login!"; //$NON-NLS-1$
|
|
msg = format(msg, username);
|
|
throw new InvalidCredentialsException(msg);
|
|
}
|
|
|
|
// validate if user is allowed to login
|
|
// this also capture the trying to login of SYSTEM user
|
|
if (user.getUserState() != UserState.ENABLED) {
|
|
String msg = "User {0} does not have state {1} and can not login!"; //$NON-NLS-1$
|
|
msg = format(msg, username, UserState.ENABLED);
|
|
throw new AccessDeniedException(msg);
|
|
}
|
|
|
|
byte[] pwHash = user.getPassword();
|
|
if (pwHash == null)
|
|
throw new InvalidCredentialsException(
|
|
format("User {0} has no password and may not login!", username)); //$NON-NLS-1$
|
|
byte[] salt = user.getSalt();
|
|
|
|
// we only work with hashed passwords
|
|
byte[] passwordHash;
|
|
if (salt == null) {
|
|
passwordHash = this.encryptionHandler.hashPasswordWithoutSalt(password);
|
|
} else if (user.getHashAlgorithm() == null || user.getHashIterations() == -1 || user.getHashKeyLength() == -1) {
|
|
passwordHash = this.encryptionHandler.hashPassword(password, salt);
|
|
} else {
|
|
passwordHash = this.encryptionHandler.hashPassword(password, salt, user.getHashAlgorithm(),
|
|
user.getHashIterations(), user.getHashKeyLength());
|
|
}
|
|
|
|
// validate password
|
|
if (!Arrays.equals(passwordHash, pwHash))
|
|
throw new InvalidCredentialsException(format("Password is incorrect for {0}", username)); //$NON-NLS-1$
|
|
|
|
// see if we need to update the hash
|
|
if (user.getHashAlgorithm() == null || user.getHashIterations() != this.encryptionHandler.getIterations()
|
|
|| user.getHashKeyLength() != this.encryptionHandler.getKeyLength()) {
|
|
|
|
logger.warn("Updating user " + username + " due to change in hashing algorithm properties ");
|
|
|
|
// get new salt for user
|
|
salt = this.encryptionHandler.nextSalt();
|
|
|
|
// hash password
|
|
passwordHash = this.encryptionHandler.hashPassword(password, salt);
|
|
|
|
// create new user
|
|
User newUser = new User(user.getUserId(), user.getUsername(), passwordHash, salt,
|
|
this.encryptionHandler.getAlgorithm(), this.encryptionHandler.getIterations(),
|
|
this.encryptionHandler.getKeyLength(), user.getFirstname(), user.getLastname(), user.getUserState(),
|
|
user.getRoles(), user.getLocale(), user.getProperties(), user.isPasswordChangeRequested(),
|
|
user.getHistory().getClone());
|
|
|
|
// delegate user replacement to persistence handler
|
|
this.persistenceHandler.replaceUser(newUser);
|
|
|
|
// perform automatic persisting, if enabled
|
|
if (this.autoPersistOnUserChangesData) {
|
|
this.persistenceHandler.persist();
|
|
}
|
|
|
|
logger.info("Updated password for " + newUser.getUsername());
|
|
}
|
|
|
|
return user;
|
|
}
|
|
|
|
/**
|
|
* Builds a {@link PrivilegeContext} for the given {@link User} and its {@link Certificate}
|
|
*
|
|
* @param certificate
|
|
* the certificate for which to build the {@link PrivilegeContext}
|
|
* @param user
|
|
* the user for which to build the {@link PrivilegeContext}
|
|
*
|
|
* @return the {@link PrivilegeContext}
|
|
*/
|
|
private PrivilegeContext buildPrivilegeContext(Certificate certificate, User user) {
|
|
|
|
Set<String> userRoles = user.getRoles();
|
|
Map<String, IPrivilege> privileges = new HashMap<>();
|
|
Map<String, PrivilegePolicy> policies = new HashMap<>();
|
|
|
|
// get a cache of the privileges and policies for this user
|
|
for (String roleName : userRoles) {
|
|
Role role = this.persistenceHandler.getRole(roleName);
|
|
if (role == null) {
|
|
logger.error("Role " + roleName + " does not exist for user " + user.getUsername());
|
|
continue;
|
|
}
|
|
|
|
Set<String> privilegeNames = role.getPrivilegeNames();
|
|
for (String privilegeName : privilegeNames) {
|
|
|
|
IPrivilege privilege = role.getPrivilege(privilegeName);
|
|
if (privilege == null) {
|
|
logger.error(format("The Privilege {0} does not exist for role {1}", privilegeName, roleName));
|
|
continue;
|
|
}
|
|
|
|
// cache the privilege
|
|
if (privileges.containsKey(privilegeName)) {
|
|
if (this.privilegeConflictResolution.isStrict()) {
|
|
throw new PrivilegeModelException(
|
|
format("User has conflicts for privilege {0} with role {1}", privilegeName, roleName));
|
|
}
|
|
|
|
IPrivilege priv = privileges.get(privilegeName);
|
|
boolean allAllowed = priv.isAllAllowed() || privilege.isAllAllowed();
|
|
Set<String> allowList;
|
|
Set<String> denyList;
|
|
if (allAllowed) {
|
|
allowList = Collections.emptySet();
|
|
denyList = Collections.emptySet();
|
|
} else {
|
|
allowList = new HashSet<>(priv.getAllowList());
|
|
allowList.addAll(privilege.getAllowList());
|
|
denyList = new HashSet<>(priv.getDenyList());
|
|
denyList.addAll(privilege.getDenyList());
|
|
}
|
|
priv = new PrivilegeImpl(priv.getName(), priv.getPolicy(), allAllowed, denyList, allowList);
|
|
|
|
privileges.put(privilegeName, priv);
|
|
continue;
|
|
}
|
|
|
|
privileges.put(privilegeName, privilege);
|
|
|
|
// cache the policy for the privilege
|
|
String policyName = privilege.getPolicy();
|
|
if (policies.containsKey(policyName))
|
|
continue;
|
|
|
|
PrivilegePolicy policy = getPolicy(policyName);
|
|
if (policy == null) {
|
|
logger.error(format("The Policy {0} does not exist for Privilege {1}", policyName, privilegeName));
|
|
continue;
|
|
}
|
|
|
|
policies.put(policyName, policy);
|
|
}
|
|
}
|
|
|
|
UserRep userRep = user.asUserRep();
|
|
return new PrivilegeContext(userRep, certificate, privileges, policies);
|
|
}
|
|
|
|
@Override
|
|
public boolean invalidate(Certificate certificate) {
|
|
|
|
// remove registration
|
|
PrivilegeContext privilegeContext = this.privilegeContextMap.remove(certificate.getSessionId());
|
|
|
|
// persist sessions
|
|
if (privilegeContext != null)
|
|
persistSessions();
|
|
|
|
// return true if object was really removed
|
|
boolean loggedOut = privilegeContext != null;
|
|
if (loggedOut)
|
|
logger.info(format("User {0} logged out.", certificate.getUsername())); //$NON-NLS-1$
|
|
else
|
|
logger.warn("User already logged out!"); //$NON-NLS-1$
|
|
|
|
return loggedOut;
|
|
}
|
|
|
|
@Override
|
|
public PrivilegeContext validate(Certificate certificate) throws PrivilegeException {
|
|
return validate(certificate, "unknown");
|
|
}
|
|
|
|
@Override
|
|
public void validateSystemSession(PrivilegeContext ctx) throws PrivilegeException {
|
|
// ctx must not be null
|
|
if (ctx == null)
|
|
throw new PrivilegeException("PrivilegeContext may not be null!"); //$NON-NLS-1$
|
|
|
|
// validate user state is system
|
|
if (ctx.getUserRep().getUserState() != UserState.SYSTEM) {
|
|
String msg = "The PrivilegeContext's user {0} does not have expected user state {1}"; //$NON-NLS-1$
|
|
msg = format(msg, ctx.getUserRep().getUsername(), UserState.SYSTEM);
|
|
throw new PrivilegeException(msg);
|
|
}
|
|
|
|
// see if a session exists for this certificate
|
|
Certificate certificate = ctx.getCertificate();
|
|
PrivilegeContext privilegeContext = this.privilegeContextMap.get(certificate.getSessionId());
|
|
if (privilegeContext == null) {
|
|
String msg = format("There is no session information for {0}", certificate); //$NON-NLS-1$
|
|
throw new NotAuthenticatedException(msg);
|
|
}
|
|
|
|
// validate same privilege contexts
|
|
if (ctx != privilegeContext) {
|
|
String msg = format("The given PrivilegeContext {0} is not the same as registered under the sessionId {1}",
|
|
ctx.getCertificate().getSessionId(), privilegeContext.getCertificate().getSessionId());
|
|
throw new PrivilegeException(msg);
|
|
}
|
|
|
|
// validate certificate has not been tampered with
|
|
Certificate sessionCertificate = privilegeContext.getCertificate();
|
|
if (!sessionCertificate.equals(certificate)) {
|
|
String msg = "Received illegal certificate for session id {0}"; //$NON-NLS-1$
|
|
msg = format(msg, certificate.getSessionId());
|
|
throw new PrivilegeException(msg);
|
|
}
|
|
|
|
certificate.setLastAccess(ZonedDateTime.now());
|
|
|
|
if (!certificate.getSource().equals(this.identifier))
|
|
throw new IllegalStateException(
|
|
"Source has changed for certificate " + certificate + " to " + certificate.getSource());
|
|
}
|
|
|
|
@Override
|
|
public PrivilegeContext validate(Certificate certificate, String source) throws PrivilegeException {
|
|
DBC.PRE.assertNotEmpty("source must not be empty!", source);
|
|
|
|
// certificate must not be null
|
|
if (certificate == null)
|
|
throw new PrivilegeException("Certificate may not be null!"); //$NON-NLS-1$
|
|
|
|
// first see if a session exists for this certificate
|
|
PrivilegeContext privilegeContext = this.privilegeContextMap.get(certificate.getSessionId());
|
|
if (privilegeContext == null) {
|
|
String msg = format("There is no session information for {0}", certificate); //$NON-NLS-1$
|
|
throw new NotAuthenticatedException(msg);
|
|
}
|
|
|
|
// validate certificate has not been tampered with
|
|
Certificate sessionCertificate = privilegeContext.getCertificate();
|
|
if (!sessionCertificate.equals(certificate)) {
|
|
String msg = "Received illegal certificate for session id {0}"; //$NON-NLS-1$
|
|
msg = format(msg, certificate.getSessionId());
|
|
throw new PrivilegeException(msg);
|
|
}
|
|
|
|
// validate that challenge certificate is not expired (1 hour only)
|
|
if (sessionCertificate.getUsage() != Usage.ANY) {
|
|
ZonedDateTime dateTime = sessionCertificate.getLoginTime();
|
|
if (dateTime.plusHours(1).isBefore(ZonedDateTime.now())) {
|
|
invalidate(sessionCertificate);
|
|
throw new NotAuthenticatedException("Certificate has already expired!"); //$NON-NLS-1$
|
|
}
|
|
}
|
|
|
|
certificate.setLastAccess(ZonedDateTime.now());
|
|
|
|
// TODO decide if we want to assert source did not change!
|
|
// if (!source.equals(SOURCE_UNKNOWN) && !certificate.getSource().equals(source)) {
|
|
// logger.warn("Source has changed for certificate " + certificate.toString() + " to " + source);
|
|
// }
|
|
|
|
return privilegeContext;
|
|
}
|
|
|
|
@Override
|
|
public void validatePassword(Locale locale, char[] password) throws PasswordStrengthException {
|
|
if (!this.passwordStrengthHandler.validateStrength(password))
|
|
throw new PasswordStrengthException(this.passwordStrengthHandler.getDescription(locale));
|
|
}
|
|
|
|
@Override
|
|
public boolean persist(Certificate certificate) {
|
|
|
|
// validate who is doing this
|
|
PrivilegeContext prvCtx = validate(certificate);
|
|
prvCtx.validateAction(new SimpleRestrictable(PRIVILEGE_ACTION, PRIVILEGE_ACTION_PERSIST));
|
|
|
|
return this.persistenceHandler.persist();
|
|
}
|
|
|
|
@Override
|
|
public boolean persistSessions(Certificate certificate, String source) {
|
|
|
|
// validate who is doing this
|
|
PrivilegeContext prvCtx = validate(certificate);
|
|
prvCtx.validateAction(new SimpleRestrictable(PRIVILEGE_ACTION, PRIVILEGE_ACTION_PERSIST_SESSIONS));
|
|
|
|
return persistSessions();
|
|
}
|
|
|
|
@Override
|
|
public boolean reload(Certificate certificate, String source) {
|
|
|
|
// validate who is doing this
|
|
PrivilegeContext prvCtx = validate(certificate, source);
|
|
prvCtx.validateAction(new SimpleRestrictable(PRIVILEGE_ACTION, PRIVILEGE_ACTION_RELOAD));
|
|
|
|
return this.persistenceHandler.reload();
|
|
}
|
|
|
|
/**
|
|
* Initializes the concrete {@link PrivilegeHandler}. The passed parameter map contains any configuration this
|
|
* {@link PrivilegeHandler} might need. This method may only be called once and this must be enforced by the
|
|
* concrete implementation
|
|
*
|
|
* @param parameterMap
|
|
* a map containing configuration properties
|
|
* @param encryptionHandler
|
|
* the {@link EncryptionHandler} instance for this {@link PrivilegeHandler}
|
|
* @param passwordStrengthHandler
|
|
* the {@link PasswordStrengthHandler} instance for this {@link PrivilegeHandler}
|
|
* @param persistenceHandler
|
|
* the {@link PersistenceHandler} instance for this {@link PrivilegeHandler}
|
|
* @param userChallengeHandler
|
|
* the handler to challenge a user's actions e.g. password change or authentication
|
|
* @param ssoHandler
|
|
* the {@link SingleSignOnHandler}
|
|
* @param policyMap
|
|
* map of {@link PrivilegePolicy} classes
|
|
*
|
|
* @throws PrivilegeException
|
|
* if the this method is called multiple times or an initialization exception occurs
|
|
*/
|
|
public synchronized void initialize(Map<String, String> parameterMap, EncryptionHandler encryptionHandler,
|
|
PasswordStrengthHandler passwordStrengthHandler, PersistenceHandler persistenceHandler,
|
|
UserChallengeHandler userChallengeHandler, SingleSignOnHandler ssoHandler,
|
|
Map<String, Class<PrivilegePolicy>> policyMap) {
|
|
|
|
if (this.initialized)
|
|
throw new PrivilegeModelException("Already initialized!"); //$NON-NLS-1$
|
|
|
|
this.policyMap = policyMap;
|
|
this.encryptionHandler = encryptionHandler;
|
|
this.passwordStrengthHandler = passwordStrengthHandler;
|
|
this.persistenceHandler = persistenceHandler;
|
|
this.userChallengeHandler = userChallengeHandler;
|
|
this.ssoHandler = ssoHandler;
|
|
|
|
handleAutoPersistOnUserDataChange(parameterMap);
|
|
handlePersistSessionsParam(parameterMap);
|
|
handleConflictResolutionParam(parameterMap);
|
|
handleSecretParams(parameterMap);
|
|
|
|
this.allowSessionRefresh = Boolean.parseBoolean(parameterMap.get(PARAM_ALLOW_SESSION_REFRESH));
|
|
|
|
// validate policies on privileges of Roles
|
|
for (Role role : persistenceHandler.getAllRoles()) {
|
|
validatePolicies(role);
|
|
}
|
|
|
|
// validate privilege conflicts
|
|
validatePrivilegeConflicts();
|
|
|
|
this.privilegeContextMap = Collections.synchronizedMap(new HashMap<>());
|
|
|
|
loadSessions();
|
|
|
|
this.parameterMap = parameterMap;
|
|
this.initialized = true;
|
|
}
|
|
|
|
private void handleAutoPersistOnUserDataChange(Map<String, String> parameterMap) {
|
|
String autoPersistS = parameterMap.get(PARAM_AUTO_PERSIST_ON_USER_CHANGES_DATA);
|
|
if (isEmpty(autoPersistS) || autoPersistS.equals(Boolean.FALSE.toString())) {
|
|
this.autoPersistOnUserChangesData = false;
|
|
} else if (autoPersistS.equals(Boolean.TRUE.toString())) {
|
|
this.autoPersistOnUserChangesData = true;
|
|
logger.info("Enabling automatic persistence when user changes their data."); //$NON-NLS-1$
|
|
} else {
|
|
String msg = "Parameter {0} has illegal value {1}. Overriding with {2}"; //$NON-NLS-1$
|
|
msg = format(msg, PARAM_AUTO_PERSIST_ON_USER_CHANGES_DATA, autoPersistS, Boolean.FALSE);
|
|
logger.error(msg);
|
|
this.autoPersistOnUserChangesData = false;
|
|
}
|
|
}
|
|
|
|
private void handlePersistSessionsParam(Map<String, String> parameterMap) {
|
|
String persistSessionsS = parameterMap.get(PARAM_PERSIST_SESSIONS);
|
|
if (isEmpty(persistSessionsS) || persistSessionsS.equals(Boolean.FALSE.toString())) {
|
|
this.persistSessions = false;
|
|
} else if (persistSessionsS.equals(Boolean.TRUE.toString())) {
|
|
this.persistSessions = true;
|
|
|
|
String persistSessionsPathS = parameterMap.get(PARAM_PERSIST_SESSIONS_PATH);
|
|
if (isEmpty(persistSessionsPathS)) {
|
|
String msg = "Parameter {0} has illegal value {1}."; //$NON-NLS-1$
|
|
msg = format(msg, PARAM_PERSIST_SESSIONS_PATH, persistSessionsPathS);
|
|
throw new PrivilegeModelException(msg);
|
|
}
|
|
|
|
File persistSessionsPath = new File(persistSessionsPathS);
|
|
if (!persistSessionsPath.getParentFile().isDirectory()) {
|
|
String msg = "Path for param {0} is invalid as parent does not exist or is not a directory. Value: {1}"; //$NON-NLS-1$
|
|
msg = format(msg, PARAM_PERSIST_SESSIONS_PATH, persistSessionsPath.getAbsolutePath());
|
|
throw new PrivilegeModelException(msg);
|
|
}
|
|
|
|
if (persistSessionsPath.exists() && (!persistSessionsPath.isFile() || !persistSessionsPath.canWrite())) {
|
|
String msg = "Path for param {0} is invalid as file exists but is not a file or not writeable. Value: {1}"; //$NON-NLS-1$
|
|
msg = format(msg, PARAM_PERSIST_SESSIONS_PATH, persistSessionsPath.getAbsolutePath());
|
|
throw new PrivilegeModelException(msg);
|
|
}
|
|
|
|
this.persistSessionsPath = persistSessionsPath;
|
|
logger.info(format("Enabling persistence of sessions to {0}", //$NON-NLS-1$
|
|
this.persistSessionsPath.getAbsolutePath()));
|
|
} else {
|
|
String msg = "Parameter {0} has illegal value {1}. Overriding with {2}"; //$NON-NLS-1$
|
|
msg = format(msg, PARAM_PERSIST_SESSIONS, persistSessionsS, Boolean.FALSE);
|
|
logger.error(msg);
|
|
this.persistSessions = false;
|
|
}
|
|
}
|
|
|
|
private void handleConflictResolutionParam(Map<String, String> parameterMap) {
|
|
String privilegeConflictResolutionS = parameterMap.get(PARAM_PRIVILEGE_CONFLICT_RESOLUTION);
|
|
if (privilegeConflictResolutionS == null) {
|
|
this.privilegeConflictResolution = PrivilegeConflictResolution.STRICT;
|
|
String msg = "No {0} parameter defined. Using {1}";
|
|
msg = format(msg, PARAM_PRIVILEGE_CONFLICT_RESOLUTION, this.privilegeConflictResolution);
|
|
logger.info(msg);
|
|
} else {
|
|
try {
|
|
this.privilegeConflictResolution = PrivilegeConflictResolution.valueOf(privilegeConflictResolutionS);
|
|
} catch (Exception e) {
|
|
String msg = "Parameter {0} has illegal value {1}."; //$NON-NLS-1$
|
|
msg = format(msg, PARAM_PRIVILEGE_CONFLICT_RESOLUTION, privilegeConflictResolutionS);
|
|
throw new PrivilegeModelException(msg);
|
|
}
|
|
}
|
|
logger.info("Privilege conflict resolution set to " + this.privilegeConflictResolution); //$NON-NLS-1$
|
|
}
|
|
|
|
private void handleSecretParams(Map<String, String> parameterMap) {
|
|
|
|
String secretKeyS = parameterMap.get(PARAM_SECRET_KEY);
|
|
if (isEmpty(secretKeyS)) {
|
|
String msg = "Parameter {0} may not be empty"; //$NON-NLS-1$
|
|
msg = format(msg, PARAM_SECRET_KEY, PARAM_PRIVILEGE_CONFLICT_RESOLUTION);
|
|
throw new PrivilegeModelException(msg);
|
|
}
|
|
|
|
String secretSaltS = parameterMap.get(PARAM_SECRET_SALT);
|
|
if (isEmpty(secretSaltS)) {
|
|
String msg = "Parameter {0} may not be empty"; //$NON-NLS-1$
|
|
msg = format(msg, PARAM_SECRET_SALT, PARAM_PRIVILEGE_CONFLICT_RESOLUTION);
|
|
throw new PrivilegeModelException(msg);
|
|
}
|
|
|
|
this.secretKey = AesCryptoHelper.buildSecret(secretKeyS.toCharArray(), secretSaltS.getBytes());
|
|
|
|
// build our identifier
|
|
byte[] encrypt = AesCryptoHelper.encrypt(this.secretKey, "PrivilegeHandler".getBytes());
|
|
this.identifier = Base64.getEncoder().encodeToString(encrypt);
|
|
|
|
// remove secrets
|
|
parameterMap.remove(PARAM_SECRET_KEY);
|
|
parameterMap.remove(PARAM_SECRET_SALT);
|
|
}
|
|
|
|
private void validatePrivilegeConflicts() {
|
|
if (!this.privilegeConflictResolution.isStrict()) {
|
|
return;
|
|
}
|
|
|
|
List<String> conflicts = new ArrayList<>();
|
|
List<User> users = this.persistenceHandler.getAllUsers();
|
|
for (User user : users) {
|
|
Map<String, String> privilegeNames = new HashMap<>();
|
|
conflicts.addAll(detectPrivilegeConflicts(privilegeNames, user));
|
|
}
|
|
|
|
if (!conflicts.isEmpty()) {
|
|
for (String conflict : conflicts) {
|
|
logger.error(conflict);
|
|
}
|
|
throw new PrivilegeModelException("There are " + conflicts.size() + " privilege conflicts!");
|
|
}
|
|
}
|
|
|
|
private void assertNoPrivilegeConflict(User user) {
|
|
if (this.privilegeConflictResolution.isStrict()) {
|
|
Map<String, String> privilegeNames = new HashMap<>();
|
|
List<String> conflicts = detectPrivilegeConflicts(privilegeNames, user);
|
|
if (!conflicts.isEmpty()) {
|
|
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);
|
|
}
|
|
}
|
|
|
|
private 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;
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
*/
|
|
private void validatePolicies(Role role) {
|
|
for (String privilegeName : role.getPrivilegeNames()) {
|
|
IPrivilege 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}"; //$NON-NLS-1$
|
|
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
|
|
*/
|
|
private void clearPassword(char[] password) {
|
|
if (password != null)
|
|
Arrays.fill(password, (char) 0);
|
|
}
|
|
|
|
@Override
|
|
public void runAs(String username, SystemAction action) throws Exception {
|
|
|
|
PrivilegeContext systemUserPrivilegeContext = initiateSystemPrivilege(username, action);
|
|
|
|
String sessionId = systemUserPrivilegeContext.getCertificate().getSessionId();
|
|
try {
|
|
// perform the action
|
|
action.execute(systemUserPrivilegeContext);
|
|
} finally {
|
|
this.privilegeContextMap.remove(sessionId);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public <T> T runWithResult(String username, SystemActionWithResult<T> action) throws Exception {
|
|
|
|
PrivilegeContext systemUserPrivilegeContext = initiateSystemPrivilege(username, action);
|
|
|
|
String sessionId = systemUserPrivilegeContext.getCertificate().getSessionId();
|
|
try {
|
|
// perform the action
|
|
return action.execute(systemUserPrivilegeContext);
|
|
} finally {
|
|
this.privilegeContextMap.remove(sessionId);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public PrivilegeContext openSystemUserContext(String username) throws PrivilegeException {
|
|
|
|
// get privilegeContext for this system user
|
|
PrivilegeContext systemUserPrivilegeContext = getSystemUserPrivilegeContext(username);
|
|
|
|
String sessionId = systemUserPrivilegeContext.getCertificate().getSessionId();
|
|
this.privilegeContextMap.put(sessionId, systemUserPrivilegeContext);
|
|
|
|
return systemUserPrivilegeContext;
|
|
}
|
|
|
|
private PrivilegeContext initiateSystemPrivilege(String username, Restrictable restrictable) {
|
|
if (username == null)
|
|
throw new PrivilegeException("systemUsername may not be null!"); //$NON-NLS-1$
|
|
if (restrictable == null)
|
|
throw new PrivilegeException("action may not be null!"); //$NON-NLS-1$
|
|
|
|
// get privilegeContext for this system user
|
|
PrivilegeContext systemUserPrivilegeContext = getSystemUserPrivilegeContext(username);
|
|
|
|
// validate this system user may perform the given action
|
|
systemUserPrivilegeContext.validateAction(restrictable);
|
|
|
|
String sessionId = systemUserPrivilegeContext.getCertificate().getSessionId();
|
|
this.privilegeContextMap.put(sessionId, systemUserPrivilegeContext);
|
|
|
|
return systemUserPrivilegeContext;
|
|
}
|
|
|
|
/**
|
|
* Returns the {@link Certificate} for the given system username. If it does not yet exist, then it is created by
|
|
* authenticating the system user
|
|
*
|
|
* @param systemUsername
|
|
* the name of the system user
|
|
*
|
|
* @return the {@link Certificate} for this system user
|
|
*/
|
|
private PrivilegeContext getSystemUserPrivilegeContext(String systemUsername) {
|
|
|
|
// get user object
|
|
User user = this.persistenceHandler.getUser(systemUsername);
|
|
|
|
// no user means no authentication
|
|
if (user == null) {
|
|
String msg = format("The system user with username {0} does not exist!", systemUsername); //$NON-NLS-1$
|
|
throw new AccessDeniedException(msg);
|
|
}
|
|
|
|
// validate password
|
|
byte[] pwHash = user.getPassword();
|
|
if (pwHash != null) {
|
|
String msg = format("System users must not have a password: {0}", user.getUsername()); //$NON-NLS-1$
|
|
throw new AccessDeniedException(msg);
|
|
}
|
|
|
|
// validate user state is system
|
|
if (user.getUserState() != UserState.SYSTEM) {
|
|
String msg = "The system {0} user does not have expected user state {1}"; //$NON-NLS-1$
|
|
msg = format(msg, user.getUsername(), UserState.SYSTEM);
|
|
throw new PrivilegeException(msg);
|
|
}
|
|
|
|
// validate user has at least one role
|
|
if (user.getRoles().isEmpty()) {
|
|
String msg = format("The system user {0} does not have any roles defined!",
|
|
user.getUsername()); //$NON-NLS-1$
|
|
throw new PrivilegeException(msg);
|
|
}
|
|
|
|
// get 2 auth tokens
|
|
String authToken = this.encryptionHandler.nextToken();
|
|
|
|
// get next session id
|
|
String sessionId = UUID.randomUUID().toString();
|
|
|
|
// create a new certificate, with details of the user
|
|
Certificate systemUserCertificate = buildCertificate(Usage.ANY, user, authToken, sessionId, this.identifier,
|
|
ZonedDateTime.now(), false);
|
|
|
|
// create and save a new privilege context
|
|
PrivilegeContext privilegeContext = buildPrivilegeContext(systemUserCertificate, user);
|
|
|
|
// log
|
|
if (logger.isDebugEnabled()) {
|
|
String msg = "The system user ''{0}'' is logged in with session {1}"; //$NON-NLS-1$
|
|
msg = format(msg, user.getUsername(), systemUserCertificate.getSessionId());
|
|
logger.info(msg);
|
|
}
|
|
|
|
return privilegeContext;
|
|
}
|
|
|
|
/**
|
|
* <p>
|
|
* This method instantiates a {@link PrivilegePolicy} object from the given policyName. The {@link PrivilegePolicy}
|
|
* is not stored in a database. The privilege name is a class name and is then used to instantiate a new
|
|
* {@link PrivilegePolicy} object
|
|
* </p>
|
|
*
|
|
* @param policyName
|
|
* the class name of the {@link PrivilegePolicy} object to return
|
|
*
|
|
* @return the {@link PrivilegePolicy} object
|
|
*
|
|
* @throws PrivilegeException
|
|
* if the {@link PrivilegePolicy} object for the given policy name could not be instantiated
|
|
*/
|
|
private PrivilegePolicy getPolicy(String policyName) {
|
|
|
|
// get the policies class
|
|
Class<PrivilegePolicy> policyClazz = this.policyMap.get(policyName);
|
|
if (policyClazz == null) {
|
|
return null;
|
|
}
|
|
|
|
// instantiate the policy
|
|
PrivilegePolicy policy;
|
|
try {
|
|
|
|
policy = policyClazz.getConstructor().newInstance();
|
|
} catch (Exception e) {
|
|
String msg = "The class for the policy with the name {0} does not exist!{1}"; //$NON-NLS-1$
|
|
msg = format(msg, policyName, policyName);
|
|
throw new PrivilegeModelException(msg, e);
|
|
}
|
|
|
|
return policy;
|
|
}
|
|
}
|