[Major] Refactored PrivilegeHandler, adding locking, no synchronized blocks and methods

This commit is contained in:
Robert von Burg 2023-04-05 15:18:13 +02:00
parent 10f201ad9d
commit 6d05ff4803
Signed by: eitch
GPG Key ID: 75DB9C85C74331F7
15 changed files with 595 additions and 385 deletions

View File

@ -36,7 +36,7 @@ import li.strolch.model.audit.Audit;
import li.strolch.persistence.api.StrolchTransaction;
import li.strolch.privilege.base.PrivilegeException;
import li.strolch.privilege.handler.*;
import li.strolch.privilege.helper.PrivilegeInitializationHelper;
import li.strolch.privilege.helper.PrivilegeInitializer;
import li.strolch.privilege.model.Certificate;
import li.strolch.privilege.model.PrivilegeContext;
import li.strolch.privilege.model.Usage;
@ -65,8 +65,8 @@ public class DefaultStrolchPrivilegeHandler extends StrolchComponent implements
// initialize privilege
RuntimeConfiguration runtimeConfiguration = configuration.getRuntimeConfiguration();
File privilegeConfigFile = configuration
.getConfigFile(PROP_PRIVILEGE_CONFIG_FILE, PRIVILEGE_CONFIG_XML, runtimeConfiguration);
File privilegeConfigFile = configuration.getConfigFile(PROP_PRIVILEGE_CONFIG_FILE, PRIVILEGE_CONFIG_XML,
runtimeConfiguration);
this.privilegeHandler = initializeFromXml(configuration, privilegeConfigFile);
}
@ -81,8 +81,8 @@ public class DefaultStrolchPrivilegeHandler extends StrolchComponent implements
ComponentConfiguration configuration = getConfiguration();
RuntimeConfiguration runtimeConfiguration = configuration.getRuntimeConfiguration();
File privilegeConfigFile = configuration
.getConfigFile(PROP_PRIVILEGE_CONFIG_FILE, PRIVILEGE_CONFIG_XML, runtimeConfiguration);
File privilegeConfigFile = configuration.getConfigFile(PROP_PRIVILEGE_CONFIG_FILE, PRIVILEGE_CONFIG_XML,
runtimeConfiguration);
this.privilegeHandler = initializeFromXml(configuration, privilegeConfigFile);
}
@ -92,8 +92,8 @@ public class DefaultStrolchPrivilegeHandler extends StrolchComponent implements
* @param privilegeXmlFile
* a {@link File} reference to the XML file containing the configuration for Privilege
*
* @return the initialized {@link PrivilegeHandler} where the {@link EncryptionHandler} and {@link
* PersistenceHandler} are set and initialized as well
* @return the initialized {@link PrivilegeHandler} where the {@link EncryptionHandler} and
* {@link PersistenceHandler} are set and initialized as well
*/
private li.strolch.privilege.handler.PrivilegeHandler initializeFromXml(ComponentConfiguration configuration,
File privilegeXmlFile) {
@ -129,7 +129,7 @@ public class DefaultStrolchPrivilegeHandler extends StrolchComponent implements
xmlParams.put(XML_PARAM_BASE_PATH, configPath.getPath());
}
return PrivilegeInitializationHelper.initializeFromXml(containerModel);
return new PrivilegeInitializer(getScheduledExecutor(getName())).initializeFromXml(containerModel);
} catch (Exception e) {
String msg = "Failed to load Privilege configuration from {0}";
@ -260,8 +260,8 @@ public class DefaultStrolchPrivilegeHandler extends StrolchComponent implements
@Override
public <T> T runAsAgentWithResult(PrivilegedRunnableWithResult<T> runnable) throws Exception {
return this.privilegeHandler
.runWithResult(StrolchConstants.SYSTEM_USER_AGENT, new StrolchSystemActionWithResult<>(runnable));
return this.privilegeHandler.runWithResult(StrolchConstants.SYSTEM_USER_AGENT,
new StrolchSystemActionWithResult<>(runnable));
}
@Override

View File

@ -8,6 +8,7 @@ import java.util.Hashtable;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ScheduledExecutorService;
import li.strolch.privilege.base.AccessDeniedException;
import li.strolch.privilege.model.UserState;
@ -26,12 +27,12 @@ public abstract class BaseLdapPrivilegeHandler extends DefaultPrivilegeHandler {
private String domain;
@Override
public synchronized void initialize(Map<String, String> parameterMap, EncryptionHandler encryptionHandler,
PasswordStrengthHandler passwordStrengthHandler, PersistenceHandler persistenceHandler,
UserChallengeHandler userChallengeHandler, SingleSignOnHandler ssoHandler,
Map<String, Class<PrivilegePolicy>> policyMap) {
public synchronized void initialize(ScheduledExecutorService executorService, Map<String, String> parameterMap,
EncryptionHandler encryptionHandler, PasswordStrengthHandler passwordStrengthHandler,
PersistenceHandler persistenceHandler, UserChallengeHandler userChallengeHandler,
SingleSignOnHandler ssoHandler, Map<String, Class<PrivilegePolicy>> policyMap) {
super.initialize(parameterMap, encryptionHandler, passwordStrengthHandler, persistenceHandler,
super.initialize(executorService, parameterMap, encryptionHandler, passwordStrengthHandler, persistenceHandler,
userChallengeHandler, ssoHandler, policyMap);
this.providerUrl = parameterMap.get("providerUrl");

View File

@ -27,6 +27,10 @@ import java.nio.file.Files;
import java.time.ZonedDateTime;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@ -38,6 +42,7 @@ 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.concurrent.ElementLockingHandler;
import li.strolch.utils.dbc.DBC;
import li.strolch.utils.helper.AesCryptoHelper;
import org.slf4j.Logger;
@ -139,6 +144,10 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
private Map<String, String> parameterMap;
private ElementLockingHandler<String> lockingHandler;
private ScheduledExecutorService executorService;
private Future<?> persistSessionsTask;
@Override
public SingleSignOnHandler getSsoHandler() {
return this.ssoHandler;
@ -381,6 +390,11 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
@Override
public UserRep addUser(Certificate certificate, UserRep userRepParam, char[] password) {
return this.lockingHandler.lockedExecuteWithResult(userRepParam.getUsername(),
() -> internalAddUser(certificate, userRepParam, password));
}
public UserRep internalAddUser(Certificate certificate, UserRep userRepParam, char[] password) {
try {
// validate user actually has this type of privilege
@ -449,6 +463,11 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
@Override
public void addOrUpdateUsers(Certificate certificate, List<UserRep> userReps) throws PrivilegeException {
this.lockingHandler.lockedExecute(PrivilegeHandler.class.getSimpleName(),
() -> internalAddOrUpdateUsers(certificate, userReps));
}
private void internalAddOrUpdateUsers(Certificate certificate, List<UserRep> userReps) throws PrivilegeException {
// validate user actually has this type of privilege
PrivilegeContext prvCtx = validate(certificate);
@ -525,7 +544,12 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
}
@Override
public UserRep replaceUser(Certificate certificate, UserRep userRep, char[] password) {
public UserRep replaceUser(Certificate certificate, UserRep userRep, char[] password) throws PrivilegeException {
return this.lockingHandler.lockedExecuteWithResult(userRep.getUsername(),
() -> internalReplaceUser(certificate, userRep, password));
}
private UserRep internalReplaceUser(Certificate certificate, UserRep userRep, char[] password) {
try {
// validate user actually has this type of privilege
@ -616,6 +640,11 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
@Override
public UserRep updateUser(Certificate certificate, UserRep userRep) throws PrivilegeException {
return this.lockingHandler.lockedExecuteWithResult(userRep.getUsername(),
() -> internalUpdateUser(certificate, userRep));
}
public UserRep internalUpdateUser(Certificate certificate, UserRep userRep) throws PrivilegeException {
// validate user actually has this type of privilege
PrivilegeContext prvCtx = validate(certificate);
@ -687,6 +716,10 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
@Override
public UserRep removeUser(Certificate certificate, String username) {
return this.lockingHandler.lockedExecuteWithResult(username, () -> internalRemoveUser(certificate, username));
}
private UserRep internalRemoveUser(Certificate certificate, String username) {
// validate user actually has this type of privilege
PrivilegeContext prvCtx = validate(certificate);
@ -713,6 +746,11 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
@Override
public UserRep addRoleToUser(Certificate certificate, String username, String roleName) {
return this.lockingHandler.lockedExecuteWithResult(username,
() -> internalAddRoleToUser(certificate, username, roleName));
}
private UserRep internalAddRoleToUser(Certificate certificate, String username, String roleName) {
// validate user actually has this type of privilege
PrivilegeContext prvCtx = validate(certificate);
@ -765,6 +803,11 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
@Override
public UserRep removeRoleFromUser(Certificate certificate, String username, String roleName) {
return this.lockingHandler.lockedExecuteWithResult(username,
() -> internalRemoveRoleFromUser(certificate, username, roleName));
}
private UserRep internalRemoveRoleFromUser(Certificate certificate, String username, String roleName) {
// validate user actually has this type of privilege
PrivilegeContext prvCtx = validate(certificate);
@ -808,6 +851,11 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
@Override
public UserRep setUserLocale(Certificate certificate, String username, Locale locale) {
return this.lockingHandler.lockedExecuteWithResult(username,
() -> internalSetUserLocale(certificate, username, locale));
}
private UserRep internalSetUserLocale(Certificate certificate, String username, Locale locale) {
// validate user actually has this type of privilege
PrivilegeContext prvCtx = validate(certificate);
@ -845,6 +893,10 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
@Override
public void requirePasswordChange(Certificate certificate, String username) throws PrivilegeException {
this.lockingHandler.lockedExecute(username, () -> internalRequirePasswordChange(certificate, username));
}
private void internalRequirePasswordChange(Certificate certificate, String username) throws PrivilegeException {
// validate user actually has this type of privilege
PrivilegeContext prvCtx = validate(certificate);
@ -878,6 +930,10 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
@Override
public void setUserPassword(Certificate certificate, String username, char[] password) {
this.lockingHandler.lockedExecute(username, () -> internalSetUserPassword(certificate, username, password));
}
private void internalSetUserPassword(Certificate certificate, String username, char[] password) {
try {
// validate user actually has this type of privilege
@ -944,6 +1000,11 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
@Override
public UserRep setUserState(Certificate certificate, String username, UserState state) {
return this.lockingHandler.lockedExecuteWithResult(username,
() -> internalSetUserState(certificate, username, state));
}
private UserRep internalSetUserState(Certificate certificate, String username, UserState state) {
// validate user actually has this type of privilege
PrivilegeContext prvCtx = validate(certificate);
@ -974,6 +1035,11 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
@Override
public RoleRep addRole(Certificate certificate, RoleRep roleRep) {
return this.lockingHandler.lockedExecuteWithResult(roleRep.getName(),
() -> internalAddRole(certificate, roleRep));
}
private RoleRep internalAddRole(Certificate certificate, RoleRep roleRep) {
// validate user actually has this type of privilege
PrivilegeContext prvCtx = validate(certificate);
@ -1007,6 +1073,11 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
@Override
public RoleRep replaceRole(Certificate certificate, RoleRep roleRep) {
return this.lockingHandler.lockedExecuteWithResult(roleRep.getName(),
() -> internalReplaceRole(certificate, roleRep));
}
private RoleRep internalReplaceRole(Certificate certificate, RoleRep roleRep) {
// validate user actually has this type of privilege
PrivilegeContext prvCtx = validate(certificate);
@ -1047,6 +1118,10 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
@Override
public RoleRep removeRole(Certificate certificate, String roleName) {
return this.lockingHandler.lockedExecuteWithResult(roleName, () -> internalRemoveRole(certificate, roleName));
}
private RoleRep internalRemoveRole(Certificate certificate, String roleName) {
// validate user actually has this type of privilege
PrivilegeContext prvCtx = validate(certificate);
@ -1083,6 +1158,12 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
@Override
public RoleRep addOrReplacePrivilegeOnRole(Certificate certificate, String roleName, PrivilegeRep privilegeRep) {
return this.lockingHandler.lockedExecuteWithResult(roleName,
() -> internalAddOrReplacePrivilegeOnRole(certificate, roleName, privilegeRep));
}
private RoleRep internalAddOrReplacePrivilegeOnRole(Certificate certificate, String roleName,
PrivilegeRep privilegeRep) {
// validate user actually has this type of privilege
PrivilegeContext prvCtx = validate(certificate);
@ -1142,6 +1223,11 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
@Override
public RoleRep removePrivilegeFromRole(Certificate certificate, String roleName, String privilegeName) {
return this.lockingHandler.lockedExecuteWithResult(roleName,
() -> internalRemovePrivilegeFromRole(certificate, roleName, privilegeName));
}
private RoleRep internalRemovePrivilegeFromRole(Certificate certificate, String roleName, String privilegeName) {
// validate user actually has this type of privilege
PrivilegeContext prvCtx = validate(certificate);
@ -1192,16 +1278,14 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
* 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);
}
List<PrivilegeContext> contexts = new ArrayList<>(this.privilegeContextMap.values());
for (PrivilegeContext ctx : contexts) {
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);
}
}
}
@ -1213,34 +1297,27 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
* 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;
List<PrivilegeContext> contexts = new ArrayList<>(this.privilegeContextMap.values());
for (PrivilegeContext ctx : contexts) {
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);
}
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())) {
List<PrivilegeContext> contexts = new ArrayList<>(this.privilegeContextMap.values());
for (PrivilegeContext ctx : contexts) {
if (ctx.getUserRep().getUsername().equals(user.getUsername()))
invalidate(ctx.getCertificate());
}
}
}
@ -1251,6 +1328,10 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
@Override
public void initiateChallengeFor(Usage usage, String username, String source) {
this.lockingHandler.lockedExecute(username, () -> internalInitiateChallengeFor(usage, username, source));
}
private void internalInitiateChallengeFor(Usage usage, String username, String source) {
DBC.PRE.assertNotEmpty("source must not be empty!", source);
// get User
@ -1272,6 +1353,12 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
@Override
public Certificate validateChallenge(String username, String challenge, String source) throws PrivilegeException {
return this.lockingHandler.lockedExecuteWithResult(username,
() -> internalValidateChallenge(username, challenge, source));
}
private Certificate internalValidateChallenge(String username, String challenge, String source)
throws PrivilegeException {
DBC.PRE.assertNotEmpty("source must not be empty!", source);
// get User
@ -1311,6 +1398,12 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
@Override
public Certificate authenticate(String username, char[] password, String source, Usage usage, boolean keepAlive) {
return this.lockingHandler.lockedExecuteWithResult(username,
() -> internalAuthenticate(username, password, source, usage, keepAlive));
}
private Certificate internalAuthenticate(String username, char[] password, String source, Usage usage,
boolean keepAlive) {
DBC.PRE.assertNotEmpty("source must not be empty!", source);
try {
@ -1381,13 +1474,20 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
}
@Override
public Certificate authenticateSingleSignOn(Object data, String source, boolean keepAlive)
throws PrivilegeException {
public Certificate authenticateSingleSignOn(Object data, String source, boolean keepAlive) {
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);
return this.lockingHandler.lockedExecuteWithResult(user.getUsername(),
() -> internalAuthenticateSingleSignOn(user, source, keepAlive));
}
private Certificate internalAuthenticateSingleSignOn(User user, String source, boolean keepAlive)
throws PrivilegeException {
DBC.PRE.assertEquals("SSO Users must have UserState.REMOTE!", UserState.REMOTE, user.getUserState());
user.getHistory().setLastLogin(ZonedDateTime.now());
@ -1427,6 +1527,11 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
@Override
public Certificate refresh(Certificate certificate, String source) throws AccessDeniedException {
return this.lockingHandler.lockedExecuteWithResult(certificate.getUsername(),
() -> internalRefresh(certificate, source));
}
private Certificate internalRefresh(Certificate certificate, String source) throws AccessDeniedException {
DBC.PRE.assertNotNull("certificate must not be null!", certificate);
try {
@ -1490,29 +1595,36 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
user.getLocale(), userRoles, new HashMap<>(user.getProperties()));
}
private synchronized boolean persistSessions() {
private 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());
// async execution, max. once per second
if (this.persistSessionsTask != null)
this.persistSessionsTask.cancel(true);
this.persistSessionsTask = this.executorService.schedule(
() -> this.lockingHandler.lockedExecute("persist-sessions", () -> {
try (OutputStream fout = Files.newOutputStream(this.persistSessionsPath.toPath());
OutputStream outputStream = AesCryptoHelper.wrapEncrypt(this.secretKey, fout)) {
List<Certificate> sessions = new ArrayList<>(this.privilegeContextMap.values()).stream()
.map(PrivilegeContext::getCertificate)
.filter(c -> !c.getUserState().isSystem())
.collect(Collectors.toList());
CertificateStubsDomWriter writer = new CertificateStubsDomWriter(sessions, outputStream);
writer.write();
outputStream.flush();
try (OutputStream out = Files.newOutputStream(this.persistSessionsPath.toPath());
OutputStream outputStream = AesCryptoHelper.wrapEncrypt(this.secretKey, out)) {
} 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());
}
}
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());
}
}
}), 1, TimeUnit.SECONDS);
return true;
}
@ -1605,7 +1717,7 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
* @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)
protected User checkCredentialsAndUserState(String username, char[] password)
throws InvalidCredentialsException, AccessDeniedException {
// and validate the password
@ -1905,6 +2017,16 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
return persistSessions();
}
@Override
public void start() {
this.lockingHandler.start();
}
@Override
public void stop() {
this.lockingHandler.stop();
}
@Override
public boolean reload(Certificate certificate, String source) {
@ -1936,16 +2058,18 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
* map of {@link PrivilegePolicy} classes
*
* @throws PrivilegeException
* if the this method is called multiple times or an initialization exception occurs
* if 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) {
public synchronized void initialize(ScheduledExecutorService executorService, 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!");
this.executorService = executorService;
this.lockingHandler = new ElementLockingHandler<>(executorService, TimeUnit.SECONDS, 10L);
this.policyMap = policyMap;
this.encryptionHandler = encryptionHandler;
this.passwordStrengthHandler = passwordStrengthHandler;
@ -1969,7 +2093,7 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
// validate privilege conflicts
validatePrivilegeConflicts();
this.privilegeContextMap = Collections.synchronizedMap(new HashMap<>());
this.privilegeContextMap = new ConcurrentHashMap<>();
loadSessions();

View File

@ -11,6 +11,7 @@ import javax.naming.directory.Attributes;
import java.io.File;
import java.io.FileReader;
import java.util.*;
import java.util.concurrent.ScheduledExecutorService;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
@ -29,12 +30,12 @@ public class JsonConfigLdapPrivilegeHandler extends BaseLdapPrivilegeHandler {
private HashMap<String, String> userLdapGroupOverrides;
@Override
public synchronized void initialize(Map<String, String> parameterMap, EncryptionHandler encryptionHandler,
PasswordStrengthHandler passwordStrengthHandler, PersistenceHandler persistenceHandler,
UserChallengeHandler userChallengeHandler, SingleSignOnHandler ssoHandler,
Map<String, Class<PrivilegePolicy>> policyMap) {
public synchronized void initialize(ScheduledExecutorService executorService, Map<String, String> parameterMap,
EncryptionHandler encryptionHandler, PasswordStrengthHandler passwordStrengthHandler,
PersistenceHandler persistenceHandler, UserChallengeHandler userChallengeHandler,
SingleSignOnHandler ssoHandler, Map<String, Class<PrivilegePolicy>> policyMap) {
super.initialize(parameterMap, encryptionHandler, passwordStrengthHandler, persistenceHandler,
super.initialize(executorService, parameterMap, encryptionHandler, passwordStrengthHandler, persistenceHandler,
userChallengeHandler, ssoHandler, policyMap);
this.realm = parameterMap.get(REALM);
@ -48,9 +49,8 @@ public class JsonConfigLdapPrivilegeHandler extends BaseLdapPrivilegeHandler {
DBC.PRE.assertNotEmpty("configFile param must be set!", configFileS);
File configFile = new File(configFileS);
if (!configFile.exists() || !configFile.isFile() || !configFile.canRead())
throw new IllegalStateException(
"configFile does not exist, is not a file, or can not be read at path " + configFile
.getAbsolutePath());
throw new IllegalStateException("configFile does not exist, is not a file, or can not be read at path "
+ configFile.getAbsolutePath());
// parse the configuration file
JsonObject configJ;
@ -133,7 +133,8 @@ public class JsonConfigLdapPrivilegeHandler extends BaseLdapPrivilegeHandler {
logger.info("Overriding LDAP group for user " + username + " to " + overrideGroup);
}
Set<String> relevantLdapGroups = ldapGroups.stream().filter(s -> this.ldapGroupNames.contains(s))
Set<String> relevantLdapGroups = ldapGroups.stream()
.filter(s -> this.ldapGroupNames.contains(s))
.collect(toSet());
if (relevantLdapGroups.isEmpty())
throw new IllegalStateException("User " + username

View File

@ -823,6 +823,16 @@ public interface PrivilegeHandler {
*/
void validatePassword(Locale locale, char[] password) throws PasswordStrengthException;
/**
* Starts the privilege handler, starting any threads and services which might be needed
*/
void start();
/**
* Stops the privilege handler, starting any threads and services which might be needed
*/
void stop();
/**
* <p>
* Informs this {@link PersistenceHandler} to reload the data from the backend

View File

@ -6,6 +6,7 @@ import static li.strolch.utils.helper.StringHelper.isEmpty;
import javax.naming.NamingException;
import javax.naming.directory.Attributes;
import java.util.*;
import java.util.concurrent.ScheduledExecutorService;
import li.strolch.privilege.helper.LdapHelper;
import li.strolch.privilege.policy.PrivilegePolicy;
@ -25,12 +26,12 @@ public class SimpleLdapPrivilegeHandler extends BaseLdapPrivilegeHandler {
private String realm;
@Override
public synchronized void initialize(Map<String, String> parameterMap, EncryptionHandler encryptionHandler,
PasswordStrengthHandler passwordStrengthHandler, PersistenceHandler persistenceHandler,
UserChallengeHandler userChallengeHandler, SingleSignOnHandler ssoHandler,
Map<String, Class<PrivilegePolicy>> policyMap) {
public synchronized void initialize(ScheduledExecutorService executorService, Map<String, String> parameterMap,
EncryptionHandler encryptionHandler, PasswordStrengthHandler passwordStrengthHandler,
PersistenceHandler persistenceHandler, UserChallengeHandler userChallengeHandler,
SingleSignOnHandler ssoHandler, Map<String, Class<PrivilegePolicy>> policyMap) {
super.initialize(parameterMap, encryptionHandler, passwordStrengthHandler, persistenceHandler,
super.initialize(executorService, parameterMap, encryptionHandler, passwordStrengthHandler, persistenceHandler,
userChallengeHandler, ssoHandler, policyMap);
this.organisation = parameterMap.getOrDefault(ORGANISATION, "");

View File

@ -21,7 +21,10 @@ import static li.strolch.privilege.helper.XmlConstants.*;
import static li.strolch.utils.helper.StringHelper.formatNanoDuration;
import java.io.File;
import java.util.*;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import li.strolch.privilege.base.PrivilegeException;
import li.strolch.privilege.helper.XmlConstants;
@ -46,8 +49,8 @@ public class XmlPersistenceHandler implements PersistenceHandler {
protected static final Logger logger = LoggerFactory.getLogger(XmlPersistenceHandler.class);
private Map<String, User> userMap;
private Map<String, Role> roleMap;
private final Map<String, User> userMap;
private final Map<String, Role> roleMap;
private boolean userMapDirty;
private boolean roleMapDirty;
@ -59,6 +62,11 @@ public class XmlPersistenceHandler implements PersistenceHandler {
private boolean caseInsensitiveUsername;
public XmlPersistenceHandler() {
this.roleMap = new ConcurrentHashMap<>();
this.userMap = new ConcurrentHashMap<>();
}
@Override
public Map<String, String> getParameterMap() {
return this.parameterMap;
@ -212,27 +220,23 @@ public class XmlPersistenceHandler implements PersistenceHandler {
@Override
public boolean reload() {
this.roleMap = Collections.synchronizedMap(new HashMap<>());
this.userMap = Collections.synchronizedMap(new HashMap<>());
// parse models xml file to XML document
PrivilegeUsersSaxReader usersXmlHandler = new PrivilegeUsersSaxReader();
PrivilegeUsersSaxReader usersXmlHandler = new PrivilegeUsersSaxReader(this.caseInsensitiveUsername);
XmlHelper.parseDocument(this.usersPath, usersXmlHandler);
PrivilegeRolesSaxReader rolesXmlHandler = new PrivilegeRolesSaxReader();
XmlHelper.parseDocument(this.rolesPath, rolesXmlHandler);
// ROLES
List<Role> roles = rolesXmlHandler.getRoles();
for (Role role : roles) {
this.roleMap.put(role.getName(), role);
synchronized (this.roleMap) {
this.roleMap.clear();
this.roleMap.putAll(rolesXmlHandler.getRoles());
}
// USERS
List<User> users = usersXmlHandler.getUsers();
for (User user : users) {
this.userMap
.put(this.caseInsensitiveUsername ? user.getUsername().toLowerCase() : user.getUsername(), user);
synchronized (this.userMap) {
this.userMap.clear();
this.userMap.putAll(usersXmlHandler.getUsers());
}
this.userMapDirty = false;
@ -242,7 +246,7 @@ public class XmlPersistenceHandler implements PersistenceHandler {
logger.info(format("Read {0} Roles", this.roleMap.size()));
// validate referenced roles exist
for (User user : users) {
for (User user : this.userMap.values()) {
for (String roleName : user.getRoles()) {
// validate that role exists

View File

@ -1,200 +0,0 @@
/*
* 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.helper;
import static li.strolch.utils.helper.StringHelper.isEmpty;
import java.io.File;
import java.io.InputStream;
import java.nio.file.Files;
import java.text.MessageFormat;
import java.util.Map;
import li.strolch.privilege.base.PrivilegeException;
import li.strolch.privilege.handler.*;
import li.strolch.privilege.model.internal.PrivilegeContainerModel;
import li.strolch.privilege.policy.PrivilegePolicy;
import li.strolch.privilege.xml.PrivilegeConfigSaxReader;
import li.strolch.utils.helper.ClassHelper;
import li.strolch.utils.helper.XmlHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This class implements the initializing of the {@link PrivilegeHandler} by loading an XML file containing the
* configuration
*
* @author Robert von Burg <eitch@eitchnet.ch>
*/
public class PrivilegeInitializationHelper {
private static final Logger logger = LoggerFactory.getLogger(PrivilegeInitializationHelper.class);
/**
* Initializes the {@link DefaultPrivilegeHandler} from the configuration file
*
* @param privilegeXmlFile
* a {@link File} reference to the XML file containing the configuration for Privilege
*
* @return the initialized {@link PrivilegeHandler} where the {@link EncryptionHandler} and {@link
* PersistenceHandler} are set and initialized as well
*/
public static PrivilegeHandler initializeFromXml(File privilegeXmlFile) {
// make sure file exists
if (!privilegeXmlFile.exists()) {
String msg = "Privilege file does not exist at path {0}";
msg = MessageFormat.format(msg, privilegeXmlFile.getAbsolutePath());
throw new PrivilegeException(msg);
}
// delegate using input stream
try (InputStream fin = Files.newInputStream(privilegeXmlFile.toPath())) {
return initializeFromXml(fin);
} catch (Exception e) {
String msg = "Failed to load configuration from {0}";
msg = MessageFormat.format(msg, privilegeXmlFile.getAbsolutePath());
throw new PrivilegeException(msg, e);
}
}
/**
* Initializes the {@link PrivilegeHandler} by loading from the given input stream. This stream must be a valid XML
* source
*
* @param privilegeConfigInputStream
* the XML stream containing the privilege configuration
*
* @return the initialized {@link PrivilegeHandler} where the {@link EncryptionHandler} and {@link
* PersistenceHandler} are set and initialized as well
*/
public static PrivilegeHandler initializeFromXml(InputStream privilegeConfigInputStream) {
// parse configuration file
PrivilegeContainerModel containerModel = new PrivilegeContainerModel();
PrivilegeConfigSaxReader xmlHandler = new PrivilegeConfigSaxReader(containerModel);
XmlHelper.parseDocument(privilegeConfigInputStream, xmlHandler);
return initializeFromXml(containerModel);
}
/**
* Initializes the {@link PrivilegeHandler} by initializing from the given {@link PrivilegeContainerModel}
*
* @param containerModel
* the configuration for the {@link PrivilegeHandler}
*
* @return the initialized {@link PrivilegeHandler} where the {@link EncryptionHandler} and {@link
* PersistenceHandler} are set and initialized as well
*/
public static PrivilegeHandler initializeFromXml(PrivilegeContainerModel containerModel) {
// initialize encryption handler
String encryptionHandlerClassName = containerModel.getEncryptionHandlerClassName();
EncryptionHandler encryptionHandler = ClassHelper.instantiateClass(encryptionHandlerClassName);
Map<String, String> parameterMap = containerModel.getEncryptionHandlerParameterMap();
try {
encryptionHandler.initialize(parameterMap);
} catch (Exception e) {
String msg = "EncryptionHandler {0} could not be initialized";
msg = MessageFormat.format(msg, encryptionHandlerClassName);
throw new PrivilegeException(msg, e);
}
// initialize password strength handler
String passwordStrengthHandlerClassName = containerModel.getPasswordStrengthHandlerClassName();
if (isEmpty(passwordStrengthHandlerClassName)) {
logger.info("No PasswordStrengthHandler defined, using " + SimplePasswordStrengthHandler.class.getName());
passwordStrengthHandlerClassName = SimplePasswordStrengthHandler.class.getName();
}
PasswordStrengthHandler passwordStrengthHandler = ClassHelper
.instantiateClass(passwordStrengthHandlerClassName);
parameterMap = containerModel.getPasswordStrengthHandlerParameterMap();
try {
passwordStrengthHandler.initialize(parameterMap);
} catch (Exception e) {
String msg = "PasswordStrengthHandler {0} could not be initialized";
msg = MessageFormat.format(msg, passwordStrengthHandlerClassName);
throw new PrivilegeException(msg, e);
}
// initialize persistence handler
String persistenceHandlerClassName = containerModel.getPersistenceHandlerClassName();
PersistenceHandler persistenceHandler = ClassHelper.instantiateClass(persistenceHandlerClassName);
parameterMap = containerModel.getPersistenceHandlerParameterMap();
try {
persistenceHandler.initialize(parameterMap);
} catch (Exception e) {
String msg = "PersistenceHandler {0} could not be initialized";
msg = MessageFormat.format(msg, persistenceHandlerClassName);
throw new PrivilegeException(msg, e);
}
// initialize challenge handler
UserChallengeHandler challengeHandler;
String challengeHandlerClassName = containerModel.getUserChallengeHandlerClassName();
challengeHandler = ClassHelper.instantiateClass(challengeHandlerClassName);
parameterMap = containerModel.getUserChallengeHandlerParameterMap();
try {
challengeHandler.initialize(parameterMap);
} catch (Exception e) {
String msg = "UserChallengeHandler {0} could not be initialized";
msg = MessageFormat.format(msg, persistenceHandlerClassName);
throw new PrivilegeException(msg, e);
}
// initialize SSO handler
SingleSignOnHandler ssoHandler;
if (containerModel.getSsoHandlerClassName() == null) {
ssoHandler = null;
} else {
String ssoHandlerClassName = containerModel.getSsoHandlerClassName();
ssoHandler = ClassHelper.instantiateClass(ssoHandlerClassName);
parameterMap = containerModel.getSsoHandlerParameterMap();
try {
ssoHandler.initialize(parameterMap);
} catch (Exception e) {
String msg = "SingleSignOnHandler {0} could not be initialized";
msg = MessageFormat.format(msg, ssoHandlerClassName);
throw new PrivilegeException(msg, e);
}
}
// initialize privilege handler
DefaultPrivilegeHandler privilegeHandler;
parameterMap = containerModel.getParameterMap();
if (containerModel.getPrivilegeHandlerClassName() == null) {
privilegeHandler = new DefaultPrivilegeHandler();
} else {
String privilegeHandlerClassName = containerModel.getPrivilegeHandlerClassName();
privilegeHandler = ClassHelper.instantiateClass(privilegeHandlerClassName);
parameterMap.putAll(containerModel.getPrivilegeHandlerParameterMap());
}
Map<String, Class<PrivilegePolicy>> policyMap = containerModel.getPolicies();
try {
privilegeHandler.initialize(parameterMap, encryptionHandler, passwordStrengthHandler, persistenceHandler,
challengeHandler, ssoHandler, policyMap);
} catch (Exception e) {
String msg = "PrivilegeHandler {0} could not be initialized";
msg = MessageFormat.format(msg, privilegeHandler.getClass().getName());
throw new PrivilegeException(msg, e);
}
return privilegeHandler;
}
}

View File

@ -0,0 +1,243 @@
/*
* 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.helper;
import static li.strolch.utils.helper.ClassHelper.instantiateClass;
import static li.strolch.utils.helper.StringHelper.isEmpty;
import java.io.File;
import java.io.InputStream;
import java.nio.file.Files;
import java.text.MessageFormat;
import java.util.Map;
import java.util.concurrent.ScheduledExecutorService;
import li.strolch.privilege.base.PrivilegeException;
import li.strolch.privilege.handler.*;
import li.strolch.privilege.model.internal.PrivilegeContainerModel;
import li.strolch.privilege.policy.PrivilegePolicy;
import li.strolch.privilege.xml.PrivilegeConfigSaxReader;
import li.strolch.utils.dbc.DBC;
import li.strolch.utils.helper.XmlHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This class implements the initializing of the {@link PrivilegeHandler} by loading an XML file containing the
* configuration
*
* @author Robert von Burg <eitch@eitchnet.ch>
*/
public class PrivilegeInitializer {
private static final Logger logger = LoggerFactory.getLogger(PrivilegeInitializer.class);
private final ScheduledExecutorService executorService;
private PrivilegeContainerModel containerModel;
private EncryptionHandler encryptionHandler;
private PasswordStrengthHandler passwordStrengthHandler;
private PersistenceHandler persistenceHandler;
private UserChallengeHandler challengeHandler;
private SingleSignOnHandler ssoHandler;
private PrivilegeHandler privilegeHandler;
public PrivilegeInitializer(ScheduledExecutorService executorService) {
DBC.PRE.assertNotNull("executorService may not be null", executorService);
this.executorService = executorService;
}
/**
* Initializes the {@link PrivilegeHandler} from the configuration file
*
* @param privilegeXmlFile
* a {@link File} reference to the XML file containing the configuration for Privilege
*
* @return the initialized {@link PrivilegeHandler} where the {@link EncryptionHandler} and
* {@link PersistenceHandler} are set and initialized as well
*/
public PrivilegeHandler initializeFromXml(File privilegeXmlFile) {
// make sure file exists
if (!privilegeXmlFile.exists()) {
String msg = "Privilege file does not exist at path {0}";
msg = MessageFormat.format(msg, privilegeXmlFile.getAbsolutePath());
throw new PrivilegeException(msg);
}
// delegate using input stream
try (InputStream fin = Files.newInputStream(privilegeXmlFile.toPath())) {
this.containerModel = parseXmlConfiguration(fin);
} catch (Exception e) {
String msg = "Failed to load configuration from {0}";
msg = MessageFormat.format(msg, privilegeXmlFile.getAbsolutePath());
throw new PrivilegeException(msg, e);
}
initializeComponents();
this.privilegeHandler = initializedPrivilegeHandler();
return this.privilegeHandler;
}
public PrivilegeHandler initializeFromXml(PrivilegeContainerModel containerModel) {
this.containerModel = containerModel;
initializeComponents();
this.privilegeHandler = initializedPrivilegeHandler();
return this.privilegeHandler;
}
/**
* Parses the privilege XML configuration from the given stream
*
* @param privilegeConfigInputStream
* the XML stream containing the privilege configuration
*/
public PrivilegeContainerModel parseXmlConfiguration(InputStream privilegeConfigInputStream) {
// parse configuration file
PrivilegeContainerModel containerModel = new PrivilegeContainerModel();
PrivilegeConfigSaxReader xmlHandler = new PrivilegeConfigSaxReader(containerModel);
XmlHelper.parseDocument(privilegeConfigInputStream, xmlHandler);
return containerModel;
}
/**
* Initializes the privilege handlers from the given container model
*/
private void initializeComponents() {
this.encryptionHandler = initializedEncryptionHandler();
this.passwordStrengthHandler = initializePasswordStrengthHandler();
this.persistenceHandler = initializePersistenceHandler();
this.challengeHandler = initializeUserChallengeHandler();
this.ssoHandler = initializeSingleSignOnHandler();
}
private PrivilegeHandler initializedPrivilegeHandler() {
DefaultPrivilegeHandler privilegeHandler;
Map<String, String> parameterMap = this.containerModel.getParameterMap();
if (this.containerModel.getPrivilegeHandlerClassName() == null) {
privilegeHandler = new DefaultPrivilegeHandler();
} else {
String privilegeHandlerClassName = this.containerModel.getPrivilegeHandlerClassName();
privilegeHandler = instantiateClass(privilegeHandlerClassName);
parameterMap.putAll(this.containerModel.getPrivilegeHandlerParameterMap());
}
Map<String, Class<PrivilegePolicy>> policyMap = this.containerModel.getPolicies();
try {
privilegeHandler.initialize(this.executorService, parameterMap, this.encryptionHandler,
this.passwordStrengthHandler, this.persistenceHandler, this.challengeHandler, this.ssoHandler,
policyMap);
} catch (Exception e) {
String msg = "PrivilegeHandler {0} could not be initialized";
msg = MessageFormat.format(msg, privilegeHandler.getClass().getName());
throw new PrivilegeException(msg, e);
}
return privilegeHandler;
}
private SingleSignOnHandler initializeSingleSignOnHandler() {
if (this.containerModel.getSsoHandlerClassName() == null)
return null;
String ssoHandlerClassName = this.containerModel.getSsoHandlerClassName();
SingleSignOnHandler ssoHandler = instantiateClass(ssoHandlerClassName);
Map<String, String> parameterMap = this.containerModel.getSsoHandlerParameterMap();
try {
ssoHandler.initialize(parameterMap);
} catch (Exception e) {
String msg = "SingleSignOnHandler {0} could not be initialized";
msg = MessageFormat.format(msg, ssoHandlerClassName);
throw new PrivilegeException(msg, e);
}
return ssoHandler;
}
private UserChallengeHandler initializeUserChallengeHandler() {
String challengeHandlerClassName = this.containerModel.getUserChallengeHandlerClassName();
UserChallengeHandler challengeHandler = instantiateClass(challengeHandlerClassName);
Map<String, String> parameterMap = this.containerModel.getUserChallengeHandlerParameterMap();
try {
challengeHandler.initialize(parameterMap);
} catch (Exception e) {
String msg = "UserChallengeHandler {0} could not be initialized";
msg = MessageFormat.format(msg, challengeHandlerClassName);
throw new PrivilegeException(msg, e);
}
return challengeHandler;
}
private PersistenceHandler initializePersistenceHandler() {
String persistenceHandlerClassName = this.containerModel.getPersistenceHandlerClassName();
PersistenceHandler persistenceHandler = instantiateClass(persistenceHandlerClassName);
Map<String, String> parameterMap = this.containerModel.getPersistenceHandlerParameterMap();
try {
persistenceHandler.initialize(parameterMap);
} catch (Exception e) {
String msg = "PersistenceHandler {0} could not be initialized";
msg = MessageFormat.format(msg, persistenceHandlerClassName);
throw new PrivilegeException(msg, e);
}
return persistenceHandler;
}
private PasswordStrengthHandler initializePasswordStrengthHandler() {
String passwordStrengthHandlerClassName = this.containerModel.getPasswordStrengthHandlerClassName();
if (isEmpty(passwordStrengthHandlerClassName)) {
logger.info("No PasswordStrengthHandler defined, using " + SimplePasswordStrengthHandler.class.getName());
passwordStrengthHandlerClassName = SimplePasswordStrengthHandler.class.getName();
}
PasswordStrengthHandler passwordStrengthHandler = instantiateClass(passwordStrengthHandlerClassName);
Map<String, String> parameterMap = this.containerModel.getPasswordStrengthHandlerParameterMap();
try {
passwordStrengthHandler.initialize(parameterMap);
} catch (Exception e) {
String msg = "PasswordStrengthHandler {0} could not be initialized";
msg = MessageFormat.format(msg, passwordStrengthHandlerClassName);
throw new PrivilegeException(msg, e);
}
return passwordStrengthHandler;
}
private EncryptionHandler initializedEncryptionHandler() {
String encryptionHandlerClassName = this.containerModel.getEncryptionHandlerClassName();
EncryptionHandler encryptionHandler = instantiateClass(encryptionHandlerClassName);
Map<String, String> parameterMap = this.containerModel.getEncryptionHandlerParameterMap();
try {
encryptionHandler.initialize(parameterMap);
} catch (Exception e) {
String msg = "EncryptionHandler {0} could not be initialized";
msg = MessageFormat.format(msg, encryptionHandlerClassName);
throw new PrivilegeException(msg, e);
}
return encryptionHandler;
}
}

View File

@ -1,7 +1,9 @@
package li.strolch.privilege.helper;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import li.strolch.privilege.model.internal.Role;
import li.strolch.privilege.xml.PrivilegeRolesDomWriter;
@ -26,7 +28,8 @@ public class WriteRolesFileHelper {
PrivilegeRolesSaxReader xmlHandler = new PrivilegeRolesSaxReader();
XmlHelper.parseDocument(src, xmlHandler);
List<Role> roles = xmlHandler.getRoles();
Map<String, Role> rolesMap = xmlHandler.getRoles();
List<Role> roles = new ArrayList<>(rolesMap.values());
PrivilegeRolesDomWriter configSaxWriter = new PrivilegeRolesDomWriter(roles, dst);
configSaxWriter.write();

View File

@ -38,13 +38,13 @@ public class PrivilegeRolesSaxReader extends DefaultHandler {
private final Deque<ElementParser> buildersStack = new ArrayDeque<>();
private final List<Role> roles;
private final Map<String, Role> roles;
public PrivilegeRolesSaxReader() {
this.roles = new ArrayList<>();
this.roles = new HashMap<>();
}
public List<Role> getRoles() {
public Map<String, Role> getRoles() {
return this.roles;
}
@ -84,19 +84,19 @@ public class PrivilegeRolesSaxReader extends DefaultHandler {
this.buildersStack.peek().notifyChild(elementParser);
}
// <Role name="AppUser">
// <Privilege name="li.strolch.privilege.test.model.TestRestrictable">
// <AllAllowed>true</AllAllowed>
// </Privilege>
// </Role>
// <Role name="system_admin_privileges">
// <Privilege name="li.strolch.privilege.test.model.TestSystemUserAction">
// <AllAllowed>true</AllAllowed>
// </Privilege>
// <Privilege name="li.strolch.privilege.test.model.TestSystemRestrictable">
// <AllAllowed>true</AllAllowed>
// </Privilege>
// </Role>
// <Role name="AppUser">
// <Privilege name="li.strolch.privilege.test.model.TestRestrictable">
// <AllAllowed>true</AllAllowed>
// </Privilege>
// </Role>
// <Role name="system_admin_privileges">
// <Privilege name="li.strolch.privilege.test.model.TestSystemUserAction">
// <AllAllowed>true</AllAllowed>
// </Privilege>
// <Privilege name="li.strolch.privilege.test.model.TestSystemRestrictable">
// <AllAllowed>true</AllAllowed>
// </Privilege>
// </Role>
public class RoleParser extends ElementParserAdapter {
@ -177,7 +177,7 @@ public class PrivilegeRolesSaxReader extends DefaultHandler {
}
case XmlConstants.XML_ROLE -> {
Role role = new Role(this.roleName, this.privileges);
getRoles().add(role);
roles.put(role.getName(), role);
logger.info(MessageFormat.format("New Role: {0}", role));
init();
}
@ -187,7 +187,7 @@ public class PrivilegeRolesSaxReader extends DefaultHandler {
static class PropertyParser extends ElementParserAdapter {
// <Property name="organizationalUnit" value="Development" />
// <Property name="organizationalUnit" value="Development" />
public final Map<String, String> parameterMap = new HashMap<>();
@ -197,9 +197,7 @@ public class PrivilegeRolesSaxReader extends DefaultHandler {
String key = attributes.getValue(XmlConstants.XML_ATTR_NAME).trim();
String value = attributes.getValue(XmlConstants.XML_ATTR_VALUE).trim();
this.parameterMap.put(key, value);
} else if (qName.equals(XmlConstants.XML_PROPERTIES)) {
// NO-OP
} else {
} else if (!qName.equals(XmlConstants.XML_PROPERTIES)) {
throw new IllegalArgumentException("Unhandled tag " + qName);
}
}

View File

@ -40,16 +40,18 @@ public class PrivilegeUsersSaxReader extends DefaultHandler {
private final Deque<ElementParser> buildersStack = new ArrayDeque<>();
private final List<User> users;
private final Map<String, User> users;
private final boolean caseInsensitiveUsername;
public PrivilegeUsersSaxReader() {
this.users = new ArrayList<>();
public PrivilegeUsersSaxReader(boolean caseInsensitiveUsername) {
this.caseInsensitiveUsername = caseInsensitiveUsername;
this.users = new HashMap<>();
}
/**
* @return the users
*/
public List<User> getUsers() {
public Map<String, User> getUsers() {
return this.users;
}
@ -88,25 +90,25 @@ public class PrivilegeUsersSaxReader extends DefaultHandler {
this.buildersStack.peek().notifyChild(elementParser);
}
// <User userId="1" username="admin" password="8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918">
// <Firstname>Application</Firstname>
// <Lastname>Administrator</Lastname>
// <State>ENABLED</State>
// <Locale>en-GB</Locale>
// <Roles>
// <Role>PrivilegeAdmin</Role>
// <Role>AppUser</Role>
// </Roles>
// <Properties>
// <Property name="organization" value="eitchnet.ch" />
// <Property name="organizationalUnit" value="Development" />
// </Properties>
// <History>
// <FirstLogin>2021-02-19T15:32:09.592+01:00</FirstLogin>
// <LastLogin>2021-02-19T15:32:09.592+01:00</LastLogin>
// <LastPasswordChange>2021-02-19T15:32:09.592+01:00</LastPasswordChange>
// </History>
// </User>
// <User userId="1" username="admin" password="8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918">
// <Firstname>Application</Firstname>
// <Lastname>Administrator</Lastname>
// <State>ENABLED</State>
// <Locale>en-GB</Locale>
// <Roles>
// <Role>PrivilegeAdmin</Role>
// <Role>AppUser</Role>
// </Roles>
// <Properties>
// <Property name="organization" value="eitchnet.ch" />
// <Property name="organizationalUnit" value="Development" />
// </Properties>
// <History>
// <FirstLogin>2021-02-19T15:32:09.592+01:00</FirstLogin>
// <LastLogin>2021-02-19T15:32:09.592+01:00</LastLogin>
// <LastPasswordChange>2021-02-19T15:32:09.592+01:00</LastPasswordChange>
// </History>
// </User>
public class UserParser extends ElementParserAdapter {
@ -210,11 +212,14 @@ public class PrivilegeUsersSaxReader extends DefaultHandler {
case XML_USER -> {
if (this.history == null)
this.history = new UserHistory();
User user = new User(this.userId, this.username, this.password, this.salt, this.hashAlgorithm,
hashIterations, hashKeyLength, this.firstName, this.lastname, this.userState, this.userRoles,
this.locale, this.parameters, this.passwordChangeRequested, this.history);
logger.info(MessageFormat.format("New User: {0}", user));
getUsers().add(user);
String username = caseInsensitiveUsername ? user.getUsername().toLowerCase() : user.getUsername();
users.put(username, user);
}
default -> {
if (!(qName.equals(XML_ROLES) //

View File

@ -1,16 +1,20 @@
package li.strolch.privilege.test;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertNotNull;
import java.io.File;
import java.nio.file.Files;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import li.strolch.privilege.base.PrivilegeException;
import li.strolch.privilege.handler.PrivilegeHandler;
import li.strolch.privilege.helper.PrivilegeInitializationHelper;
import li.strolch.privilege.helper.PrivilegeInitializer;
import li.strolch.privilege.model.Certificate;
import li.strolch.privilege.model.PrivilegeContext;
import li.strolch.utils.helper.FileHelper;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -18,12 +22,24 @@ public class AbstractPrivilegeTest {
protected static final Logger logger = LoggerFactory.getLogger(AbstractPrivilegeTest.class);
protected static ScheduledExecutorService executorService;
protected PrivilegeHandler privilegeHandler;
protected PrivilegeContext ctx;
@BeforeClass
public static void beforeClass() {
executorService = Executors.newScheduledThreadPool(1);
}
@AfterClass
public static void afterClass() {
if (executorService != null)
executorService.shutdownNow();
}
protected void login(String username, char[] password) {
Certificate certificate = privilegeHandler.authenticate(username, password, false);
assertTrue("Certificate is null!", certificate != null);
assertNotNull("Certificate is null!", certificate);
this.ctx = privilegeHandler.validate(certificate);
}
@ -101,7 +117,7 @@ public class AbstractPrivilegeTest {
protected void initialize(String dst, String configFilename) {
try {
File privilegeConfigFile = getPrivilegeConfigFile(dst, configFilename);
this.privilegeHandler = PrivilegeInitializationHelper.initializeFromXml(privilegeConfigFile);
this.privilegeHandler = new PrivilegeInitializer(executorService).initializeFromXml(privilegeConfigFile);
} catch (Exception e) {
logger.error(e.getMessage(), e);
throw new RuntimeException("Initialization failed", e);

View File

@ -30,7 +30,7 @@ public class PersistSessionsTest extends AbstractPrivilegeTest {
}
@Test
public void shouldPersistAndReloadSessions() {
public void shouldPersistAndReloadSessions() throws InterruptedException {
// assert no sessions file
File sessionsFile = new File("target/PersistSessionsTest/sessions.dat");
@ -39,6 +39,8 @@ public class PersistSessionsTest extends AbstractPrivilegeTest {
// login and assert sessions file was written
login("admin", "admin".toCharArray());
this.privilegeHandler.validate(ctx.getCertificate());
// persisting is async, once per second
Thread.sleep(1200L);
assertTrue("Sessions File should have been created!", sessionsFile.isFile());
// re-initialize and assert still logged in

View File

@ -112,8 +112,6 @@ public class XmlTest {
assertEquals(4, containerModel.getPolicies().size());
assertEquals(3, containerModel.getEncryptionHandlerParameterMap().size());
assertEquals(3, containerModel.getPersistenceHandlerParameterMap().size());
// TODO extend assertions to actual model
}
@Test
@ -150,11 +148,11 @@ public class XmlTest {
@Test
public void canReadUsers() {
PrivilegeUsersSaxReader xmlHandler = new PrivilegeUsersSaxReader();
PrivilegeUsersSaxReader xmlHandler = new PrivilegeUsersSaxReader(true);
File xmlFile = new File(SRC_TEST + "PrivilegeUsers.xml");
XmlHelper.parseDocument(xmlFile, xmlHandler);
List<User> users = xmlHandler.getUsers();
Map<String, User> users = xmlHandler.getUsers();
assertNotNull(users);
assertEquals(4, users.size());
@ -201,7 +199,7 @@ public class XmlTest {
File xmlFile = new File(SRC_TEST + "PrivilegeRoles.xml");
XmlHelper.parseDocument(xmlFile, xmlHandler);
List<Role> roles = xmlHandler.getRoles();
Map<String, Role> roles = xmlHandler.getRoles();
assertNotNull(roles);
assertEquals(6, roles.size());
@ -227,8 +225,8 @@ public class XmlTest {
assertEquals(0, privilegeAddRole.getAllowList().size());
assertEquals(0, privilegeAddRole.getDenyList().size());
IPrivilege privilegeRemRoleFromUser = privilegeAdmin
.getPrivilege(PrivilegeHandler.PRIVILEGE_REMOVE_ROLE_FROM_USER);
IPrivilege privilegeRemRoleFromUser = privilegeAdmin.getPrivilege(
PrivilegeHandler.PRIVILEGE_REMOVE_ROLE_FROM_USER);
assertTrue(privilegeRemRoleFromUser.isAllAllowed());
assertEquals(0, privilegeRemRoleFromUser.getAllowList().size());
assertEquals(0, privilegeRemRoleFromUser.getDenyList().size());
@ -254,16 +252,16 @@ public class XmlTest {
containsInAnyOrder("li.strolch.privilege.handler.SystemAction",
"li.strolch.privilege.test.model.TestSystemRestrictable"));
IPrivilege testSystemUserAction = systemAdminPrivileges
.getPrivilege("li.strolch.privilege.handler.SystemAction");
IPrivilege testSystemUserAction = systemAdminPrivileges.getPrivilege(
"li.strolch.privilege.handler.SystemAction");
assertEquals("li.strolch.privilege.handler.SystemAction", testSystemUserAction.getName());
assertEquals("DefaultPrivilege", testSystemUserAction.getPolicy());
assertFalse(testSystemUserAction.isAllAllowed());
assertEquals(1, testSystemUserAction.getAllowList().size());
assertEquals(1, testSystemUserAction.getDenyList().size());
IPrivilege testSystemRestrictable = systemAdminPrivileges
.getPrivilege("li.strolch.privilege.test.model.TestSystemRestrictable");
IPrivilege testSystemRestrictable = systemAdminPrivileges.getPrivilege(
"li.strolch.privilege.test.model.TestSystemRestrictable");
assertEquals("li.strolch.privilege.test.model.TestSystemRestrictable", testSystemRestrictable.getName());
assertEquals("DefaultPrivilege", testSystemRestrictable.getPolicy());
assertTrue(testSystemRestrictable.isAllAllowed());
@ -287,22 +285,14 @@ public class XmlTest {
MatcherAssert.assertThat(testSystemUserAction2.getDenyList(), containsInAnyOrder("goodbye"));
}
private User findUser(String username, List<User> users) {
for (User user : users) {
if (user.getUsername().equals(username))
return user;
}
throw new RuntimeException("No user exists with username " + username);
private User findUser(String username, Map<String, User> users) {
return Optional.ofNullable(users.get(username))
.orElseThrow(() -> new IllegalStateException("User " + username + " does not exist!"));
}
private Role findRole(String name, List<Role> roles) {
for (Role role : roles) {
if (role.getName().equals(name))
return role;
}
throw new RuntimeException("No role exists with name " + name);
private Role findRole(String name, Map<String, Role> roles) {
return Optional.ofNullable(roles.get(name))
.orElseThrow(() -> new IllegalStateException("Role " + name + " does not exist!"));
}
@Test
@ -337,16 +327,22 @@ public class XmlTest {
PrivilegeUsersDomWriter configSaxWriter = new PrivilegeUsersDomWriter(users, modelFile);
configSaxWriter.write();
PrivilegeUsersSaxReader xmlHandler = new PrivilegeUsersSaxReader();
PrivilegeUsersSaxReader xmlHandler = new PrivilegeUsersSaxReader(true);
XmlHelper.parseDocument(modelFile, xmlHandler);
List<User> parsedUsers = xmlHandler.getUsers();
Map<String, User> parsedUsers = xmlHandler.getUsers();
assertNotNull(parsedUsers);
assertEquals(2, parsedUsers.size());
User parsedUser1 = parsedUsers.stream().filter(u -> u.getUsername().equals("user1")).findAny()
User parsedUser1 = parsedUsers.values()
.stream()
.filter(u -> u.getUsername().equals("user1"))
.findAny()
.orElseThrow(() -> new RuntimeException("user1 missing!"));
User parsedUser2 = parsedUsers.stream().filter(u -> u.getUsername().equals("user2")).findAny()
User parsedUser2 = parsedUsers.values()
.stream()
.filter(u -> u.getUsername().equals("user2"))
.findAny()
.orElseThrow(() -> new RuntimeException("user2 missing!"));
assertEquals(user1.getFirstname(), parsedUser1.getFirstname());
@ -397,14 +393,20 @@ public class XmlTest {
PrivilegeRolesSaxReader xmlHandler = new PrivilegeRolesSaxReader();
XmlHelper.parseDocument(modelFile, xmlHandler);
List<Role> parsedRoles = xmlHandler.getRoles();
Map<String, Role> parsedRoles = xmlHandler.getRoles();
assertNotNull(parsedRoles);
assertEquals(2, parsedRoles.size());
assertEquals(2, parsedRoles.size());
Role parsedRole1 = parsedRoles.stream().filter(r -> r.getName().equals("role1")).findAny()
Role parsedRole1 = parsedRoles.values()
.stream()
.filter(r -> r.getName().equals("role1"))
.findAny()
.orElseThrow(() -> new RuntimeException("role1 missing!"));
Role parsedRole2 = parsedRoles.stream().filter(r -> r.getName().equals("role2")).findAny()
Role parsedRole2 = parsedRoles.values()
.stream()
.filter(r -> r.getName().equals("role2"))
.findAny()
.orElseThrow(() -> new RuntimeException("role2 missing!"));
Set<String> privilegeNames = role1.getPrivilegeNames();