Merge branch 'develop' into release/2.1

This commit is contained in:
Robert von Burg 2023-10-30 12:20:22 +01:00
commit 42ee3de41f
Signed by: eitch
GPG Key ID: 75DB9C85C74331F7
143 changed files with 4792 additions and 5668 deletions

View File

@ -15,8 +15,6 @@
*/ */
package li.strolch.policy; package li.strolch.policy;
import static li.strolch.model.StrolchModelConstants.PolicyConstants.PARAM_ORDER;
import li.strolch.agent.api.ComponentContainer; import li.strolch.agent.api.ComponentContainer;
import li.strolch.agent.api.StrolchAgent; import li.strolch.agent.api.StrolchAgent;
import li.strolch.agent.api.StrolchComponent; import li.strolch.agent.api.StrolchComponent;
@ -27,6 +25,8 @@ import li.strolch.service.api.Command;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import static li.strolch.model.StrolchModelConstants.PolicyConstants.PARAM_ORDER;
/** /**
* Interface for all Strolch policies, which are instantiated by the {@link PolicyHandler} * Interface for all Strolch policies, which are instantiated by the {@link PolicyHandler}
* *
@ -103,6 +103,14 @@ public abstract class StrolchPolicy {
return this.tx; return this.tx;
} }
/**
* Returns true if this TX is still open, or committing, and thus can still be used
* @return true if this TX is still open, or committing, and thus can still be used
*/
protected boolean isTxOpen() {
return this.tx.isOpen() || this.tx.isCommitting();
}
protected Order getOrder(IActivityElement element) { protected Order getOrder(IActivityElement element) {
return tx().getOrderByRelation(element.getRootElement(), PARAM_ORDER, true); return tx().getOrderByRelation(element.getRootElement(), PARAM_ORDER, true);
} }

View File

@ -19,7 +19,7 @@ import static java.lang.Boolean.parseBoolean;
import static java.util.concurrent.TimeUnit.NANOSECONDS; import static java.util.concurrent.TimeUnit.NANOSECONDS;
import static li.strolch.privilege.handler.PrivilegeHandler.PARAM_PERSIST_SESSIONS; import static li.strolch.privilege.handler.PrivilegeHandler.PARAM_PERSIST_SESSIONS;
import static li.strolch.privilege.handler.PrivilegeHandler.PARAM_PERSIST_SESSIONS_PATH; import static li.strolch.privilege.handler.PrivilegeHandler.PARAM_PERSIST_SESSIONS_PATH;
import static li.strolch.privilege.helper.XmlConstants.XML_PARAM_BASE_PATH; import static li.strolch.privilege.helper.XmlConstants.PARAM_BASE_PATH;
import static li.strolch.runtime.StrolchConstants.StrolchPrivilegeConstants.*; import static li.strolch.runtime.StrolchConstants.StrolchPrivilegeConstants.*;
import java.io.File; import java.io.File;
@ -126,7 +126,7 @@ public class DefaultStrolchPrivilegeHandler extends StrolchComponent implements
if (containerModel.getPersistenceHandlerClassName().equals(XmlPersistenceHandler.class.getName())) { if (containerModel.getPersistenceHandlerClassName().equals(XmlPersistenceHandler.class.getName())) {
Map<String, String> xmlParams = containerModel.getPersistenceHandlerParameterMap(); Map<String, String> xmlParams = containerModel.getPersistenceHandlerParameterMap();
File configPath = runtimeConfig.getConfigPath(); File configPath = runtimeConfig.getConfigPath();
xmlParams.put(XML_PARAM_BASE_PATH, configPath.getPath()); xmlParams.put(PARAM_BASE_PATH, configPath.getPath());
} }
return new PrivilegeInitializer(getScheduledExecutor(getName())).initializeFromXml(containerModel); return new PrivilegeInitializer(getScheduledExecutor(getName())).initializeFromXml(containerModel);

View File

@ -9,7 +9,7 @@ import li.strolch.model.StrolchRootElement;
import li.strolch.privilege.base.AccessDeniedException; import li.strolch.privilege.base.AccessDeniedException;
import li.strolch.privilege.base.PrivilegeException; import li.strolch.privilege.base.PrivilegeException;
import li.strolch.privilege.i18n.PrivilegeMessages; import li.strolch.privilege.i18n.PrivilegeMessages;
import li.strolch.privilege.model.IPrivilege; import li.strolch.privilege.model.Privilege;
import li.strolch.privilege.model.PrivilegeContext; import li.strolch.privilege.model.PrivilegeContext;
import li.strolch.privilege.model.Restrictable; import li.strolch.privilege.model.Restrictable;
import li.strolch.privilege.model.internal.Role; import li.strolch.privilege.model.internal.Role;
@ -20,10 +20,10 @@ public class ModelPrivilege implements PrivilegePolicy {
/** /**
* The value of {@link Restrictable#getPrivilegeValue()} is used to check if the {@link Role} has this privilege * The value of {@link Restrictable#getPrivilegeValue()} is used to check if the {@link Role} has this privilege
* *
* @see li.strolch.privilege.policy.PrivilegePolicy#validateAction(PrivilegeContext, IPrivilege, Restrictable) * @see li.strolch.privilege.policy.PrivilegePolicy#validateAction(PrivilegeContext, Privilege, Restrictable)
*/ */
@Override @Override
public void validateAction(PrivilegeContext ctx, IPrivilege privilege, Restrictable restrictable) public void validateAction(PrivilegeContext ctx, Privilege privilege, Restrictable restrictable)
throws AccessDeniedException { throws AccessDeniedException {
validateAction(ctx, privilege, restrictable, true); validateAction(ctx, privilege, restrictable, true);
} }
@ -31,15 +31,15 @@ public class ModelPrivilege implements PrivilegePolicy {
/** /**
* The value of {@link Restrictable#getPrivilegeValue()} is used to check if the {@link Role} has this privilege * The value of {@link Restrictable#getPrivilegeValue()} is used to check if the {@link Role} has this privilege
* *
* @see li.strolch.privilege.policy.PrivilegePolicy#validateAction(PrivilegeContext, IPrivilege, Restrictable) * @see li.strolch.privilege.policy.PrivilegePolicy#validateAction(PrivilegeContext, Privilege, Restrictable)
*/ */
@Override @Override
public boolean hasPrivilege(PrivilegeContext ctx, IPrivilege privilege, Restrictable restrictable) public boolean hasPrivilege(PrivilegeContext ctx, Privilege privilege, Restrictable restrictable)
throws PrivilegeException { throws PrivilegeException {
return validateAction(ctx, privilege, restrictable, false); return validateAction(ctx, privilege, restrictable, false);
} }
protected boolean validateAction(PrivilegeContext ctx, IPrivilege privilege, Restrictable restrictable, protected boolean validateAction(PrivilegeContext ctx, Privilege privilege, Restrictable restrictable,
boolean assertHasPrivilege) throws AccessDeniedException { boolean assertHasPrivilege) throws AccessDeniedException {
preValidate(privilege, restrictable); preValidate(privilege, restrictable);

View File

@ -13,21 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package li.strolch.rest; package li.strolch.runtime.sessions;
import static java.util.function.Function.identity;
import static li.strolch.runtime.StrolchConstants.StrolchPrivilegeConstants.PRIVILEGE_GET_SESSION;
import static li.strolch.runtime.StrolchConstants.StrolchPrivilegeConstants.PRIVILEGE_INVALIDATE_SESSION;
import java.text.MessageFormat;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import li.strolch.agent.api.ComponentContainer; import li.strolch.agent.api.ComponentContainer;
import li.strolch.agent.api.StrolchComponent; import li.strolch.agent.api.StrolchComponent;
@ -38,13 +24,25 @@ import li.strolch.privilege.model.Certificate;
import li.strolch.privilege.model.PrivilegeContext; import li.strolch.privilege.model.PrivilegeContext;
import li.strolch.privilege.model.SimpleRestrictable; import li.strolch.privilege.model.SimpleRestrictable;
import li.strolch.privilege.model.Usage; import li.strolch.privilege.model.Usage;
import li.strolch.rest.model.UserSession;
import li.strolch.runtime.configuration.ComponentConfiguration; import li.strolch.runtime.configuration.ComponentConfiguration;
import li.strolch.runtime.privilege.PrivilegeHandler; import li.strolch.runtime.privilege.PrivilegeHandler;
import li.strolch.utils.dbc.DBC; import li.strolch.utils.dbc.DBC;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.text.MessageFormat;
import java.time.ZonedDateTime;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import static java.util.function.Function.identity;
import static li.strolch.runtime.StrolchConstants.StrolchPrivilegeConstants.PRIVILEGE_GET_SESSION;
import static li.strolch.runtime.StrolchConstants.StrolchPrivilegeConstants.PRIVILEGE_INVALIDATE_SESSION;
/** /**
* @author Robert von Burg <eitch@eitchnet.ch> * @author Robert von Burg <eitch@eitchnet.ch>
*/ */
@ -52,12 +50,10 @@ public class DefaultStrolchSessionHandler extends StrolchComponent implements St
public static final String PARAM_SESSION_TTL_MINUTES = "session.ttl.minutes"; public static final String PARAM_SESSION_TTL_MINUTES = "session.ttl.minutes";
public static final String PARAM_SESSION_MAX_KEEP_ALIVE_MINUTES = "session.maxKeepAlive.minutes"; public static final String PARAM_SESSION_MAX_KEEP_ALIVE_MINUTES = "session.maxKeepAlive.minutes";
public static final String PARAM_SESSION_RELOAD_SESSIONS = "session.reload";
private static final Logger logger = LoggerFactory.getLogger(DefaultStrolchSessionHandler.class); private static final Logger logger = LoggerFactory.getLogger(DefaultStrolchSessionHandler.class);
private PrivilegeHandler privilegeHandler; private PrivilegeHandler privilegeHandler;
private final Map<String, Certificate> certificateMap; private final Map<String, Certificate> certificateMap;
private boolean reloadSessions;
private int sessionTtlMinutes; private int sessionTtlMinutes;
private int maxKeepAliveMinutes; private int maxKeepAliveMinutes;
@ -74,6 +70,29 @@ public class DefaultStrolchSessionHandler extends StrolchComponent implements St
return this.sessionTtlMinutes; return this.sessionTtlMinutes;
} }
@Override
public void refreshSessions() {
Map<String, Certificate> certificates;
try {
certificates = runAsAgentWithResult(ctx -> {
Certificate cert = ctx.getCertificate();
return this.privilegeHandler.getPrivilegeHandler().getCertificates(cert).stream()
.filter(c -> !c.getUserState().isSystem())
.collect(Collectors.toMap(Certificate::getAuthToken, identity()));
});
} catch (Exception e) {
throw new IllegalStateException("Failed to refresh sessions!", e);
}
synchronized (this.certificateMap) {
this.certificateMap.clear();
this.certificateMap.putAll(certificates);
}
checkSessionsForTimeout();
logger.info("Restored " + certificates.size() + " sessions of which " +
(certificates.size() - this.certificateMap.size()) + " had timed out and were removed.");
}
@Override @Override
public int getSessionMaxKeepAliveMinutes() { public int getSessionMaxKeepAliveMinutes() {
return this.maxKeepAliveMinutes; return this.maxKeepAliveMinutes;
@ -89,30 +108,14 @@ public class DefaultStrolchSessionHandler extends StrolchComponent implements St
this.sessionTtlMinutes = configuration.getInt(PARAM_SESSION_TTL_MINUTES, 30); this.sessionTtlMinutes = configuration.getInt(PARAM_SESSION_TTL_MINUTES, 30);
this.maxKeepAliveMinutes = configuration.getInt(PARAM_SESSION_MAX_KEEP_ALIVE_MINUTES, this.maxKeepAliveMinutes = configuration.getInt(PARAM_SESSION_MAX_KEEP_ALIVE_MINUTES,
Math.max(this.sessionTtlMinutes, 30)); Math.max(this.sessionTtlMinutes, 30));
this.reloadSessions = configuration.getBoolean(PARAM_SESSION_RELOAD_SESSIONS, false);
super.initialize(configuration); super.initialize(configuration);
} }
@Override @Override
public void start() throws Exception { public void start() throws Exception {
this.privilegeHandler = getContainer().getComponent(PrivilegeHandler.class); this.privilegeHandler = getContainer().getComponent(PrivilegeHandler.class);
this.certificateMap.clear();
if (this.reloadSessions) { refreshSessions();
Map<String, Certificate> certificates = runAsAgentWithResult(ctx -> {
Certificate cert = ctx.getCertificate();
return this.privilegeHandler.getPrivilegeHandler()
.getCertificates(cert)
.stream()
.filter(c -> !c.getUserState().isSystem())
.collect(Collectors.toMap(Certificate::getAuthToken, identity()));
});
this.certificateMap.putAll(certificates);
checkSessionsForTimeout();
logger.info("Restored " + certificates.size() + " sessions of which " + (certificates.size()
- this.certificateMap.size()) + " had timed out and were removed.");
}
this.validateSessionsTask = getScheduledExecutor("SessionHandler").scheduleWithFixedDelay( this.validateSessionsTask = getScheduledExecutor("SessionHandler").scheduleWithFixedDelay(
this::checkSessionsForTimeout, 5, 1, TimeUnit.MINUTES); this::checkSessionsForTimeout, 5, 1, TimeUnit.MINUTES);
@ -126,27 +129,6 @@ public class DefaultStrolchSessionHandler extends StrolchComponent implements St
if (this.validateSessionsTask != null) if (this.validateSessionsTask != null)
this.validateSessionsTask.cancel(true); this.validateSessionsTask.cancel(true);
if (this.reloadSessions) {
if (this.privilegeHandler != null)
persistSessions();
} else {
Map<String, Certificate> certificateMap;
synchronized (this.certificateMap) {
certificateMap = new HashMap<>(this.certificateMap);
this.certificateMap.clear();
}
for (Certificate certificate : certificateMap.values()) {
try {
this.privilegeHandler.invalidate(certificate);
} catch (Exception e) {
logger.error("Failed to invalidate certificate " + certificate, e);
}
}
}
this.privilegeHandler = null; this.privilegeHandler = null;
super.stop(); super.stop();
} }
@ -321,8 +303,8 @@ public class DefaultStrolchSessionHandler extends StrolchComponent implements St
} }
private void checkSessionsForTimeout() { private void checkSessionsForTimeout() {
ZonedDateTime maxKeepAliveTime = ZonedDateTime.now().minus(this.maxKeepAliveMinutes, ChronoUnit.MINUTES); ZonedDateTime maxKeepAliveTime = ZonedDateTime.now().minusMinutes(this.maxKeepAliveMinutes);
ZonedDateTime timeOutTime = ZonedDateTime.now().minus(this.sessionTtlMinutes, ChronoUnit.MINUTES); ZonedDateTime timeOutTime = ZonedDateTime.now().minusMinutes(this.sessionTtlMinutes);
Map<String, Certificate> certificateMap = getCertificateMapCopy(); Map<String, Certificate> certificateMap = getCertificateMapCopy();
for (Certificate certificate : certificateMap.values()) { for (Certificate certificate : certificateMap.values()) {

View File

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package li.strolch.rest; package li.strolch.runtime.sessions;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
@ -24,7 +24,6 @@ import li.strolch.privilege.base.PrivilegeException;
import li.strolch.privilege.model.Certificate; import li.strolch.privilege.model.Certificate;
import li.strolch.privilege.model.PrivilegeContext; import li.strolch.privilege.model.PrivilegeContext;
import li.strolch.privilege.model.Usage; import li.strolch.privilege.model.Usage;
import li.strolch.rest.model.UserSession;
/** /**
* The {@link StrolchSessionHandler} implements session management. It authenticates, validates and invalidates session * The {@link StrolchSessionHandler} implements session management. It authenticates, validates and invalidates session
@ -34,6 +33,11 @@ import li.strolch.rest.model.UserSession;
*/ */
public interface StrolchSessionHandler { public interface StrolchSessionHandler {
/**
* Refreshes the sessions from the {@link li.strolch.runtime.privilege.PrivilegeHandler}
*/
void refreshSessions();
/** /**
* Returns the time to live for a session in minutes * Returns the time to live for a session in minutes
* *

View File

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package li.strolch.rest.model; package li.strolch.runtime.sessions;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import java.util.Locale; import java.util.Locale;

View File

@ -1,40 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Privilege> <Privilege>
<Container> <Container>
<Parameters> <Parameters>
<!-- parameters for the container itself --> <Parameter name="secretKey" value="CHANGE-ME"/>
<Parameter name="secretKey" value="58d27145-5d1a-48ea-9e1c-f5a522f6a19f"/> <Parameter name="secretSalt" value="CHANGE-ME"/>
<Parameter name="secretSalt" value="25bd8911-0008-464f-82b8-4c5200db8dd7"/>
<Parameter name="autoPersistOnUserChangesData" value="true" /> <Parameter name="autoPersistOnUserChangesData" value="true" />
<Parameter name="privilegeConflictResolution" value="MERGE"/>
</Parameters> </Parameters>
<EncryptionHandler class="li.strolch.privilege.handler.DefaultEncryptionHandler"> <EncryptionHandler class="li.strolch.privilege.handler.DefaultEncryptionHandler">
<Parameters> <Parameters>
<!-- WARNING: If you change iterations or keyLength, then all passwords are invalid -->
<!-- default algorithm is: PBKDF2WithHmacSHA512 -->
<Parameter name="hashAlgorithm" value="PBKDF2WithHmacSHA512" /> <Parameter name="hashAlgorithm" value="PBKDF2WithHmacSHA512" />
<!-- default iterations: 200000 -->
<Parameter name="hashIterations" value="10000" /> <Parameter name="hashIterations" value="10000" />
<!-- default key length: 256 -->
<Parameter name="hashKeyLength" value="256" /> <Parameter name="hashKeyLength" value="256" />
</Parameters> </Parameters>
</EncryptionHandler> </EncryptionHandler>
<PersistenceHandler class="li.strolch.privilege.handler.XmlPersistenceHandler"/>
<PersistenceHandler class="li.strolch.privilege.handler.XmlPersistenceHandler"> <UserChallengeHandler class="li.strolch.privilege.handler.ConsoleUserChallengeHandler"/>
<Parameters>
<Parameter name="usersXmlFile" value="PrivilegeUsers.xml" />
<Parameter name="rolesXmlFile" value="PrivilegeRoles.xml" />
</Parameters>
</PersistenceHandler>
<UserChallengeHandler class="li.strolch.privilege.handler.ConsoleUserChallengeHandler">
</UserChallengeHandler>
</Container> </Container>
<Policies> <Policies>
<Policy name="DefaultPrivilege" class="li.strolch.privilege.policy.DefaultPrivilege" /> <Policy name="DefaultPrivilege" class="li.strolch.privilege.policy.DefaultPrivilege" />
<Policy name="ModelPrivilege" class="li.strolch.runtime.privilege.ModelPrivilege" /> <Policy name="ModelPrivilege" class="li.strolch.runtime.privilege.ModelPrivilege" />
@ -42,5 +23,4 @@
<Policy name="UserAccessPrivilege" class="li.strolch.privilege.policy.UserAccessPrivilege" /> <Policy name="UserAccessPrivilege" class="li.strolch.privilege.policy.UserAccessPrivilege" />
<Policy name="UserSessionAccessPrivilege" class="li.strolch.privilege.policy.UsernameFromCertificatePrivilege"/> <Policy name="UserSessionAccessPrivilege" class="li.strolch.privilege.policy.UsernameFromCertificatePrivilege"/>
</Policies> </Policies>
</Privilege> </Privilege>

View File

@ -1,40 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Privilege> <Privilege>
<Container> <Container>
<Parameters> <Parameters>
<!-- parameters for the container itself --> <Parameter name="secretKey" value="CHANGE-ME"/>
<Parameter name="secretKey" value="58d27145-5d1a-48ea-9e1c-f5a522f6a19f"/> <Parameter name="secretSalt" value="CHANGE-ME"/>
<Parameter name="secretSalt" value="25bd8911-0008-464f-82b8-4c5200db8dd7"/>
<Parameter name="autoPersistOnUserChangesData" value="true" /> <Parameter name="autoPersistOnUserChangesData" value="true" />
<Parameter name="privilegeConflictResolution" value="MERGE"/>
</Parameters> </Parameters>
<EncryptionHandler class="li.strolch.privilege.handler.DefaultEncryptionHandler"> <EncryptionHandler class="li.strolch.privilege.handler.DefaultEncryptionHandler">
<Parameters> <Parameters>
<!-- WARNING: If you change iterations or keyLength, then all passwords are invalid -->
<!-- default algorithm is: PBKDF2WithHmacSHA512 -->
<Parameter name="hashAlgorithm" value="PBKDF2WithHmacSHA512" /> <Parameter name="hashAlgorithm" value="PBKDF2WithHmacSHA512" />
<!-- default iterations: 200000 -->
<Parameter name="hashIterations" value="10000" /> <Parameter name="hashIterations" value="10000" />
<!-- default key length: 256 -->
<Parameter name="hashKeyLength" value="256" /> <Parameter name="hashKeyLength" value="256" />
</Parameters> </Parameters>
</EncryptionHandler> </EncryptionHandler>
<PersistenceHandler class="li.strolch.privilege.handler.XmlPersistenceHandler"/>
<PersistenceHandler class="li.strolch.privilege.handler.XmlPersistenceHandler"> <UserChallengeHandler class="li.strolch.privilege.handler.ConsoleUserChallengeHandler"/>
<Parameters>
<Parameter name="usersXmlFile" value="PrivilegeUsers.xml" />
<Parameter name="rolesXmlFile" value="PrivilegeRoles.xml" />
</Parameters>
</PersistenceHandler>
<UserChallengeHandler class="li.strolch.privilege.handler.ConsoleUserChallengeHandler">
</UserChallengeHandler>
</Container> </Container>
<Policies> <Policies>
<Policy name="DefaultPrivilege" class="li.strolch.privilege.policy.DefaultPrivilege" /> <Policy name="DefaultPrivilege" class="li.strolch.privilege.policy.DefaultPrivilege" />
<Policy name="ModelPrivilege" class="li.strolch.runtime.privilege.ModelPrivilege" /> <Policy name="ModelPrivilege" class="li.strolch.runtime.privilege.ModelPrivilege" />
@ -42,5 +23,4 @@
<Policy name="UserAccessPrivilege" class="li.strolch.privilege.policy.UserAccessPrivilege" /> <Policy name="UserAccessPrivilege" class="li.strolch.privilege.policy.UserAccessPrivilege" />
<Policy name="UserSessionAccessPrivilege" class="li.strolch.privilege.policy.UsernameFromCertificatePrivilege"/> <Policy name="UserSessionAccessPrivilege" class="li.strolch.privilege.policy.UsernameFromCertificatePrivilege"/>
</Policies> </Policies>
</Privilege> </Privilege>

View File

@ -1,40 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Privilege> <Privilege>
<Container> <Container>
<Parameters> <Parameters>
<!-- parameters for the container itself --> <Parameter name="secretKey" value="CHANGE-ME"/>
<Parameter name="secretKey" value="58d27145-5d1a-48ea-9e1c-f5a522f6a19f"/> <Parameter name="secretSalt" value="CHANGE-ME"/>
<Parameter name="secretSalt" value="25bd8911-0008-464f-82b8-4c5200db8dd7"/>
<Parameter name="autoPersistOnUserChangesData" value="true" /> <Parameter name="autoPersistOnUserChangesData" value="true" />
<Parameter name="privilegeConflictResolution" value="MERGE"/>
</Parameters> </Parameters>
<EncryptionHandler class="li.strolch.privilege.handler.DefaultEncryptionHandler"> <EncryptionHandler class="li.strolch.privilege.handler.DefaultEncryptionHandler">
<Parameters> <Parameters>
<!-- WARNING: If you change iterations or keyLength, then all passwords are invalid -->
<!-- default algorithm is: PBKDF2WithHmacSHA512 -->
<Parameter name="hashAlgorithm" value="PBKDF2WithHmacSHA512" /> <Parameter name="hashAlgorithm" value="PBKDF2WithHmacSHA512" />
<!-- default iterations: 200000 -->
<Parameter name="hashIterations" value="10000" /> <Parameter name="hashIterations" value="10000" />
<!-- default key length: 256 -->
<Parameter name="hashKeyLength" value="256" /> <Parameter name="hashKeyLength" value="256" />
</Parameters> </Parameters>
</EncryptionHandler> </EncryptionHandler>
<PersistenceHandler class="li.strolch.privilege.handler.XmlPersistenceHandler"/>
<PersistenceHandler class="li.strolch.privilege.handler.XmlPersistenceHandler"> <UserChallengeHandler class="li.strolch.privilege.handler.ConsoleUserChallengeHandler"/>
<Parameters>
<Parameter name="usersXmlFile" value="PrivilegeUsers.xml" />
<Parameter name="rolesXmlFile" value="PrivilegeRoles.xml" />
</Parameters>
</PersistenceHandler>
<UserChallengeHandler class="li.strolch.privilege.handler.ConsoleUserChallengeHandler">
</UserChallengeHandler>
</Container> </Container>
<Policies> <Policies>
<Policy name="DefaultPrivilege" class="li.strolch.privilege.policy.DefaultPrivilege" /> <Policy name="DefaultPrivilege" class="li.strolch.privilege.policy.DefaultPrivilege" />
<Policy name="ModelPrivilege" class="li.strolch.runtime.privilege.ModelPrivilege" /> <Policy name="ModelPrivilege" class="li.strolch.runtime.privilege.ModelPrivilege" />
@ -42,5 +23,4 @@
<Policy name="UserAccessPrivilege" class="li.strolch.privilege.policy.UserAccessPrivilege" /> <Policy name="UserAccessPrivilege" class="li.strolch.privilege.policy.UserAccessPrivilege" />
<Policy name="UserSessionAccessPrivilege" class="li.strolch.privilege.policy.UsernameFromCertificatePrivilege"/> <Policy name="UserSessionAccessPrivilege" class="li.strolch.privilege.policy.UsernameFromCertificatePrivilege"/>
</Policies> </Policies>
</Privilege> </Privilege>

View File

@ -1,40 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Privilege> <Privilege>
<Container> <Container>
<Parameters> <Parameters>
<!-- parameters for the container itself --> <Parameter name="secretKey" value="CHANGE-ME"/>
<Parameter name="secretKey" value="58d27145-5d1a-48ea-9e1c-f5a522f6a19f"/> <Parameter name="secretSalt" value="CHANGE-ME"/>
<Parameter name="secretSalt" value="25bd8911-0008-464f-82b8-4c5200db8dd7"/>
<Parameter name="autoPersistOnUserChangesData" value="true" /> <Parameter name="autoPersistOnUserChangesData" value="true" />
<Parameter name="privilegeConflictResolution" value="MERGE"/>
</Parameters> </Parameters>
<EncryptionHandler class="li.strolch.privilege.handler.DefaultEncryptionHandler"> <EncryptionHandler class="li.strolch.privilege.handler.DefaultEncryptionHandler">
<Parameters> <Parameters>
<!-- WARNING: If you change iterations or keyLength, then all passwords are invalid -->
<!-- default algorithm is: PBKDF2WithHmacSHA512 -->
<Parameter name="hashAlgorithm" value="PBKDF2WithHmacSHA512" /> <Parameter name="hashAlgorithm" value="PBKDF2WithHmacSHA512" />
<!-- default iterations: 200000 -->
<Parameter name="hashIterations" value="10000" /> <Parameter name="hashIterations" value="10000" />
<!-- default key length: 256 -->
<Parameter name="hashKeyLength" value="256" /> <Parameter name="hashKeyLength" value="256" />
</Parameters> </Parameters>
</EncryptionHandler> </EncryptionHandler>
<PersistenceHandler class="li.strolch.privilege.handler.XmlPersistenceHandler"/>
<PersistenceHandler class="li.strolch.privilege.handler.XmlPersistenceHandler"> <UserChallengeHandler class="li.strolch.privilege.handler.ConsoleUserChallengeHandler"/>
<Parameters>
<Parameter name="usersXmlFile" value="PrivilegeUsers.xml" />
<Parameter name="rolesXmlFile" value="PrivilegeRoles.xml" />
</Parameters>
</PersistenceHandler>
<UserChallengeHandler class="li.strolch.privilege.handler.ConsoleUserChallengeHandler">
</UserChallengeHandler>
</Container> </Container>
<Policies> <Policies>
<Policy name="DefaultPrivilege" class="li.strolch.privilege.policy.DefaultPrivilege" /> <Policy name="DefaultPrivilege" class="li.strolch.privilege.policy.DefaultPrivilege" />
<Policy name="ModelPrivilege" class="li.strolch.runtime.privilege.ModelPrivilege" /> <Policy name="ModelPrivilege" class="li.strolch.runtime.privilege.ModelPrivilege" />
@ -42,5 +23,4 @@
<Policy name="UserAccessPrivilege" class="li.strolch.privilege.policy.UserAccessPrivilege" /> <Policy name="UserAccessPrivilege" class="li.strolch.privilege.policy.UserAccessPrivilege" />
<Policy name="UserSessionAccessPrivilege" class="li.strolch.privilege.policy.UsernameFromCertificatePrivilege"/> <Policy name="UserSessionAccessPrivilege" class="li.strolch.privilege.policy.UsernameFromCertificatePrivilege"/>
</Policies> </Policies>
</Privilege> </Privilege>

View File

@ -1,40 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Privilege> <Privilege>
<Container> <Container>
<Parameters> <Parameters>
<!-- parameters for the container itself --> <Parameter name="secretKey" value="CHANGE-ME"/>
<Parameter name="secretKey" value="58d27145-5d1a-48ea-9e1c-f5a522f6a19f"/> <Parameter name="secretSalt" value="CHANGE-ME"/>
<Parameter name="secretSalt" value="25bd8911-0008-464f-82b8-4c5200db8dd7"/>
<Parameter name="autoPersistOnUserChangesData" value="true" /> <Parameter name="autoPersistOnUserChangesData" value="true" />
<Parameter name="privilegeConflictResolution" value="MERGE"/>
</Parameters> </Parameters>
<EncryptionHandler class="li.strolch.privilege.handler.DefaultEncryptionHandler"> <EncryptionHandler class="li.strolch.privilege.handler.DefaultEncryptionHandler">
<Parameters> <Parameters>
<!-- WARNING: If you change iterations or keyLength, then all passwords are invalid -->
<!-- default algorithm is: PBKDF2WithHmacSHA512 -->
<Parameter name="hashAlgorithm" value="PBKDF2WithHmacSHA512" /> <Parameter name="hashAlgorithm" value="PBKDF2WithHmacSHA512" />
<!-- default iterations: 200000 -->
<Parameter name="hashIterations" value="10000" /> <Parameter name="hashIterations" value="10000" />
<!-- default key length: 256 -->
<Parameter name="hashKeyLength" value="256" /> <Parameter name="hashKeyLength" value="256" />
</Parameters> </Parameters>
</EncryptionHandler> </EncryptionHandler>
<PersistenceHandler class="li.strolch.privilege.handler.XmlPersistenceHandler"/>
<PersistenceHandler class="li.strolch.privilege.handler.XmlPersistenceHandler"> <UserChallengeHandler class="li.strolch.privilege.handler.ConsoleUserChallengeHandler"/>
<Parameters>
<Parameter name="usersXmlFile" value="PrivilegeUsers.xml" />
<Parameter name="rolesXmlFile" value="PrivilegeRoles.xml" />
</Parameters>
</PersistenceHandler>
<UserChallengeHandler class="li.strolch.privilege.handler.ConsoleUserChallengeHandler">
</UserChallengeHandler>
</Container> </Container>
<Policies> <Policies>
<Policy name="DefaultPrivilege" class="li.strolch.privilege.policy.DefaultPrivilege" /> <Policy name="DefaultPrivilege" class="li.strolch.privilege.policy.DefaultPrivilege" />
<Policy name="ModelPrivilege" class="li.strolch.runtime.privilege.ModelPrivilege" /> <Policy name="ModelPrivilege" class="li.strolch.runtime.privilege.ModelPrivilege" />
@ -42,5 +23,4 @@
<Policy name="UserAccessPrivilege" class="li.strolch.privilege.policy.UserAccessPrivilege" /> <Policy name="UserAccessPrivilege" class="li.strolch.privilege.policy.UserAccessPrivilege" />
<Policy name="UserSessionAccessPrivilege" class="li.strolch.privilege.policy.UsernameFromCertificatePrivilege"/> <Policy name="UserSessionAccessPrivilege" class="li.strolch.privilege.policy.UsernameFromCertificatePrivilege"/>
</Policies> </Policies>
</Privilege> </Privilege>

View File

@ -213,10 +213,10 @@ public enum State {
} }
/** /**
* @return true if {@link #PLANNED} or {@link #EXECUTION} or {@link #WARNING} or {@link #ERROR} * @return true if {@link #PLANNED} or {@link #EXECUTION} or {@link #WARNING} or {@link #ERROR} or {@link #STOPPED}
*/ */
public boolean canSetToError() { public boolean canSetToError() {
return this == PLANNED || this == EXECUTION || this == WARNING || this == ERROR; return this == PLANNED || this == EXECUTION || this == WARNING || this == ERROR || this == STOPPED;
} }
/** /**
@ -282,8 +282,8 @@ public enum State {
// execution // execution
if (states.contains(EXECUTABLE) || states.contains(EXECUTION)) if (states.contains(EXECUTABLE) || states.contains(EXECUTION))
return EXECUTION; return EXECUTION;
if (states.contains(EXECUTED) && (states.contains(CREATED) || states.contains(PLANNING) || states.contains( if (states.contains(EXECUTED) &&
PLANNED))) (states.contains(CREATED) || states.contains(PLANNING) || states.contains(PLANNED)))
return EXECUTION; return EXECUTION;
// executed // executed

View File

@ -740,6 +740,8 @@ public class Activity extends AbstractStrolchRootElement
@Override @Override
public Activity ensureModifiable() { public Activity ensureModifiable() {
if (!this.isRootElement())
throw new IllegalStateException("Only call this method on the root element!");
if (isReadOnly()) if (isReadOnly())
return getClone(true); return getClone(true);
return this; return this;

View File

@ -1,7 +1,5 @@
package li.strolch.model.json; package li.strolch.model.json;
import java.util.*;
import com.google.gson.JsonArray; import com.google.gson.JsonArray;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
@ -11,6 +9,8 @@ import li.strolch.privilege.model.RoleRep;
import li.strolch.privilege.model.UserRep; import li.strolch.privilege.model.UserRep;
import li.strolch.privilege.model.UserState; import li.strolch.privilege.model.UserState;
import java.util.*;
public class PrivilegeElementFromJsonVisitor { public class PrivilegeElementFromJsonVisitor {
public UserRep userRepFromJson(String string) { public UserRep userRepFromJson(String string) {
@ -32,11 +32,12 @@ public class PrivilegeElementFromJsonVisitor {
String name = nameE == null ? null : nameE.getAsString().trim(); String name = nameE == null ? null : nameE.getAsString().trim();
List<PrivilegeRep> privileges = new ArrayList<>(); Map<String, PrivilegeRep> privileges = new HashMap<>();
if (privilegesE != null) { if (privilegesE != null) {
JsonArray privilegesArr = privilegesE.getAsJsonArray(); JsonArray privilegesArr = privilegesE.getAsJsonArray();
for (JsonElement privilegeE : privilegesArr) { for (JsonElement privilegeE : privilegesArr) {
privileges.add(privilegeRepFromJson(privilegeE.getAsJsonObject())); PrivilegeRep privilegeRep = privilegeRepFromJson(privilegeE.getAsJsonObject());
privileges.put(privilegeRep.getName(), privilegeRep);
} }
} }
@ -82,6 +83,7 @@ public class PrivilegeElementFromJsonVisitor {
JsonElement lastNameE = jsonObject.get("lastname"); JsonElement lastNameE = jsonObject.get("lastname");
JsonElement userStateE = jsonObject.get("userState"); JsonElement userStateE = jsonObject.get("userState");
JsonElement localeE = jsonObject.get("locale"); JsonElement localeE = jsonObject.get("locale");
JsonElement groupsE = jsonObject.get("groups");
JsonElement rolesE = jsonObject.get("roles"); JsonElement rolesE = jsonObject.get("roles");
JsonElement propertiesE = jsonObject.get("properties"); JsonElement propertiesE = jsonObject.get("properties");
@ -90,16 +92,11 @@ public class PrivilegeElementFromJsonVisitor {
String firstname = firstNameE == null ? null : firstNameE.getAsString().trim(); String firstname = firstNameE == null ? null : firstNameE.getAsString().trim();
String lastname = lastNameE == null ? null : lastNameE.getAsString().trim(); String lastname = lastNameE == null ? null : lastNameE.getAsString().trim();
UserState userState = userStateE == null ? null : UserState.valueOf(userStateE.getAsString().trim()); UserState userState = userStateE == null ? null : UserState.valueOf(userStateE.getAsString().trim());
Locale locale = localeE == null ? null : new Locale(localeE.getAsString().trim()); Locale locale = localeE == null ? null : Locale.forLanguageTag(localeE.getAsString().trim());
Set<String> roles = null; Set<String> groups = jsonArrayToSet(groupsE);
if (rolesE != null) {
roles = new HashSet<>(); Set<String> roles = jsonArrayToSet(rolesE);
JsonArray rolesArr = rolesE.getAsJsonArray();
for (JsonElement role : rolesArr) {
roles.add(role.getAsString().trim());
}
}
Map<String, String> properties = null; Map<String, String> properties = null;
if (propertiesE != null) { if (propertiesE != null) {
@ -111,6 +108,19 @@ public class PrivilegeElementFromJsonVisitor {
} }
} }
return new UserRep(userId, username, firstname, lastname, userState, roles, locale, properties, null); return new UserRep(userId, username, firstname, lastname, userState, groups, roles, locale, properties, null);
}
private Set<String> jsonArrayToSet(JsonElement array) {
if (array == null)
return Set.of();
Set<String> result = new HashSet<>();
JsonArray rolesArr = array.getAsJsonArray();
for (JsonElement role : rolesArr) {
result.add(role.getAsString().trim());
}
return result;
} }
} }

View File

@ -53,7 +53,7 @@ public class PrivilegeElementToJsonVisitor implements PrivilegeElementVisitor<Js
jsonObject.addProperty("name", roleRep.getName()); jsonObject.addProperty("name", roleRep.getName());
JsonArray privilegesJ = new JsonArray(); JsonArray privilegesJ = new JsonArray();
roleRep.getPrivileges().forEach(p -> privilegesJ.add(p.accept(this))); roleRep.getPrivileges().values().forEach(p -> privilegesJ.add(p.accept(this)));
jsonObject.add("privileges", privilegesJ); jsonObject.add("privileges", privilegesJ);
return jsonObject; return jsonObject;

View File

@ -1,46 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Privilege> <Privilege>
<Container> <Container>
<Parameters> <Parameters>
<!-- parameters for the container itself --> <Parameter name="secretKey" value="CHANGE-ME"/>
<Parameter name="secretKey" value="58d27145-5d1a-48ea-9e1c-f5a522f6a19f"/> <Parameter name="secretSalt" value="CHANGE-ME"/>
<Parameter name="secretSalt" value="25bd8911-0008-464f-82b8-4c5200db8dd7"/>
<Parameter name="autoPersistOnUserChangesData" value="true" /> <Parameter name="autoPersistOnUserChangesData" value="true" />
<Parameter name="privilegeConflictResolution" value="MERGE"/>
</Parameters> </Parameters>
<EncryptionHandler class="li.strolch.privilege.handler.DefaultEncryptionHandler"> <EncryptionHandler class="li.strolch.privilege.handler.DefaultEncryptionHandler">
<Parameters> <Parameters>
<!-- WARNING: If you change iterations or keyLength, then all passwords are invalid -->
<!-- default algorithm is: PBKDF2WithHmacSHA512 -->
<Parameter name="hashAlgorithm" value="PBKDF2WithHmacSHA512" /> <Parameter name="hashAlgorithm" value="PBKDF2WithHmacSHA512" />
<!-- default iterations: 200000 -->
<Parameter name="hashIterations" value="10000" /> <Parameter name="hashIterations" value="10000" />
<!-- default key length: 256 -->
<Parameter name="hashKeyLength" value="256" /> <Parameter name="hashKeyLength" value="256" />
</Parameters> </Parameters>
</EncryptionHandler> </EncryptionHandler>
<PersistenceHandler class="li.strolch.privilege.handler.XmlPersistenceHandler"/>
<PersistenceHandler class="li.strolch.privilege.handler.XmlPersistenceHandler"> <UserChallengeHandler class="li.strolch.privilege.handler.ConsoleUserChallengeHandler"/>
<Parameters>
<Parameter name="usersXmlFile" value="PrivilegeUsers.xml" />
<Parameter name="rolesXmlFile" value="PrivilegeRoles.xml" />
</Parameters>
</PersistenceHandler>
<UserChallengeHandler class="li.strolch.privilege.handler.ConsoleUserChallengeHandler">
</UserChallengeHandler>
</Container> </Container>
<Policies> <Policies>
<Policy name="DefaultPrivilege" class="li.strolch.privilege.policy.DefaultPrivilege"/> <Policy name="DefaultPrivilege" class="li.strolch.privilege.policy.DefaultPrivilege" />
<Policy name="ModelPrivilege" class="li.strolch.runtime.privilege.ModelPrivilege"/> <Policy name="ModelPrivilege" class="li.strolch.runtime.privilege.ModelPrivilege" />
<Policy name="RoleAccessPrivilege" class="li.strolch.privilege.policy.RoleAccessPrivilege"/> <Policy name="RoleAccessPrivilege" class="li.strolch.privilege.policy.RoleAccessPrivilege" />
<Policy name="UserAccessPrivilege" class="li.strolch.privilege.policy.UserAccessPrivilege"/> <Policy name="UserAccessPrivilege" class="li.strolch.privilege.policy.UserAccessPrivilege" />
<Policy name="UserSessionAccessPrivilege" class="li.strolch.privilege.policy.UsernameFromCertificatePrivilege"/> <Policy name="UserSessionAccessPrivilege" class="li.strolch.privilege.policy.UsernameFromCertificatePrivilege"/>
</Policies> </Policies>
</Privilege> </Privilege>

View File

@ -1,40 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Privilege> <Privilege>
<Container> <Container>
<Parameters> <Parameters>
<!-- parameters for the container itself --> <Parameter name="secretKey" value="CHANGE-ME"/>
<Parameter name="secretKey" value="58d27145-5d1a-48ea-9e1c-f5a522f6a19f"/> <Parameter name="secretSalt" value="CHANGE-ME"/>
<Parameter name="secretSalt" value="25bd8911-0008-464f-82b8-4c5200db8dd7"/>
<Parameter name="autoPersistOnUserChangesData" value="true" /> <Parameter name="autoPersistOnUserChangesData" value="true" />
<Parameter name="privilegeConflictResolution" value="MERGE"/>
</Parameters> </Parameters>
<EncryptionHandler class="li.strolch.privilege.handler.DefaultEncryptionHandler"> <EncryptionHandler class="li.strolch.privilege.handler.DefaultEncryptionHandler">
<Parameters> <Parameters>
<!-- WARNING: If you change iterations or keyLength, then all passwords are invalid -->
<!-- default algorithm is: PBKDF2WithHmacSHA512 -->
<Parameter name="hashAlgorithm" value="PBKDF2WithHmacSHA512" /> <Parameter name="hashAlgorithm" value="PBKDF2WithHmacSHA512" />
<!-- default iterations: 200000 -->
<Parameter name="hashIterations" value="10000" /> <Parameter name="hashIterations" value="10000" />
<!-- default key length: 256 -->
<Parameter name="hashKeyLength" value="256" /> <Parameter name="hashKeyLength" value="256" />
</Parameters> </Parameters>
</EncryptionHandler> </EncryptionHandler>
<PersistenceHandler class="li.strolch.privilege.handler.XmlPersistenceHandler"/>
<PersistenceHandler class="li.strolch.privilege.handler.XmlPersistenceHandler"> <UserChallengeHandler class="li.strolch.privilege.handler.ConsoleUserChallengeHandler"/>
<Parameters>
<Parameter name="usersXmlFile" value="PrivilegeUsers.xml" />
<Parameter name="rolesXmlFile" value="PrivilegeRoles.xml" />
</Parameters>
</PersistenceHandler>
<UserChallengeHandler class="li.strolch.privilege.handler.ConsoleUserChallengeHandler">
</UserChallengeHandler>
</Container> </Container>
<Policies> <Policies>
<Policy name="DefaultPrivilege" class="li.strolch.privilege.policy.DefaultPrivilege" /> <Policy name="DefaultPrivilege" class="li.strolch.privilege.policy.DefaultPrivilege" />
<Policy name="ModelPrivilege" class="li.strolch.runtime.privilege.ModelPrivilege" /> <Policy name="ModelPrivilege" class="li.strolch.runtime.privilege.ModelPrivilege" />
@ -42,5 +23,4 @@
<Policy name="UserAccessPrivilege" class="li.strolch.privilege.policy.UserAccessPrivilege" /> <Policy name="UserAccessPrivilege" class="li.strolch.privilege.policy.UserAccessPrivilege" />
<Policy name="UserSessionAccessPrivilege" class="li.strolch.privilege.policy.UsernameFromCertificatePrivilege"/> <Policy name="UserSessionAccessPrivilege" class="li.strolch.privilege.policy.UsernameFromCertificatePrivilege"/>
</Policies> </Policies>
</Privilege> </Privilege>

View File

@ -1,40 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Privilege> <Privilege>
<Container> <Container>
<Parameters> <Parameters>
<!-- parameters for the container itself --> <Parameter name="secretKey" value="CHANGE-ME"/>
<Parameter name="secretKey" value="58d27145-5d1a-48ea-9e1c-f5a522f6a19f"/> <Parameter name="secretSalt" value="CHANGE-ME"/>
<Parameter name="secretSalt" value="25bd8911-0008-464f-82b8-4c5200db8dd7"/>
<Parameter name="autoPersistOnUserChangesData" value="true" /> <Parameter name="autoPersistOnUserChangesData" value="true" />
<Parameter name="privilegeConflictResolution" value="MERGE"/>
</Parameters> </Parameters>
<EncryptionHandler class="li.strolch.privilege.handler.DefaultEncryptionHandler"> <EncryptionHandler class="li.strolch.privilege.handler.DefaultEncryptionHandler">
<Parameters> <Parameters>
<!-- WARNING: If you change iterations or keyLength, then all passwords are invalid -->
<!-- default algorithm is: PBKDF2WithHmacSHA512 -->
<Parameter name="hashAlgorithm" value="PBKDF2WithHmacSHA512" /> <Parameter name="hashAlgorithm" value="PBKDF2WithHmacSHA512" />
<!-- default iterations: 200000 -->
<Parameter name="hashIterations" value="10000" /> <Parameter name="hashIterations" value="10000" />
<!-- default key length: 256 -->
<Parameter name="hashKeyLength" value="256" /> <Parameter name="hashKeyLength" value="256" />
</Parameters> </Parameters>
</EncryptionHandler> </EncryptionHandler>
<PersistenceHandler class="li.strolch.privilege.handler.XmlPersistenceHandler"/>
<PersistenceHandler class="li.strolch.privilege.handler.XmlPersistenceHandler"> <UserChallengeHandler class="li.strolch.privilege.handler.ConsoleUserChallengeHandler"/>
<Parameters>
<Parameter name="usersXmlFile" value="PrivilegeUsers.xml" />
<Parameter name="rolesXmlFile" value="PrivilegeRoles.xml" />
</Parameters>
</PersistenceHandler>
<UserChallengeHandler class="li.strolch.privilege.handler.ConsoleUserChallengeHandler">
</UserChallengeHandler>
</Container> </Container>
<Policies> <Policies>
<Policy name="DefaultPrivilege" class="li.strolch.privilege.policy.DefaultPrivilege" /> <Policy name="DefaultPrivilege" class="li.strolch.privilege.policy.DefaultPrivilege" />
<Policy name="ModelPrivilege" class="li.strolch.runtime.privilege.ModelPrivilege" /> <Policy name="ModelPrivilege" class="li.strolch.runtime.privilege.ModelPrivilege" />
@ -42,5 +23,4 @@
<Policy name="UserAccessPrivilege" class="li.strolch.privilege.policy.UserAccessPrivilege" /> <Policy name="UserAccessPrivilege" class="li.strolch.privilege.policy.UserAccessPrivilege" />
<Policy name="UserSessionAccessPrivilege" class="li.strolch.privilege.policy.UsernameFromCertificatePrivilege"/> <Policy name="UserSessionAccessPrivilege" class="li.strolch.privilege.policy.UsernameFromCertificatePrivilege"/>
</Policies> </Policies>
</Privilege> </Privilege>

View File

@ -1,40 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Privilege> <Privilege>
<Container> <Container>
<Parameters> <Parameters>
<!-- parameters for the container itself --> <Parameter name="secretKey" value="CHANGE-ME"/>
<Parameter name="secretKey" value="58d27145-5d1a-48ea-9e1c-f5a522f6a19f"/> <Parameter name="secretSalt" value="CHANGE-ME"/>
<Parameter name="secretSalt" value="25bd8911-0008-464f-82b8-4c5200db8dd7"/>
<Parameter name="autoPersistOnUserChangesData" value="true" /> <Parameter name="autoPersistOnUserChangesData" value="true" />
<Parameter name="privilegeConflictResolution" value="MERGE"/>
</Parameters> </Parameters>
<EncryptionHandler class="li.strolch.privilege.handler.DefaultEncryptionHandler"> <EncryptionHandler class="li.strolch.privilege.handler.DefaultEncryptionHandler">
<Parameters> <Parameters>
<!-- WARNING: If you change iterations or keyLength, then all passwords are invalid -->
<!-- default algorithm is: PBKDF2WithHmacSHA512 -->
<Parameter name="hashAlgorithm" value="PBKDF2WithHmacSHA512" /> <Parameter name="hashAlgorithm" value="PBKDF2WithHmacSHA512" />
<!-- default iterations: 200000 -->
<Parameter name="hashIterations" value="10000" /> <Parameter name="hashIterations" value="10000" />
<!-- default key length: 256 -->
<Parameter name="hashKeyLength" value="256" /> <Parameter name="hashKeyLength" value="256" />
</Parameters> </Parameters>
</EncryptionHandler> </EncryptionHandler>
<PersistenceHandler class="li.strolch.privilege.handler.XmlPersistenceHandler"/>
<PersistenceHandler class="li.strolch.privilege.handler.XmlPersistenceHandler"> <UserChallengeHandler class="li.strolch.privilege.handler.ConsoleUserChallengeHandler"/>
<Parameters>
<Parameter name="usersXmlFile" value="PrivilegeUsers.xml" />
<Parameter name="rolesXmlFile" value="PrivilegeRoles.xml" />
</Parameters>
</PersistenceHandler>
<UserChallengeHandler class="li.strolch.privilege.handler.ConsoleUserChallengeHandler">
</UserChallengeHandler>
</Container> </Container>
<Policies> <Policies>
<Policy name="DefaultPrivilege" class="li.strolch.privilege.policy.DefaultPrivilege" /> <Policy name="DefaultPrivilege" class="li.strolch.privilege.policy.DefaultPrivilege" />
<Policy name="ModelPrivilege" class="li.strolch.runtime.privilege.ModelPrivilege" /> <Policy name="ModelPrivilege" class="li.strolch.runtime.privilege.ModelPrivilege" />
@ -42,5 +23,4 @@
<Policy name="UserAccessPrivilege" class="li.strolch.privilege.policy.UserAccessPrivilege" /> <Policy name="UserAccessPrivilege" class="li.strolch.privilege.policy.UserAccessPrivilege" />
<Policy name="UserSessionAccessPrivilege" class="li.strolch.privilege.policy.UsernameFromCertificatePrivilege"/> <Policy name="UserSessionAccessPrivilege" class="li.strolch.privilege.policy.UsernameFromCertificatePrivilege"/>
</Policies> </Policies>
</Privilege> </Privilege>

View File

@ -1,40 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Privilege> <Privilege>
<Container> <Container>
<Parameters> <Parameters>
<!-- parameters for the container itself --> <Parameter name="secretKey" value="CHANGE-ME"/>
<Parameter name="secretKey" value="58d27145-5d1a-48ea-9e1c-f5a522f6a19f"/> <Parameter name="secretSalt" value="CHANGE-ME"/>
<Parameter name="secretSalt" value="25bd8911-0008-464f-82b8-4c5200db8dd7"/>
<Parameter name="autoPersistOnUserChangesData" value="true" /> <Parameter name="autoPersistOnUserChangesData" value="true" />
<Parameter name="privilegeConflictResolution" value="MERGE"/>
</Parameters> </Parameters>
<EncryptionHandler class="li.strolch.privilege.handler.DefaultEncryptionHandler"> <EncryptionHandler class="li.strolch.privilege.handler.DefaultEncryptionHandler">
<Parameters> <Parameters>
<!-- WARNING: If you change iterations or keyLength, then all passwords are invalid -->
<!-- default algorithm is: PBKDF2WithHmacSHA512 -->
<Parameter name="hashAlgorithm" value="PBKDF2WithHmacSHA512" /> <Parameter name="hashAlgorithm" value="PBKDF2WithHmacSHA512" />
<!-- default iterations: 200000 -->
<Parameter name="hashIterations" value="10000" /> <Parameter name="hashIterations" value="10000" />
<!-- default key length: 256 -->
<Parameter name="hashKeyLength" value="256" /> <Parameter name="hashKeyLength" value="256" />
</Parameters> </Parameters>
</EncryptionHandler> </EncryptionHandler>
<PersistenceHandler class="li.strolch.privilege.handler.XmlPersistenceHandler"/>
<PersistenceHandler class="li.strolch.privilege.handler.XmlPersistenceHandler"> <UserChallengeHandler class="li.strolch.privilege.handler.ConsoleUserChallengeHandler"/>
<Parameters>
<Parameter name="usersXmlFile" value="PrivilegeUsers.xml" />
<Parameter name="rolesXmlFile" value="PrivilegeRoles.xml" />
</Parameters>
</PersistenceHandler>
<UserChallengeHandler class="li.strolch.privilege.handler.ConsoleUserChallengeHandler">
</UserChallengeHandler>
</Container> </Container>
<Policies> <Policies>
<Policy name="DefaultPrivilege" class="li.strolch.privilege.policy.DefaultPrivilege" /> <Policy name="DefaultPrivilege" class="li.strolch.privilege.policy.DefaultPrivilege" />
<Policy name="ModelPrivilege" class="li.strolch.runtime.privilege.ModelPrivilege" /> <Policy name="ModelPrivilege" class="li.strolch.runtime.privilege.ModelPrivilege" />
@ -42,5 +23,4 @@
<Policy name="UserAccessPrivilege" class="li.strolch.privilege.policy.UserAccessPrivilege" /> <Policy name="UserAccessPrivilege" class="li.strolch.privilege.policy.UserAccessPrivilege" />
<Policy name="UserSessionAccessPrivilege" class="li.strolch.privilege.policy.UsernameFromCertificatePrivilege"/> <Policy name="UserSessionAccessPrivilege" class="li.strolch.privilege.policy.UsernameFromCertificatePrivilege"/>
</Policies> </Policies>
</Privilege> </Privilege>

View File

@ -1,40 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Privilege> <Privilege>
<Container> <Container>
<Parameters> <Parameters>
<!-- parameters for the container itself --> <Parameter name="secretKey" value="CHANGE-ME"/>
<Parameter name="secretKey" value="58d27145-5d1a-48ea-9e1c-f5a522f6a19f"/> <Parameter name="secretSalt" value="CHANGE-ME"/>
<Parameter name="secretSalt" value="25bd8911-0008-464f-82b8-4c5200db8dd7"/>
<Parameter name="autoPersistOnUserChangesData" value="true" /> <Parameter name="autoPersistOnUserChangesData" value="true" />
<Parameter name="privilegeConflictResolution" value="MERGE"/>
</Parameters> </Parameters>
<EncryptionHandler class="li.strolch.privilege.handler.DefaultEncryptionHandler"> <EncryptionHandler class="li.strolch.privilege.handler.DefaultEncryptionHandler">
<Parameters> <Parameters>
<!-- WARNING: If you change iterations or keyLength, then all passwords are invalid -->
<!-- default algorithm is: PBKDF2WithHmacSHA512 -->
<Parameter name="hashAlgorithm" value="PBKDF2WithHmacSHA512" /> <Parameter name="hashAlgorithm" value="PBKDF2WithHmacSHA512" />
<!-- default iterations: 200000 -->
<Parameter name="hashIterations" value="10000" /> <Parameter name="hashIterations" value="10000" />
<!-- default key length: 256 -->
<Parameter name="hashKeyLength" value="256" /> <Parameter name="hashKeyLength" value="256" />
</Parameters> </Parameters>
</EncryptionHandler> </EncryptionHandler>
<PersistenceHandler class="li.strolch.privilege.handler.XmlPersistenceHandler"/>
<PersistenceHandler class="li.strolch.privilege.handler.XmlPersistenceHandler"> <UserChallengeHandler class="li.strolch.privilege.handler.ConsoleUserChallengeHandler"/>
<Parameters>
<Parameter name="usersXmlFile" value="PrivilegeUsers.xml" />
<Parameter name="rolesXmlFile" value="PrivilegeRoles.xml" />
</Parameters>
</PersistenceHandler>
<UserChallengeHandler class="li.strolch.privilege.handler.ConsoleUserChallengeHandler">
</UserChallengeHandler>
</Container> </Container>
<Policies> <Policies>
<Policy name="DefaultPrivilege" class="li.strolch.privilege.policy.DefaultPrivilege" /> <Policy name="DefaultPrivilege" class="li.strolch.privilege.policy.DefaultPrivilege" />
<Policy name="ModelPrivilege" class="li.strolch.runtime.privilege.ModelPrivilege" /> <Policy name="ModelPrivilege" class="li.strolch.runtime.privilege.ModelPrivilege" />
@ -42,5 +23,4 @@
<Policy name="UserAccessPrivilege" class="li.strolch.privilege.policy.UserAccessPrivilege" /> <Policy name="UserAccessPrivilege" class="li.strolch.privilege.policy.UserAccessPrivilege" />
<Policy name="UserSessionAccessPrivilege" class="li.strolch.privilege.policy.UsernameFromCertificatePrivilege"/> <Policy name="UserSessionAccessPrivilege" class="li.strolch.privilege.policy.UsernameFromCertificatePrivilege"/>
</Policies> </Policies>
</Privilege> </Privilege>

View File

@ -1,40 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Privilege> <Privilege>
<Container> <Container>
<Parameters> <Parameters>
<!-- parameters for the container itself --> <Parameter name="secretKey" value="CHANGE-ME"/>
<Parameter name="secretKey" value="58d27145-5d1a-48ea-9e1c-f5a522f6a19f"/> <Parameter name="secretSalt" value="CHANGE-ME"/>
<Parameter name="secretSalt" value="25bd8911-0008-464f-82b8-4c5200db8dd7"/>
<Parameter name="autoPersistOnUserChangesData" value="true" /> <Parameter name="autoPersistOnUserChangesData" value="true" />
<Parameter name="privilegeConflictResolution" value="MERGE"/>
</Parameters> </Parameters>
<EncryptionHandler class="li.strolch.privilege.handler.DefaultEncryptionHandler"> <EncryptionHandler class="li.strolch.privilege.handler.DefaultEncryptionHandler">
<Parameters> <Parameters>
<!-- WARNING: If you change iterations or keyLength, then all passwords are invalid -->
<!-- default algorithm is: PBKDF2WithHmacSHA512 -->
<Parameter name="hashAlgorithm" value="PBKDF2WithHmacSHA512" /> <Parameter name="hashAlgorithm" value="PBKDF2WithHmacSHA512" />
<!-- default iterations: 200000 -->
<Parameter name="hashIterations" value="10000" /> <Parameter name="hashIterations" value="10000" />
<!-- default key length: 256 -->
<Parameter name="hashKeyLength" value="256" /> <Parameter name="hashKeyLength" value="256" />
</Parameters> </Parameters>
</EncryptionHandler> </EncryptionHandler>
<PersistenceHandler class="li.strolch.privilege.handler.XmlPersistenceHandler"/>
<PersistenceHandler class="li.strolch.privilege.handler.XmlPersistenceHandler"> <UserChallengeHandler class="li.strolch.privilege.handler.ConsoleUserChallengeHandler"/>
<Parameters>
<Parameter name="usersXmlFile" value="PrivilegeUsers.xml" />
<Parameter name="rolesXmlFile" value="PrivilegeRoles.xml" />
</Parameters>
</PersistenceHandler>
<UserChallengeHandler class="li.strolch.privilege.handler.ConsoleUserChallengeHandler">
</UserChallengeHandler>
</Container> </Container>
<Policies> <Policies>
<Policy name="DefaultPrivilege" class="li.strolch.privilege.policy.DefaultPrivilege" /> <Policy name="DefaultPrivilege" class="li.strolch.privilege.policy.DefaultPrivilege" />
<Policy name="ModelPrivilege" class="li.strolch.runtime.privilege.ModelPrivilege" /> <Policy name="ModelPrivilege" class="li.strolch.runtime.privilege.ModelPrivilege" />
@ -42,5 +23,4 @@
<Policy name="UserAccessPrivilege" class="li.strolch.privilege.policy.UserAccessPrivilege" /> <Policy name="UserAccessPrivilege" class="li.strolch.privilege.policy.UserAccessPrivilege" />
<Policy name="UserSessionAccessPrivilege" class="li.strolch.privilege.policy.UsernameFromCertificatePrivilege"/> <Policy name="UserSessionAccessPrivilege" class="li.strolch.privilege.policy.UsernameFromCertificatePrivilege"/>
</Policies> </Policies>
</Privilege> </Privilege>

View File

@ -14,6 +14,7 @@ public class PrivilegeConstants {
public static final String PRIMARY_LOCATION = "primaryLocation"; public static final String PRIMARY_LOCATION = "primaryLocation";
public static final String SECONDARY_LOCATIONS = "secondaryLocations"; public static final String SECONDARY_LOCATIONS = "secondaryLocations";
public static final String ROLES = "roles"; public static final String ROLES = "roles";
public static final String GROUPS = "groups";
public static final String EMAIL = "email"; public static final String EMAIL = "email";
public static final String ROLE_STROLCH_ADMIN = "StrolchAdmin"; public static final String ROLE_STROLCH_ADMIN = "StrolchAdmin";

View File

@ -1,8 +1,18 @@
package li.strolch.privilege.handler; package li.strolch.privilege.handler;
import li.strolch.privilege.base.AccessDeniedException;
import li.strolch.privilege.model.UserState;
import li.strolch.privilege.model.internal.User;
import li.strolch.privilege.model.internal.UserHistory;
import li.strolch.privilege.policy.PrivilegePolicy;
import li.strolch.utils.helper.ExceptionHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.naming.Context; import javax.naming.Context;
import javax.naming.NamingEnumeration; import javax.naming.NamingEnumeration;
import javax.naming.NamingException; import javax.naming.NamingException;
import javax.naming.PartialResultException;
import javax.naming.directory.*; import javax.naming.directory.*;
import java.util.Hashtable; import java.util.Hashtable;
import java.util.Locale; import java.util.Locale;
@ -10,21 +20,20 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import li.strolch.privilege.base.AccessDeniedException; import static li.strolch.utils.helper.StringHelper.trimOrEmpty;
import li.strolch.privilege.model.UserState;
import li.strolch.privilege.model.internal.User;
import li.strolch.privilege.model.internal.UserHistory;
import li.strolch.privilege.policy.PrivilegePolicy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class BaseLdapPrivilegeHandler extends DefaultPrivilegeHandler { public abstract class BaseLdapPrivilegeHandler extends DefaultPrivilegeHandler {
protected static final Logger logger = LoggerFactory.getLogger(BaseLdapPrivilegeHandler.class); protected static final Logger logger = LoggerFactory.getLogger(BaseLdapPrivilegeHandler.class);
public static final String LDAP_FILTER_TEMPLATE = "(&(objectCategory=person)(objectClass=user)(%s=%s)%s)";
public static final String SAM_ACCOUNT_NAME = "sAMAccountName";
public static final String USER_PRINCIPAL_NAME = "userPrincipalName";
private String providerUrl; private String providerUrl;
private String searchBase; private String searchBase;
private String additionalFilter;
private String domain; private String domain;
private String domainPrefix;
@Override @Override
public void initialize(ScheduledExecutorService executorService, Map<String, String> parameterMap, public void initialize(ScheduledExecutorService executorService, Map<String, String> parameterMap,
@ -36,9 +45,24 @@ public abstract class BaseLdapPrivilegeHandler extends DefaultPrivilegeHandler {
userChallengeHandler, ssoHandler, policyMap); userChallengeHandler, ssoHandler, policyMap);
this.providerUrl = parameterMap.get("providerUrl"); this.providerUrl = parameterMap.get("providerUrl");
logger.info("providerUrl: " + this.providerUrl);
this.searchBase = parameterMap.get("searchBase"); this.searchBase = parameterMap.get("searchBase");
this.domain = parameterMap.get("domain"); logger.info("searchBase: " + this.searchBase);
this.additionalFilter = trimOrEmpty(parameterMap.get("additionalFilter"));
if (!this.additionalFilter.isEmpty())
logger.info("additionalFilter: " + this.additionalFilter);
this.domain = trimOrEmpty(parameterMap.get("domain"));
if (!this.domain.isEmpty()) {
if (this.domain.startsWith("@")) {
logger.warn(
"Remove the @ symbol from the domain property! Will be added automatically where required.");
this.domain = this.domain.substring(1);
}
this.domainPrefix = this.domain + '\\';
logger.info("domain: " + this.domain);
logger.info("domain prefix: " + this.domainPrefix);
}
} }
@Override @Override
@ -49,52 +73,24 @@ public abstract class BaseLdapPrivilegeHandler extends DefaultPrivilegeHandler {
if (internalUser != null && internalUser.getUserState() != UserState.REMOTE) if (internalUser != null && internalUser.getUserState() != UserState.REMOTE)
return super.checkCredentialsAndUserState(username, password); return super.checkCredentialsAndUserState(username, password);
// Set up the environment for creating the initial context String userPrincipalName;
Hashtable<String, String> env = new Hashtable<>(); if (this.domain.isEmpty()) {
userPrincipalName = username;
} else {
if (!this.domainPrefix.isEmpty() && username.startsWith(this.domainPrefix)) {
logger.warn("Trimming domain from given username, to first search in sAMAccountName");
username = username.substring(this.domainPrefix.length());
}
userPrincipalName = username + "@" + this.domain;
}
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); logger.info("User {} tries to login on ldap {}", username, this.providerUrl);
env.put(Context.PROVIDER_URL, this.providerUrl);
// Authenticate
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, username + this.domain);
env.put(Context.SECURITY_CREDENTIALS, new String(password));
logger.info("User {} tries to login on ldap {}", username + this.domain, this.providerUrl);
// Create the initial context // Create the initial context
DirContext ctx = null; DirContext ctx = null;
try { try {
ctx = new InitialDirContext(env); ctx = new InitialDirContext(buildLdapEnv(password, userPrincipalName));
SearchResult searchResult = searchLdap(username, ctx, userPrincipalName);
//Create the search controls
SearchControls searchCtls = new SearchControls();
//Specify the search scope
searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
String searchFilter = "(&(objectCategory=person)(objectClass=user)(userPrincipalName=" + username +
this.domain + "))";
// Search for objects using the filter
NamingEnumeration<SearchResult> answer = ctx.search(this.searchBase, searchFilter, searchCtls);
if (!answer.hasMore()) {
logger.warn("No LDAP data retrieved using userPrincipalName, trying with sAMAccountName...");
searchFilter = "(&(objectCategory=person)(objectClass=user)(sAMAccountName=" + username + "))";
answer = ctx.search(this.searchBase, searchFilter, searchCtls);
if (!answer.hasMore())
throw new AccessDeniedException("Could not login with user: " + username + this.domain +
" on Ldap: no LDAP Data, for either userPrincipalName or sAMAccountName");
}
SearchResult searchResult = answer.next();
if (answer.hasMore())
throw new AccessDeniedException(
"Could not login with user: " + username + this.domain + " on Ldap: Multiple LDAP Data");
User user = buildUserFromSearchResult(username, searchResult); User user = buildUserFromSearchResult(username, searchResult);
// persist this user // persist this user
@ -104,13 +100,15 @@ public abstract class BaseLdapPrivilegeHandler extends DefaultPrivilegeHandler {
this.persistenceHandler.replaceUser(user); this.persistenceHandler.replaceUser(user);
if (this.autoPersistOnUserChangesData) if (this.autoPersistOnUserChangesData)
this.persistenceHandler.persist(); persistModelAsync();
return user; return user;
} catch (AccessDeniedException e) {
throw e;
} catch (Exception e) { } catch (Exception e) {
logger.error("Could not login with user: " + username + this.domain + " on Ldap", e); logger.error("Could not login with user: " + username + " on Ldap", e);
throw new AccessDeniedException("Could not login with user: " + username + this.domain + " on Ldap", e); throw new AccessDeniedException("Could not login with user: " + username + " on Ldap", e);
} finally { } finally {
if (ctx != null) { if (ctx != null) {
try { try {
@ -122,6 +120,65 @@ public abstract class BaseLdapPrivilegeHandler extends DefaultPrivilegeHandler {
} }
} }
private Hashtable<String, String> buildLdapEnv(char[] password, String userPrincipalName) {
// Set up the environment for creating the initial context
Hashtable<String, String> env = new Hashtable<>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, this.providerUrl);
// Authenticate
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, userPrincipalName);
env.put(Context.SECURITY_CREDENTIALS, new String(password));
env.put(Context.REFERRAL, "ignore");
return env;
}
private SearchResult searchLdap(String username, DirContext ctx, String userPrincipalName) throws NamingException {
SearchControls searchControls = new SearchControls();
searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
// the first search is using sAMAccountName
NamingEnumeration<SearchResult> answer = ctx.search(this.searchBase,
LDAP_FILTER_TEMPLATE.formatted(SAM_ACCOUNT_NAME, username, this.additionalFilter), searchControls);
SearchResult searchResult = null;
while (searchResult == null) {
try {
// and if we don't find anything, then we search with userPrincipalName
if (!answer.hasMore()) {
logger.warn("No LDAP data retrieved using " + SAM_ACCOUNT_NAME + ", trying with " +
USER_PRINCIPAL_NAME + "...");
answer = ctx.search(this.searchBase,
LDAP_FILTER_TEMPLATE.formatted(USER_PRINCIPAL_NAME, userPrincipalName,
this.additionalFilter), searchControls);
if (!answer.hasMore())
throw new AccessDeniedException("Could not login user: " + username +
" on Ldap: no LDAP Data, for either sAMAccountName or userPrincipalName searches. Domain used is " +
this.domain);
}
searchResult = answer.next();
if (answer.hasMore())
throw new AccessDeniedException(
"Could not login with user: " + username + " on Ldap: Multiple LDAP Data");
} catch (PartialResultException e) {
if (ExceptionHelper.getExceptionMessage(e).contains("Unprocessed Continuation Reference(s)"))
logger.warn("Ignoring partial result exception, as we are not following referrals!");
else
throw e;
}
}
return searchResult;
}
protected User buildUserFromSearchResult(String username, SearchResult sr) throws Exception { protected User buildUserFromSearchResult(String username, SearchResult sr) throws Exception {
Attributes attrs = sr.getAttributes(); Attributes attrs = sr.getAttributes();
@ -135,19 +192,20 @@ public abstract class BaseLdapPrivilegeHandler extends DefaultPrivilegeHandler {
Set<String> ldapGroups = getLdapGroups(username, attrs); Set<String> ldapGroups = getLdapGroups(username, attrs);
logger.info("User " + username + " is member of the following LDAP groups: "); logger.info("User " + username + " is member of the following LDAP groups: ");
ldapGroups.forEach(s -> logger.info("- " + s)); ldapGroups.forEach(s -> logger.info("- " + s));
Set<String> strolchGroups = mapToStrolchGroups(username, ldapGroups);
Set<String> strolchRoles = mapToStrolchRoles(username, ldapGroups); Set<String> strolchRoles = mapToStrolchRoles(username, ldapGroups);
Map<String, String> properties = buildProperties(username, attrs, ldapGroups, strolchRoles); Map<String, String> properties = buildProperties(username, attrs, ldapGroups, strolchRoles);
return new User(username, username, null, firstName, lastName, UserState.REMOTE, strolchRoles, locale, return new User(username, username, null, firstName, lastName, UserState.REMOTE, strolchGroups, strolchRoles,
properties, false, new UserHistory()); locale, properties, false, UserHistory.EMPTY);
} }
protected abstract Map<String, String> buildProperties(String username, Attributes attrs, Set<String> ldapGroups, protected abstract Map<String, String> buildProperties(String username, Attributes attrs, Set<String> ldapGroups,
Set<String> strolchRoles) throws Exception; Set<String> strolchRoles) throws Exception;
protected String validateLdapUsername(String username, Attributes attrs) throws NamingException { protected String validateLdapUsername(String username, Attributes attrs) throws NamingException {
Attribute sAMAccountName = attrs.get("sAMAccountName"); Attribute sAMAccountName = attrs.get(SAM_ACCOUNT_NAME);
if (sAMAccountName == null || !username.equalsIgnoreCase(sAMAccountName.get().toString())) if (sAMAccountName == null || !username.equalsIgnoreCase(sAMAccountName.get().toString()))
throw new AccessDeniedException( throw new AccessDeniedException(
"Could not login with user: " + username + this.domain + " on Ldap: Wrong LDAP Data"); "Could not login with user: " + username + this.domain + " on Ldap: Wrong LDAP Data");
@ -168,5 +226,7 @@ public abstract class BaseLdapPrivilegeHandler extends DefaultPrivilegeHandler {
protected abstract Set<String> getLdapGroups(String username, Attributes attrs) throws NamingException; protected abstract Set<String> getLdapGroups(String username, Attributes attrs) throws NamingException;
protected abstract Set<String> mapToStrolchGroups(String username, Set<String> ldapGroups);
protected abstract Set<String> mapToStrolchRoles(String username, Set<String> ldapGroups); protected abstract Set<String> mapToStrolchRoles(String username, Set<String> ldapGroups);
} }

View File

@ -37,8 +37,8 @@ public class BasicPasswordStrengthHandler implements PasswordStrengthHandler {
String description; String description;
if (this.maxLength < 100) if (this.maxLength < 100)
description = MessageFormat description = MessageFormat.format(getString(locale, "Privilege.passwordLengthBetween"), this.minLength,
.format(getString(locale, "Privilege.passwordLengthBetween"), this.minLength, this.maxLength); this.maxLength);
else else
description = MessageFormat.format(getString(locale, "Privilege.passwordLengthAtLeast"), this.minLength); description = MessageFormat.format(getString(locale, "Privilege.passwordLengthAtLeast"), this.minLength);

View File

@ -44,7 +44,7 @@ import static li.strolch.privilege.helper.XmlConstants.*;
* <p> * <p>
* Required parameters: * Required parameters:
* <ul> * <ul>
* <li>{@link XmlConstants#XML_PARAM_HASH_ALGORITHM}</li> * <li>{@link XmlConstants#PARAM_HASH_ALGORITHM}</li>
* </ul> * </ul>
* *
* @author Robert von Burg <eitch@eitchnet.ch> * @author Robert von Burg <eitch@eitchnet.ch>
@ -121,7 +121,7 @@ public class DefaultEncryptionHandler implements EncryptionHandler {
try { try {
MessageDigest digest = MessageDigest.getInstance(this.nonSaltAlgorithm); MessageDigest digest = MessageDigest.getInstance(this.nonSaltAlgorithm);
return new PasswordCrypt(digest.digest(new String(password).getBytes()), null); return PasswordCrypt.of(digest.digest(new String(password).getBytes()), null);
} catch (NoSuchAlgorithmException e) { } catch (NoSuchAlgorithmException e) {
throw new PrivilegeException(MessageFormat.format("Algorithm {0} was not found!", nonSaltAlgorithm), throw new PrivilegeException(MessageFormat.format("Algorithm {0} was not found!", nonSaltAlgorithm),
@ -151,9 +151,8 @@ public class DefaultEncryptionHandler implements EncryptionHandler {
@Override @Override
public boolean isPasswordCryptOutdated(PasswordCrypt passwordCrypt) { public boolean isPasswordCryptOutdated(PasswordCrypt passwordCrypt) {
return passwordCrypt.getSalt() == null || passwordCrypt.getHashAlgorithm() == null || return passwordCrypt.salt() == null || passwordCrypt.hashAlgorithm() == null ||
passwordCrypt.getHashIterations() != this.iterations || passwordCrypt.hashIterations() != this.iterations || passwordCrypt.hashKeyLength() != this.keyLength;
passwordCrypt.getHashKeyLength() != this.keyLength;
} }
@Override @Override
@ -163,13 +162,13 @@ public class DefaultEncryptionHandler implements EncryptionHandler {
this.secureRandom = new SecureRandom(); this.secureRandom = new SecureRandom();
// get hash algorithm parameters // get hash algorithm parameters
this.algorithm = parameterMap.getOrDefault(XML_PARAM_HASH_ALGORITHM, DEFAULT_ALGORITHM); this.algorithm = parameterMap.getOrDefault(PARAM_HASH_ALGORITHM, DEFAULT_ALGORITHM);
this.nonSaltAlgorithm = parameterMap.getOrDefault(XML_PARAM_HASH_ALGORITHM_NON_SALT, this.nonSaltAlgorithm = parameterMap.getOrDefault(PARAM_HASH_ALGORITHM_NON_SALT,
DEFAULT_ALGORITHM_NON_SALT); DEFAULT_ALGORITHM_NON_SALT);
this.iterations = Integer.parseInt( this.iterations = Integer.parseInt(
parameterMap.getOrDefault(XML_PARAM_HASH_ITERATIONS, valueOf(DEFAULT_ITERATIONS))); parameterMap.getOrDefault(PARAM_HASH_ITERATIONS, valueOf(DEFAULT_ITERATIONS)));
this.keyLength = Integer.parseInt( this.keyLength = Integer.parseInt(
parameterMap.getOrDefault(XML_PARAM_HASH_KEY_LENGTH, valueOf(DEFAULT_KEY_LENGTH))); parameterMap.getOrDefault(PARAM_HASH_KEY_LENGTH, valueOf(DEFAULT_KEY_LENGTH)));
// test non-salt hash algorithm // test non-salt hash algorithm
try { try {
@ -178,7 +177,7 @@ public class DefaultEncryptionHandler implements EncryptionHandler {
MessageFormat.format("Using non-salt hashing algorithm {0}", this.nonSaltAlgorithm)); MessageFormat.format("Using non-salt hashing algorithm {0}", this.nonSaltAlgorithm));
} catch (Exception e) { } catch (Exception e) {
String msg = "[{0}] Defined parameter {1} is invalid because of underlying exception: {2}"; String msg = "[{0}] Defined parameter {1} is invalid because of underlying exception: {2}";
msg = MessageFormat.format(msg, EncryptionHandler.class.getName(), XML_PARAM_HASH_ALGORITHM_NON_SALT, msg = MessageFormat.format(msg, EncryptionHandler.class.getName(), PARAM_HASH_ALGORITHM_NON_SALT,
e.getLocalizedMessage()); e.getLocalizedMessage());
throw new PrivilegeException(msg, e); throw new PrivilegeException(msg, e);
} }
@ -189,7 +188,7 @@ public class DefaultEncryptionHandler implements EncryptionHandler {
DefaultEncryptionHandler.logger.info(MessageFormat.format("Using hashing algorithm {0}", this.algorithm)); DefaultEncryptionHandler.logger.info(MessageFormat.format("Using hashing algorithm {0}", this.algorithm));
} catch (Exception e) { } catch (Exception e) {
String msg = "[{0}] Defined parameter {1} is invalid because of underlying exception: {2}"; String msg = "[{0}] Defined parameter {1} is invalid because of underlying exception: {2}";
msg = MessageFormat.format(msg, EncryptionHandler.class.getName(), XML_PARAM_HASH_ALGORITHM, msg = MessageFormat.format(msg, EncryptionHandler.class.getName(), PARAM_HASH_ALGORITHM,
e.getLocalizedMessage()); e.getLocalizedMessage());
throw new PrivilegeException(msg, e); throw new PrivilegeException(msg, e);
} }

View File

@ -1,10 +1,11 @@
package li.strolch.privilege.handler; package li.strolch.privilege.handler;
import static java.lang.String.join; import com.google.gson.JsonElement;
import static java.util.stream.Collectors.toSet; import com.google.gson.JsonObject;
import static li.strolch.privilege.base.PrivilegeConstants.*; import com.google.gson.JsonParser;
import static li.strolch.utils.helper.StringHelper.isEmpty; import li.strolch.privilege.helper.LdapHelper;
import static li.strolch.utils.helper.StringHelper.isNotEmpty; import li.strolch.privilege.policy.PrivilegePolicy;
import li.strolch.utils.dbc.DBC;
import javax.naming.NamingException; import javax.naming.NamingException;
import javax.naming.directory.Attributes; import javax.naming.directory.Attributes;
@ -13,12 +14,11 @@ import java.io.FileReader;
import java.util.*; import java.util.*;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import com.google.gson.JsonElement; import static java.lang.String.join;
import com.google.gson.JsonObject; import static java.util.stream.Collectors.toSet;
import com.google.gson.JsonParser; import static li.strolch.privilege.base.PrivilegeConstants.*;
import li.strolch.privilege.helper.LdapHelper; import static li.strolch.utils.helper.StringHelper.isEmpty;
import li.strolch.privilege.policy.PrivilegePolicy; import static li.strolch.utils.helper.StringHelper.isNotEmpty;
import li.strolch.utils.dbc.DBC;
public class JsonConfigLdapPrivilegeHandler extends BaseLdapPrivilegeHandler { public class JsonConfigLdapPrivilegeHandler extends BaseLdapPrivilegeHandler {
@ -42,15 +42,14 @@ public class JsonConfigLdapPrivilegeHandler extends BaseLdapPrivilegeHandler {
DBC.PRE.assertNotEmpty("realm must be set!", realm); DBC.PRE.assertNotEmpty("realm must be set!", realm);
this.defaultLocale = parameterMap.containsKey("defaultLocale") ? this.defaultLocale = parameterMap.containsKey("defaultLocale") ?
Locale.forLanguageTag(parameterMap.get("defaultLocale")) : Locale.forLanguageTag(parameterMap.get("defaultLocale")) : Locale.getDefault();
Locale.getDefault();
String configFileS = parameterMap.get("configFile"); String configFileS = parameterMap.get("configFile");
DBC.PRE.assertNotEmpty("configFile param must be set!", configFileS); DBC.PRE.assertNotEmpty("configFile param must be set!", configFileS);
File configFile = new File(configFileS); File configFile = new File(configFileS);
if (!configFile.exists() || !configFile.isFile() || !configFile.canRead()) 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 " throw new IllegalStateException("configFile does not exist, is not a file, or can not be read at path " +
+ configFile.getAbsolutePath()); configFile.getAbsolutePath());
// parse the configuration file // parse the configuration file
JsonObject configJ; JsonObject configJ;
@ -82,14 +81,14 @@ public class JsonConfigLdapPrivilegeHandler extends BaseLdapPrivilegeHandler {
// validate the configuration // validate the configuration
for (String name : this.ldapGroupNames) { for (String name : this.ldapGroupNames) {
JsonObject config = ldapGroupConfigs.get(name).getAsJsonObject(); JsonObject config = ldapGroupConfigs.get(name).getAsJsonObject();
if (!config.has(LOCATION) || !config.get(LOCATION).isJsonArray() if (!config.has(LOCATION) || !config.get(LOCATION).isJsonArray() ||
|| config.get(LOCATION).getAsJsonArray().size() == 0) config.get(LOCATION).getAsJsonArray().isEmpty())
throw new IllegalStateException("LDAP Group " + name throw new IllegalStateException("LDAP Group " + name +
+ " is missing a location attribute, or it is not an array or the array is empty"); " is missing a location attribute, or it is not an array or the array is empty");
if (!config.has(LOCATION) || !config.get(LOCATION).isJsonArray() if (!config.has(LOCATION) || !config.get(LOCATION).isJsonArray() ||
|| config.get(LOCATION).getAsJsonArray().size() == 0) config.get(LOCATION).getAsJsonArray().isEmpty())
throw new IllegalStateException("LDAP Group " + name throw new IllegalStateException("LDAP Group " + name +
+ " is missing a roles attribute, or it is not an array or the array is empty"); " is missing a roles attribute, or it is not an array or the array is empty");
} }
this.userLdapGroupOverrides = new HashMap<>(); this.userLdapGroupOverrides = new HashMap<>();
@ -133,33 +132,40 @@ public class JsonConfigLdapPrivilegeHandler extends BaseLdapPrivilegeHandler {
logger.info("Overriding LDAP group for user " + username + " to " + overrideGroup); logger.info("Overriding LDAP group for user " + username + " to " + overrideGroup);
} }
Set<String> relevantLdapGroups = ldapGroups.stream() Set<String> relevantLdapGroups = ldapGroups.stream().filter(s -> this.ldapGroupNames.contains(s))
.filter(s -> this.ldapGroupNames.contains(s))
.collect(toSet()); .collect(toSet());
if (relevantLdapGroups.isEmpty()) if (relevantLdapGroups.isEmpty())
throw new IllegalStateException("User " + username throw new IllegalStateException("User " + username +
+ " can not login, as none of their LDAP Groups have mappings to Strolch Roles!"); " can not login, as none of their LDAP Groups have mappings to Strolch Roles!");
if (relevantLdapGroups.size() > 1) { if (relevantLdapGroups.size() > 1) {
logger.warn( logger.warn(
"User " + username + " has multiple relevant LDAP Groups which will lead to undefined behaviour: " "User " + username + " has multiple relevant LDAP Groups which will lead to undefined behaviour: " +
+ join(",", relevantLdapGroups)); join(",", relevantLdapGroups));
} }
return relevantLdapGroups; return relevantLdapGroups;
} }
@Override
protected Set<String> mapToStrolchGroups(String username, Set<String> ldapGroups) {
return mapLdapGroupToStrolch(ldapGroups, GROUPS);
}
@Override @Override
protected Set<String> mapToStrolchRoles(String username, Set<String> ldapGroups) { protected Set<String> mapToStrolchRoles(String username, Set<String> ldapGroups) {
return mapLdapGroupToStrolch(ldapGroups, ROLES);
}
Set<String> strolchRoles = new HashSet<>(); private Set<String> mapLdapGroupToStrolch(Set<String> ldapGroups, String type) {
Set<String> mappedValues = new HashSet<>();
for (String relevantLdapGroup : ldapGroups) { for (String relevantLdapGroup : ldapGroups) {
JsonObject mappingJ = this.ldapGroupConfigs.get(relevantLdapGroup).getAsJsonObject(); JsonObject mappingJ = this.ldapGroupConfigs.get(relevantLdapGroup).getAsJsonObject();
mappingJ.get(ROLES).getAsJsonArray().forEach(e -> strolchRoles.add(e.getAsString())); if (mappingJ.has(type))
mappingJ.get(type).getAsJsonArray().forEach(e -> mappedValues.add(e.getAsString()));
} }
return strolchRoles; return mappedValues;
} }
@Override @Override
@ -194,9 +200,8 @@ public class JsonConfigLdapPrivilegeHandler extends BaseLdapPrivilegeHandler {
} else { } else {
String location = primaryLocationJ.getAsString(); String location = primaryLocationJ.getAsString();
if (!secondaryLocations.contains(location)) { if (!secondaryLocations.contains(location)) {
logger.warn( logger.warn("Primary location already set by previous LDAP Group config for LDAP Group " +
"Primary location already set by previous LDAP Group config for LDAP Group " + ldapGroup ldapGroup + ", adding to secondary locations.");
+ ", adding to secondary locations.");
secondaryLocations.add(location); secondaryLocations.add(location);
} }
} }
@ -210,9 +215,8 @@ public class JsonConfigLdapPrivilegeHandler extends BaseLdapPrivilegeHandler {
else else
secondaryLocationsJ.getAsJsonArray().forEach(s -> secondaryLocations.add(s.getAsString())); secondaryLocationsJ.getAsJsonArray().forEach(s -> secondaryLocations.add(s.getAsString()));
} else { } else {
logger.warn( logger.warn("Secondary locations already set by previous LDAP Group config for LDAP Group " +
"Secondary locations already set by previous LDAP Group config for LDAP Group " + ldapGroup ldapGroup + ", adding additional");
+ ", adding additional");
if (secondaryLocationsJ.isJsonPrimitive()) if (secondaryLocationsJ.isJsonPrimitive())
secondaryLocations.add(secondaryLocationsJ.getAsString()); secondaryLocations.add(secondaryLocationsJ.getAsString());
else else

View File

@ -16,9 +16,9 @@ public class MailUserChallengeHandler extends UserChallengeHandler {
String subject = "Mail TAN"; String subject = "Mail TAN";
String text = "Hello " + user.getFirstname() + " " + user.getLastname() + "\n\n" String text = "Hello " + user.getFirstname() + " " + user.getLastname() + "\n\n" +
+ "You have requested an action which requires you to respond to a challenge.\n\n" "You have requested an action which requires you to respond to a challenge.\n\n" +
+ "Please use the following code to response to the challenge:\n\n" + challenge; "Please use the following code to response to the challenge:\n\n" + challenge;
String recipient = user.getEmail(); String recipient = user.getEmail();
if (StringHelper.isEmpty(recipient)) { if (StringHelper.isEmpty(recipient)) {
String msg = "User {0} has no property {1}, so can not initiate challenge!"; String msg = "User {0} has no property {1}, so can not initiate challenge!";

View File

@ -12,8 +12,7 @@ public interface PasswordStrengthHandler {
* Initialize the concrete {@link PasswordStrengthHandler}. The passed parameter map contains any configuration the * Initialize the concrete {@link PasswordStrengthHandler}. The passed parameter map contains any configuration the
* concrete {@link PasswordStrengthHandler} might need * concrete {@link PasswordStrengthHandler} might need
* *
* @param parameterMap * @param parameterMap a map containing configuration properties
* a map containing configuration properties
*/ */
void initialize(Map<String, String> parameterMap); void initialize(Map<String, String> parameterMap);
@ -21,16 +20,16 @@ public interface PasswordStrengthHandler {
* Returns a description what a password must contain in order to be regarded as strong for this concrete * Returns a description what a password must contain in order to be regarded as strong for this concrete
* implementation * implementation
* *
* @param locale the locale in which to return the description
*
* @return a description of a strong password * @return a description of a strong password
* @param locale
*/ */
String getDescription(Locale locale); String getDescription(Locale locale);
/** /**
* Performs the validation of the given password * Performs the validation of the given password
* *
* @param password * @param password the password to validate
* the password to validate
* *
* @return true if the password meets the criteria for a strong password * @return true if the password meets the criteria for a strong password
*/ */

View File

@ -15,15 +15,18 @@
*/ */
package li.strolch.privilege.handler; package li.strolch.privilege.handler;
import java.util.List; import li.strolch.privilege.model.Privilege;
import java.util.Map;
import li.strolch.privilege.model.IPrivilege;
import li.strolch.privilege.model.Restrictable; import li.strolch.privilege.model.Restrictable;
import li.strolch.privilege.model.internal.Group;
import li.strolch.privilege.model.internal.Role; import li.strolch.privilege.model.internal.Role;
import li.strolch.privilege.model.internal.User; import li.strolch.privilege.model.internal.User;
import li.strolch.privilege.policy.PrivilegePolicy; import li.strolch.privilege.policy.PrivilegePolicy;
import javax.xml.stream.XMLStreamException;
import java.io.IOException;
import java.util.List;
import java.util.Map;
/** /**
* <p> * <p>
* The {@link PersistenceHandler} takes care of retrieving and persisting model objects to the underlying database. This * The {@link PersistenceHandler} takes care of retrieving and persisting model objects to the underlying database. This
@ -33,7 +36,7 @@ import li.strolch.privilege.policy.PrivilegePolicy;
* <p> * <p>
* The {@link PersistenceHandler} also serves the special {@link PrivilegePolicy} objects. These policies are special * The {@link PersistenceHandler} also serves the special {@link PrivilegePolicy} objects. These policies are special
* objects which implement an algorithm to define if an action is allowed on a {@link Restrictable} by a {@link Role} * objects which implement an algorithm to define if an action is allowed on a {@link Restrictable} by a {@link Role}
* and {@link IPrivilege} * and {@link Privilege}
* </p> * </p>
* *
* @author Robert von Burg <eitch@eitchnet.ch> * @author Robert von Burg <eitch@eitchnet.ch>
@ -47,6 +50,13 @@ public interface PersistenceHandler {
*/ */
List<User> getAllUsers(); List<User> getAllUsers();
/**
* Returns all currently known {@link Group}s
*
* @return all currently known {@link Group}s
*/
List<Group> getAllGroups();
/** /**
* Returns all currently known {@link Role}s * Returns all currently known {@link Role}s
* *
@ -57,18 +67,25 @@ public interface PersistenceHandler {
/** /**
* Returns a {@link User} object from the underlying database * Returns a {@link User} object from the underlying database
* *
* @param username * @param username the name/id of the {@link User} object to return
* the name/id of the {@link User} object to return
* *
* @return the {@link User} object, or null if it was not found * @return the {@link User} object, or null if it was not found
*/ */
User getUser(String username); User getUser(String username);
/**
* Returns a {@link Group} object from the underlying database
*
* @param groupName the name/id of the {@link Group} object to return
*
* @return the {@link Group} object, or null if it was not found
*/
Group getGroup(String groupName);
/** /**
* Returns a {@link Role} object from the underlying database * Returns a {@link Role} object from the underlying database
* *
* @param roleName * @param roleName the name/id of the {@link Role} object to return
* the name/id of the {@link Role} object to return
* *
* @return the {@link Role} object, or null if it was not found * @return the {@link Role} object, or null if it was not found
*/ */
@ -77,18 +94,25 @@ public interface PersistenceHandler {
/** /**
* Removes a {@link User} with the given name and returns the removed object if it existed * Removes a {@link User} with the given name and returns the removed object if it existed
* *
* @param username * @param username the name of the {@link User} to remove
* the name of the {@link User} to remove
* *
* @return the {@link User} removed, or null if it did not exist * @return the {@link User} removed, or null if it did not exist
*/ */
User removeUser(String username); User removeUser(String username);
/**
* Removes a {@link Group} with the given name and returns the removed object if it existed
*
* @param groupName the name of the {@link Group} to remove
*
* @return the {@link Group} removed, or null if it did not exist
*/
Group removeGroup(String groupName);
/** /**
* Removes a {@link Role} with the given name and returns the removed object if it existed * Removes a {@link Role} with the given name and returns the removed object if it existed
* *
* @param roleName * @param roleName the name of the {@link Role} to remove
* the name of the {@link Role} to remove
* *
* @return the {@link Role} removed, or null if it did not exist * @return the {@link Role} removed, or null if it did not exist
*/ */
@ -97,41 +121,51 @@ public interface PersistenceHandler {
/** /**
* Adds a {@link User} object to the underlying database * Adds a {@link User} object to the underlying database
* *
* @param user * @param user the {@link User} object to add
* the {@link User} object to add
*/ */
void addUser(User user); void addUser(User user);
/** /**
* Replaces the existing {@link User} object in the underlying database * Replaces the existing {@link User} object in the underlying database
* *
* @param user * @param user the {@link User} object to add
* the {@link User} object to add
*/ */
void replaceUser(User user); void replaceUser(User user);
/** /**
* Adds a {@link Role} object to the underlying database * Adds a {@link Role} object to the underlying database
* *
* @param role * @param role the {@link Role} object to add
* the {@link User} object to add
*/ */
void addRole(Role role); void addRole(Role role);
/** /**
* Replaces the {@link Role} object in the underlying database * Replaces the {@link Role} object in the underlying database
* *
* @param role * @param role the {@link User} object to add
* the {@link User} object to add
*/ */
void replaceRole(Role role); void replaceRole(Role role);
/**
* Adds a {@link Group} object to the underlying database
*
* @param group the {@link Group} object to add
*/
void addGroup(Group group);
/**
* Replaces the {@link Group} object in the underlying database
*
* @param group the {@link Group} object to add
*/
void replaceGroup(Group group);
/** /**
* Informs this {@link PersistenceHandler} to persist any changes which need to be saved * Informs this {@link PersistenceHandler} to persist any changes which need to be saved
* *
* @return true if changes were persisted successfully, false if nothing needed to be persisted * @return true if changes were persisted successfully, false if nothing needed to be persisted
*/ */
boolean persist(); boolean persist() throws XMLStreamException, IOException;
/** /**
* Informs this {@link PersistenceHandler} to reload the data from the backend * Informs this {@link PersistenceHandler} to reload the data from the backend
@ -144,8 +178,7 @@ public interface PersistenceHandler {
* Initialize the concrete {@link PersistenceHandler}. The passed parameter map contains any configuration the * Initialize the concrete {@link PersistenceHandler}. The passed parameter map contains any configuration the
* concrete {@link PersistenceHandler} might need * concrete {@link PersistenceHandler} might need
* *
* @param parameterMap * @param parameterMap a map containing configuration properties
* a map containing configuration properties
*/ */
void initialize(Map<String, String> parameterMap); void initialize(Map<String, String> parameterMap);

View File

@ -0,0 +1,204 @@
package li.strolch.privilege.handler;
import li.strolch.privilege.base.PrivilegeConflictResolution;
import li.strolch.privilege.base.PrivilegeException;
import li.strolch.privilege.base.PrivilegeModelException;
import li.strolch.privilege.model.*;
import li.strolch.privilege.model.internal.Group;
import li.strolch.privilege.model.internal.Role;
import li.strolch.privilege.model.internal.User;
import li.strolch.privilege.policy.PrivilegePolicy;
import li.strolch.utils.dbc.DBC;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.time.ZonedDateTime;
import java.util.*;
import static java.text.MessageFormat.format;
import static java.util.stream.Collectors.toCollection;
import static li.strolch.privilege.handler.DefaultPrivilegeHandler.streamAllRolesForUser;
public class PrivilegeContextBuilder {
private static final Logger logger = LoggerFactory.getLogger(PrivilegeContextBuilder.class);
private final Map<String, Class<PrivilegePolicy>> policyMap;
private final DefaultPrivilegeHandler privilegeHandler;
private final PrivilegeConflictResolution conflictResolution;
private final PersistenceHandler persistenceHandler;
private Set<String> userGroups;
private Set<String> userRoles;
private Map<String, String> userProperties;
public PrivilegeContextBuilder(DefaultPrivilegeHandler privilegeHandler) {
this.privilegeHandler = privilegeHandler;
this.persistenceHandler = privilegeHandler.persistenceHandler;
this.policyMap = privilegeHandler.policyMap;
this.conflictResolution = privilegeHandler.privilegeConflictResolution;
}
public PrivilegeContext buildPrivilegeContext(Usage usage, User user, String source, ZonedDateTime loginTime,
boolean keepAlive) {
String authToken = this.privilegeHandler.getEncryptionHandler().nextToken();
String sessionId = UUID.randomUUID().toString();
return buildPrivilegeContext(usage, user, authToken, sessionId, source, loginTime, keepAlive);
}
public PrivilegeContext buildPrivilegeContext(Usage usage, User user, String authToken, String sessionId,
String source, ZonedDateTime loginTime, boolean keepAlive) {
DBC.PRE.assertNotEmpty("source must not be empty!", source);
keepAlive = keepAlive && this.privilegeHandler.allowSessionRefresh;
prepare(user);
Map<String, Privilege> privileges = new HashMap<>();
Map<String, PrivilegePolicy> policies = new HashMap<>();
// cache the privileges and policies for this user by role
addPrivilegesForRoles(this.userRoles, user.getUsername(), privileges, policies);
UserRep userRep = user.asUserRep();
userRep.setRoles(this.userRoles);
userRep.setGroups(this.userGroups);
userRep.setProperties(this.userProperties);
userRep.readOnly();
Certificate certificate = new Certificate(usage, sessionId, user.getUsername(), user.getFirstname(),
user.getLastname(), user.getUserState(), authToken, source, loginTime, keepAlive, user.getLocale(),
this.userGroups, this.userRoles, this.userProperties);
return new PrivilegeContext(userRep, certificate, privileges, policies);
}
private void prepare(User user) {
this.userGroups = user.getGroups().stream().sorted().collect(toCollection(TreeSet::new));
this.userRoles = streamAllRolesForUser(this.persistenceHandler, user).sorted()
.collect(toCollection(TreeSet::new));
this.userProperties = new HashMap<>(user.getProperties());
// copy properties from groups to user properties
for (String groupName : this.userGroups) {
Group group = this.persistenceHandler.getGroup(groupName);
if (group == null) {
logger.error("Group " + groupName + " does not exist!");
continue;
}
Map<String, String> groupProperties = group.getProperties();
for (String key : groupProperties.keySet()) {
String replaced = this.userProperties.put(key, groupProperties.get(key));
if (replaced != null)
logger.error("Duplicate property {} for user {} from group {}", key, user.getUsername(), groupName);
}
}
}
private void addPrivilegesForRoles(Set<String> roles, String userName, Map<String, Privilege> privileges,
Map<String, PrivilegePolicy> policies) {
for (String roleName : roles) {
Role role = this.persistenceHandler.getRole(roleName);
if (role == null) {
logger.error("Role " + roleName + " does not exist for user " + userName);
} else {
addPrivilegesForRole(userName, role, privileges, policies);
}
}
}
private void addPrivilegesForRole(String userName, Role role, Map<String, Privilege> privileges,
Map<String, PrivilegePolicy> policies) {
for (Privilege privilege : role.privilegeMap().values()) {
String privilegeName = privilege.name();
if (!privileges.containsKey(privilegeName)) {
privileges.put(privilegeName, privilege);
} else {
handleDuplicatePrivilege(userName, role, privileges, privilege, privilegeName);
}
// cache the policy for the privilege
addPolicyForPrivilege(policies, privilege, privilegeName);
}
}
private void addPolicyForPrivilege(Map<String, PrivilegePolicy> policies, Privilege privilege,
String privilegeName) {
String policyName = privilege.getPolicy();
if (policies.containsKey(policyName))
return;
PrivilegePolicy policy = getPolicy(policyName);
if (policy == null) {
logger.error(format("The Policy {0} does not exist for Privilege {1}", policyName, privilegeName));
} else {
policies.put(policyName, policy);
}
}
private void handleDuplicatePrivilege(String userName, Role role, Map<String, Privilege> privileges,
Privilege additionalPrivilege, String privilegeName) {
// for strict, we have to throw an exception
if (this.conflictResolution.isStrict())
throw new PrivilegeModelException(
format("User " + userName + " has conflicts for privilege {0} with role {1}", privilegeName,
role.name()));
// merge privileges
Privilege knownPrivilege = privileges.get(privilegeName);
boolean allAllowed = knownPrivilege.isAllAllowed() || additionalPrivilege.isAllAllowed();
Set<String> allowList;
Set<String> denyList;
if (allAllowed) {
allowList = Set.of();
denyList = Set.of();
} else {
allowList = new HashSet<>(knownPrivilege.getAllowList());
allowList.addAll(additionalPrivilege.getAllowList());
denyList = new HashSet<>(knownPrivilege.getDenyList());
denyList.addAll(additionalPrivilege.getDenyList());
}
String policy = knownPrivilege.getPolicy();
privileges.put(privilegeName, new Privilege(knownPrivilege.getName(), policy, allAllowed, denyList, allowList));
}
/**
* <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}";
msg = format(msg, policyName, policyName);
throw new PrivilegeModelException(msg, e);
}
return policy;
}
}

View File

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

View File

@ -15,16 +15,16 @@
*/ */
package li.strolch.privilege.handler; package li.strolch.privilege.handler;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import li.strolch.privilege.base.*; import li.strolch.privilege.base.*;
import li.strolch.privilege.model.*; import li.strolch.privilege.model.*;
import li.strolch.privilege.model.internal.Role; import li.strolch.privilege.model.internal.Role;
import li.strolch.privilege.model.internal.User; import li.strolch.privilege.model.internal.User;
import li.strolch.privilege.policy.PrivilegePolicy; import li.strolch.privilege.policy.PrivilegePolicy;
import java.util.List;
import java.util.Locale;
import java.util.Map;
/** /**
* The {@link PrivilegeHandler} is the centrally exposed API for accessing the privilege library. It exposes all needed * The {@link PrivilegeHandler} is the centrally exposed API for accessing the privilege library. It exposes all needed
* methods to access Privilege data model objects, modify them and validate if users or roles have privileges to perform * methods to access Privilege data model objects, modify them and validate if users or roles have privileges to perform
@ -212,10 +212,8 @@ public interface PrivilegeHandler {
/** /**
* Returns a {@link UserRep} for the given username * Returns a {@link UserRep} for the given username
* *
* @param certificate * @param certificate the {@link Certificate} of the user which has the privilege to perform this action
* the {@link Certificate} of the user which has the privilege to perform this action * @param username the name of the {@link UserRep} to return
* @param username
* the name of the {@link UserRep} to return
* *
* @return the {@link UserRep} for the given username, or null if it was not found * @return the {@link UserRep} for the given username, or null if it was not found
*/ */
@ -224,10 +222,8 @@ public interface PrivilegeHandler {
/** /**
* Returns a {@link RoleRep} for the given roleName * Returns a {@link RoleRep} for the given roleName
* *
* @param certificate * @param certificate the {@link Certificate} of the user which has the privilege to perform this action
* the {@link Certificate} of the user which has the privilege to perform this action * @param roleName the name of the {@link RoleRep} to return
* @param roleName
* the name of the {@link RoleRep} to return
* *
* @return the {@link RoleRep} for the given roleName, or null if it was not found * @return the {@link RoleRep} for the given roleName, or null if it was not found
*/ */
@ -236,8 +232,7 @@ public interface PrivilegeHandler {
/** /**
* Returns the map of {@link PrivilegePolicy} definitions * Returns the map of {@link PrivilegePolicy} definitions
* *
* @param certificate * @param certificate the {@link Certificate} of the user which has the privilege to perform this action
* the {@link Certificate} of the user which has the privilege to perform this action
* *
* @return the map of {@link PrivilegePolicy} definitions * @return the map of {@link PrivilegePolicy} definitions
*/ */
@ -246,8 +241,7 @@ public interface PrivilegeHandler {
/** /**
* Returns the list of {@link Certificate Certificates} * Returns the list of {@link Certificate Certificates}
* *
* @param certificate * @param certificate the {@link Certificate} of the user which has the privilege to perform this action
* the {@link Certificate} of the user which has the privilege to perform this action
* *
* @return the list of {@link Certificate Certificates} * @return the list of {@link Certificate Certificates}
*/ */
@ -256,8 +250,7 @@ public interface PrivilegeHandler {
/** /**
* Returns all {@link RoleRep RoleReps} * Returns all {@link RoleRep RoleReps}
* *
* @param certificate * @param certificate the {@link Certificate} of the user which has the privilege to perform this action
* the {@link Certificate} of the user which has the privilege to perform this action
* *
* @return the list of {@link RoleRep RoleReps} * @return the list of {@link RoleRep RoleReps}
*/ */
@ -266,8 +259,7 @@ public interface PrivilegeHandler {
/** /**
* Returns all {@link UserRep UserReps} * Returns all {@link UserRep UserReps}
* *
* @param certificate * @param certificate the {@link Certificate} of the user which has the privilege to perform this action
* the {@link Certificate} of the user which has the privilege to perform this action
* *
* @return the list of {@link UserRep UserReps} * @return the list of {@link UserRep UserReps}
*/ */
@ -277,10 +269,8 @@ public interface PrivilegeHandler {
* Method to query {@link UserRep} which meet the criteria set in the given {@link UserRep}. Null fields mean the * Method to query {@link UserRep} which meet the criteria set in the given {@link UserRep}. Null fields mean the
* fields are irrelevant. * fields are irrelevant.
* *
* @param certificate * @param certificate the {@link Certificate} of the user which has the privilege to perform this action
* the {@link Certificate} of the user which has the privilege to perform this action * @param selectorRep the {@link UserRep} to use as criteria selection
* @param selectorRep
* the {@link UserRep} to use as criteria selection
* *
* @return a list of {@link UserRep}s which fit the given criteria * @return a list of {@link UserRep}s which fit the given criteria
*/ */
@ -289,72 +279,30 @@ public interface PrivilegeHandler {
/** /**
* Removes the user with the given username * Removes the user with the given username
* *
* @param certificate * @param certificate the {@link Certificate} of the user which has the privilege to perform this action
* the {@link Certificate} of the user which has the privilege to perform this action * @param username the username of the user to remove
* @param username
* the username of the user to remove
* *
* @return the {@link UserRep} of the user removed, or null if the user did not exist * @return the {@link UserRep} of the user removed, or null if the user did not exist
* *
* @throws AccessDeniedException * @throws AccessDeniedException if the user for this certificate may not perform the action
* if the user for this certificate may not perform the action * @throws PrivilegeException if there is anything wrong with this certificate
* @throws PrivilegeException
* if there is anything wrong with this certificate
*/ */
UserRep removeUser(Certificate certificate, String username) throws PrivilegeException; UserRep removeUser(Certificate certificate, String username) throws PrivilegeException;
/**
* Removes the role with the given roleName from the user with the given username
*
* @param certificate
* the {@link Certificate} of the user which has the privilege to perform this action
* @param username
* the username of the user from which the role is to be removed
* @param roleName
* the roleName of the role to remove from the user
*
* @throws AccessDeniedException
* if the user for this certificate may not perform the action
* @throws PrivilegeException
* if there is anything wrong with this certificate
*/
UserRep removeRoleFromUser(Certificate certificate, String username, String roleName) throws PrivilegeException;
/** /**
* Removes the role with the given roleName * Removes the role with the given roleName
* *
* @param certificate * @param certificate the {@link Certificate} of the user which has the privilege to perform this action
* the {@link Certificate} of the user which has the privilege to perform this action * @param roleName the roleName of the role to remove
* @param roleName
* the roleName of the role to remove
* *
* @return the {@link RoleRep} of the role removed, or null if the role did not exist * @return the {@link RoleRep} of the role removed, or null if the role did not exist
* *
* @throws AccessDeniedException * @throws AccessDeniedException if the user for this certificate may not perform the action
* if the user for this certificate may not perform the action * @throws PrivilegeException if there is anything wrong with this certificate or the role is still in use by a
* @throws PrivilegeException * user
* if there is anything wrong with this certificate or the role is still in use by a user
*/ */
RoleRep removeRole(Certificate certificate, String roleName) throws PrivilegeException; RoleRep removeRole(Certificate certificate, String roleName) throws PrivilegeException;
/**
* Removes the privilege with the given privilegeName from the role with the given roleName
*
* @param certificate
* the {@link Certificate} of the user which has the privilege to perform this action
* @param roleName
* the roleName of the role from which the privilege is to be removed
* @param privilegeName
* the privilegeName of the privilege to remove from the role
*
* @throws AccessDeniedException
* if the user for this certificate may not perform the action
* @throws PrivilegeException
* if there is anything wrong with this certificate
*/
RoleRep removePrivilegeFromRole(Certificate certificate, String roleName, String privilegeName)
throws PrivilegeException;
/** /**
* <p> * <p>
* Adds a new user with the information from this {@link UserRep} * Adds a new user with the information from this {@link UserRep}
@ -365,18 +313,14 @@ public interface PrivilegeHandler {
* the requirements of the implementation under {@link PrivilegeHandler#validatePassword(Locale, char[])} * the requirements of the implementation under {@link PrivilegeHandler#validatePassword(Locale, char[])}
* </p> * </p>
* *
* @param certificate * @param certificate the {@link Certificate} of the user which has the privilege to perform this action
* the {@link Certificate} of the user which has the privilege to perform this action * @param userRep the {@link UserRep} containing the information to create the new {@link User}
* @param userRep * @param password the password of the new user. If the password is null, then this is accepted but the user can
* the {@link UserRep} containing the information to create the new {@link User} * not login, otherwise the password must be validated against
* @param password * {@link PrivilegeHandler#validatePassword(Locale, char[])}
* the password of the new user. If the password is null, then this is accepted but the user can not login,
* otherwise the password must be validated against {@link PrivilegeHandler#validatePassword(Locale, char[])}
* *
* @throws AccessDeniedException * @throws AccessDeniedException if the user for this certificate may not perform the action
* if the user for this certificate may not perform the action * @throws PrivilegeException if there is anything wrong with this certificate or the user already exists
* @throws PrivilegeException
* if there is anything wrong with this certificate or the user already exists
*/ */
UserRep addUser(Certificate certificate, UserRep userRep, char[] password) throws PrivilegeException; UserRep addUser(Certificate certificate, UserRep userRep, char[] password) throws PrivilegeException;
@ -384,10 +328,8 @@ public interface PrivilegeHandler {
* Allows the bulk adding or updating of users. If the user exists, the user's history and password is kept, * Allows the bulk adding or updating of users. If the user exists, the user's history and password is kept,
* otherwise the user is created without a password * otherwise the user is created without a password
* *
* @param certificate * @param certificate the {@link Certificate} of the user which has the privilege to perform this action
* the {@link Certificate} of the user which has the privilege to perform this action * @param userReps the list of users to add or update
* @param userReps
* the list of users to add or update
*/ */
void addOrUpdateUsers(Certificate certificate, List<UserRep> userReps) throws PrivilegeException; void addOrUpdateUsers(Certificate certificate, List<UserRep> userReps) throws PrivilegeException;
@ -410,108 +352,39 @@ public interface PrivilegeHandler {
* Any other fields will be ignored * Any other fields will be ignored
* </p> * </p>
* *
* @param certificate * @param certificate the {@link Certificate} of the user which has the privilege to perform this action
* the {@link Certificate} of the user which has the privilege to perform this action * @param userRep the {@link UserRep} with the fields set to their new values
* @param userRep * @param password the password of the new user. If the password is null, then this is accepted but the user can
* the {@link UserRep} with the fields set to their new values * not login, otherwise the password must be validated against
* {@link PrivilegeHandler#validatePassword(Locale, char[])}
* *
* @throws AccessDeniedException * @throws AccessDeniedException if the user for this certificate may not perform the action
* if the user for this certificate may not perform the action * @throws PrivilegeException if there is anything wrong with this certificate or if the user does not exist
* @throws PrivilegeException
* if there is anything wrong with this certificate or if the user does not exist
*/ */
UserRep updateUser(Certificate certificate, UserRep userRep) throws PrivilegeException; UserRep updateUser(Certificate certificate, UserRep userRep, char[] password) throws PrivilegeException;
/**
* <p>
* Replaces the existing user with the information from this {@link UserRep} if the user already exists
* </p>
*
* <p>
* If the password given is null, then the user is created, but can not not login! Otherwise the password must meet
* the requirements of the implementation under {@link PrivilegeHandler#validatePassword(Locale, char[])}
* </p>
*
* @param certificate
* the {@link Certificate} of the user which has the privilege to perform this action
* @param userRep
* the {@link UserRep} containing the information to replace the existing {@link User}
* @param password
* the password of the new user. If the password is null, then this is accepted but the user can not login,
* otherwise the password must be validated against {@link PrivilegeHandler#validatePassword(Locale, char[])}
*
* @throws AccessDeniedException
* if the user for this certificate may not perform the action
* @throws PrivilegeException
* if there is anything wrong with this certificate or if the user does not exist
*/
UserRep replaceUser(Certificate certificate, UserRep userRep, char[] password) throws PrivilegeException;
/** /**
* Adds a new role with the information from this {@link RoleRep} * Adds a new role with the information from this {@link RoleRep}
* *
* @param certificate * @param certificate the {@link Certificate} of the user which has the privilege to perform this action
* the {@link Certificate} of the user which has the privilege to perform this action * @param roleRep the {@link RoleRep} containing the information to create the new {@link Role}
* @param roleRep
* the {@link RoleRep} containing the information to create the new {@link Role}
* *
* @throws AccessDeniedException * @throws AccessDeniedException if the user for this certificate may not perform the action
* if the user for this certificate may not perform the action * @throws PrivilegeException if there is anything wrong with this certificate or if the role already exists
* @throws PrivilegeException
* if there is anything wrong with this certificate or if the role already exists
*/ */
RoleRep addRole(Certificate certificate, RoleRep roleRep) throws PrivilegeException; RoleRep addRole(Certificate certificate, RoleRep roleRep) throws PrivilegeException;
/** /**
* Replaces the existing role with the information from this {@link RoleRep} * Replaces the existing role with the information from this {@link RoleRep}
* *
* @param certificate * @param certificate the {@link Certificate} of the user which has the privilege to perform this action
* the {@link Certificate} of the user which has the privilege to perform this action * @param roleRep the {@link RoleRep} containing the information to replace the existing {@link Role}
* @param roleRep
* the {@link RoleRep} containing the information to replace the existing {@link Role}
* *
* @throws AccessDeniedException * @throws AccessDeniedException if the user for this certificate may not perform the action
* if the user for this certificate may not perform the action * @throws PrivilegeException if there is anything wrong with this certificate or if the role does not exist
* @throws PrivilegeException
* if there is anything wrong with this certificate or if the role does not exist
*/ */
RoleRep replaceRole(Certificate certificate, RoleRep roleRep) throws PrivilegeException; RoleRep replaceRole(Certificate certificate, RoleRep roleRep) throws PrivilegeException;
/**
* Adds the role with the given roleName to the {@link User} with the given username
*
* @param certificate
* the {@link Certificate} of the user which has the privilege to perform this action
* @param username
* the username of the {@link User} to which the role should be added
* @param roleName
* the roleName of the {@link Role} which should be added to the {@link User}
*
* @throws AccessDeniedException
* if the user for this certificate may not perform the action
* @throws PrivilegeException
* if there is anything wrong with this certificate or if the role does not exist
*/
UserRep addRoleToUser(Certificate certificate, String username, String roleName) throws PrivilegeException;
/**
* Adds the {@link PrivilegeRep} to the {@link Role} with the given roleName or replaces it, if it already exists
*
* @param certificate
* the {@link Certificate} of the user which has the privilege to perform this action
* @param roleName
* the roleName of the {@link Role} to which the privilege should be added
* @param privilegeRep
* the representation of the {@link IPrivilege} which should be added or replaced on the {@link Role}
*
* @throws AccessDeniedException
* if the user for this certificate may not perform the action
* @throws PrivilegeException
* if there is anything wrong with this certificate or the role does not exist
*/
RoleRep addOrReplacePrivilegeOnRole(Certificate certificate, String roleName, PrivilegeRep privilegeRep)
throws PrivilegeException;
/** /**
* <p> * <p>
* Changes the password for the {@link User} with the given username. If the password is null, then the {@link User} * Changes the password for the {@link User} with the given username. If the password is null, then the {@link User}
@ -523,19 +396,14 @@ public interface PrivilegeHandler {
* It should be possible for a user to change their own password * It should be possible for a user to change their own password
* </p> * </p>
* *
* @param certificate * @param certificate the {@link Certificate} of the user which has the privilege to perform this action
* the {@link Certificate} of the user which has the privilege to perform this action * @param username the username of the {@link User} for which the password is to be changed
* @param username * @param password the new password for this user. If the password is null, then the {@link User} can not login
* the username of the {@link User} for which the password is to be changed * anymore. Otherwise the password must meet the requirements of the implementation under
* @param password * {@link PrivilegeHandler#validatePassword(Locale, char[])}
* the new password for this user. If the password is null, then the {@link User} can not login anymore. Otherwise
* the password must meet the requirements of the implementation under
* {@link PrivilegeHandler#validatePassword(Locale, char[])}
* *
* @throws AccessDeniedException * @throws AccessDeniedException if the user for this certificate may not perform the action
* if the user for this certificate may not perform the action * @throws PrivilegeException if there is anything wrong with this certificate
* @throws PrivilegeException
* if there is anything wrong with this certificate
*/ */
void setUserPassword(Certificate certificate, String username, char[] password) throws PrivilegeException; void setUserPassword(Certificate certificate, String username, char[] password) throws PrivilegeException;
@ -544,105 +412,79 @@ public interface PrivilegeHandler {
* Requires the given user to change their password after next login * Requires the given user to change their password after next login
* </p> * </p>
* *
* @param certificate * @param certificate the {@link Certificate} of the user which has the privilege to perform this action
* the {@link Certificate} of the user which has the privilege to perform this action * @param username the username of the {@link User} for which the password change is requested
* @param username
* the username of the {@link User} for which the password change is requested
* *
* @throws AccessDeniedException * @throws AccessDeniedException if the user for this certificate may not perform the action
* if the user for this certificate may not perform the action * @throws PrivilegeException if there is anything wrong with this certificate
* @throws PrivilegeException
* if there is anything wrong with this certificate
*/ */
void requirePasswordChange(Certificate certificate, String username) throws PrivilegeException; void requirePasswordChange(Certificate certificate, String username) throws PrivilegeException;
/** /**
* Changes the {@link UserState} of the user * Changes the {@link UserState} of the user
* *
* @param certificate * @param certificate the {@link Certificate} of the user which has the privilege to perform this action
* the {@link Certificate} of the user which has the privilege to perform this action * @param username the username of the {@link User} for which the {@link UserState} is to be changed
* @param username * @param state the new state for the user
* the username of the {@link User} for which the {@link UserState} is to be changed
* @param state
* the new state for the user
* *
* @throws AccessDeniedException * @throws AccessDeniedException if the user for this certificate may not perform the action
* if the user for this certificate may not perform the action * @throws PrivilegeException if there is anything wrong with this certificate
* @throws PrivilegeException
* if there is anything wrong with this certificate
*/ */
UserRep setUserState(Certificate certificate, String username, UserState state) throws PrivilegeException; UserRep setUserState(Certificate certificate, String username, UserState state) throws PrivilegeException;
/** /**
* Changes the {@link Locale} of the user * Changes the {@link Locale} of the user
* *
* @param certificate * @param certificate the {@link Certificate} of the user which has the privilege to perform this action
* the {@link Certificate} of the user which has the privilege to perform this action * @param username the username of the {@link User} for which the {@link Locale} is to be changed
* @param username * @param locale the new {@link Locale} for the user
* the username of the {@link User} for which the {@link Locale} is to be changed
* @param locale
* the new {@link Locale} for the user
* *
* @throws AccessDeniedException * @throws AccessDeniedException if the user for this certificate may not perform the action
* if the user for this certificate may not perform the action * @throws PrivilegeException if there is anything wrong with this certificate
* @throws PrivilegeException
* if there is anything wrong with this certificate
*/ */
UserRep setUserLocale(Certificate certificate, String username, Locale locale) throws PrivilegeException; UserRep setUserLocale(Certificate certificate, String username, Locale locale) throws PrivilegeException;
/** /**
* Initiate a password reset challenge for the given username * Initiate a password reset challenge for the given username
* *
* @param usage * @param usage the usage for which the challenge is requested
* the usage for which the challenge is requested * @param username the username of the user to initiate the challenge for
* @param username
* the username of the user to initiate the challenge for
*/ */
void initiateChallengeFor(Usage usage, String username); void initiateChallengeFor(Usage usage, String username);
/** /**
* Initiate a password reset challenge for the given username * Initiate a password reset challenge for the given username
* *
* @param usage * @param usage the usage for which the challenge is requested
* the usage for which the challenge is requested * @param username the username of the user to initiate the challenge for
* @param username * @param source the source of the challenge
* the username of the user to initiate the challenge for
* @param source
* the source of the challenge
*/ */
void initiateChallengeFor(Usage usage, String username, String source); void initiateChallengeFor(Usage usage, String username, String source);
/** /**
* Validate the response of a challenge for the given username * Validate the response of a challenge for the given username
* *
* @param username * @param username the username of the user for which the challenge is to be validated
* the username of the user for which the challenge is to be validated * @param challenge the challenge from the user
* @param challenge
* the challenge from the user
* *
* @return certificate with which the user can access the system with the {@link Usage} set to the value from the * @return certificate with which the user can access the system with the {@link Usage} set to the value from the
* initiated challenge * initiated challenge
* *
* @throws PrivilegeException * @throws PrivilegeException if anything goes wrong
* if anything goes wrong
*/ */
Certificate validateChallenge(String username, String challenge) throws PrivilegeException; Certificate validateChallenge(String username, String challenge) throws PrivilegeException;
/** /**
* Validate the response of a challenge for the given username * Validate the response of a challenge for the given username
* *
* @param username * @param username the username of the user for which the challenge is to be validated
* the username of the user for which the challenge is to be validated * @param challenge the challenge from the user
* @param challenge * @param source the source of the challenge validation
* the challenge from the user
* @param source
* the source of the challenge validation
* *
* @return certificate with which the user can access the system with the {@link Usage} set to the value from the * @return certificate with which the user can access the system with the {@link Usage} set to the value from the
* initiated challenge * initiated challenge
* *
* @throws PrivilegeException * @throws PrivilegeException if anything goes wrong
* if anything goes wrong
*/ */
Certificate validateChallenge(String username, String challenge, String source) throws PrivilegeException; Certificate validateChallenge(String username, String challenge, String source) throws PrivilegeException;
@ -650,18 +492,14 @@ public interface PrivilegeHandler {
* Authenticates a user by validating that a {@link User} for the given username and password exist and then returns * Authenticates a user by validating that a {@link User} for the given username and password exist and then returns
* a {@link Certificate} with which this user may then perform actions * a {@link Certificate} with which this user may then perform actions
* *
* @param username * @param username the username of the {@link User} which is registered in the {@link PersistenceHandler}
* the username of the {@link User} which is registered in the {@link PersistenceHandler} * @param password the password with which this user is to be authenticated. Null passwords are not accepted and
* @param password * they must meet the requirements of the {@link #validatePassword(Locale, char[])}-method
* the password with which this user is to be authenticated. Null passwords are not accepted and they must meet * @param keepAlive should this session be kept alive
* the requirements of the {@link #validatePassword(Locale, char[])}-method
* @param keepAlive
* should this session be kept alive
* *
* @return a {@link Certificate} with which this user may then perform actions * @return a {@link Certificate} with which this user may then perform actions
* *
* @throws AccessDeniedException * @throws AccessDeniedException if the user credentials are not valid
* if the user credentials are not valid
*/ */
Certificate authenticate(String username, char[] password, boolean keepAlive) throws AccessDeniedException; Certificate authenticate(String username, char[] password, boolean keepAlive) throws AccessDeniedException;
@ -669,22 +507,16 @@ public interface PrivilegeHandler {
* Authenticates a user by validating that a {@link User} for the given username and password exist and then returns * Authenticates a user by validating that a {@link User} for the given username and password exist and then returns
* a {@link Certificate} with which this user may then perform actions * a {@link Certificate} with which this user may then perform actions
* *
* @param username * @param username the username of the {@link User} which is registered in the {@link PersistenceHandler}
* the username of the {@link User} which is registered in the {@link PersistenceHandler} * @param password the password with which this user is to be authenticated. Null passwords are not accepted and
* @param password * they must meet the requirements of the {@link #validatePassword(Locale, char[])}-method
* the password with which this user is to be authenticated. Null passwords are not accepted and they must meet * @param source the source of the authentication request, i.e. remote IP
* the requirements of the {@link #validatePassword(Locale, char[])}-method * @param usage the usage type for this authentication
* @param source * @param keepAlive should this session be kept alive
* the source of the authentication request, i.e. remote IP
* @param usage
* the usage type for this authentication
* @param keepAlive
* should this session be kept alive
* *
* @return a {@link Certificate} with which this user may then perform actions * @return a {@link Certificate} with which this user may then perform actions
* *
* @throws AccessDeniedException * @throws AccessDeniedException if the user credentials are not valid
* if the user credentials are not valid
*/ */
Certificate authenticate(String username, char[] password, String source, Usage usage, boolean keepAlive) Certificate authenticate(String username, char[] password, String source, Usage usage, boolean keepAlive)
throws AccessDeniedException; throws AccessDeniedException;
@ -692,47 +524,37 @@ public interface PrivilegeHandler {
/** /**
* Authenticates a user on a remote Single Sign On service. This is implemented by the * Authenticates a user on a remote Single Sign On service. This is implemented by the
* *
* @param data * @param data the data to perform the SSO
* the data to perform the SSO * @param keepAlive should this session be kept alive
* @param keepAlive
* should this session be kept alive
* *
* @return the {@link Certificate} for the user * @return the {@link Certificate} for the user
* *
* @throws PrivilegeException * @throws PrivilegeException if something goes wrong with the SSO
* if something goes wrong with the SSO
*/ */
Certificate authenticateSingleSignOn(Object data, boolean keepAlive) throws PrivilegeException; Certificate authenticateSingleSignOn(Object data, boolean keepAlive) throws PrivilegeException;
/** /**
* Authenticates a user on a remote Single Sign On service. This is implemented by the * Authenticates a user on a remote Single Sign On service. This is implemented by the
* *
* @param data * @param data the data to perform the SSO
* the data to perform the SSO * @param source the source of the SSO authentication
* @param source * @param keepAlive may the certificate be kept alive
* the source of the SSO authentication
* @param keepAlive
* may the certificate be kept alive
* *
* @return the {@link Certificate} for the user * @return the {@link Certificate} for the user
* *
* @throws PrivilegeException * @throws PrivilegeException if something goes wrong with the SSO
* if something goes wrong with the SSO
*/ */
Certificate authenticateSingleSignOn(Object data, String source, boolean keepAlive) throws PrivilegeException; Certificate authenticateSingleSignOn(Object data, String source, boolean keepAlive) throws PrivilegeException;
/** /**
* Refreshes the given certificate's session with a new session, i.e. a new certificate * Refreshes the given certificate's session with a new session, i.e. a new certificate
* *
* @param certificate * @param certificate the certificate for which to perform a refresh
* the certificate for which to perform a refresh * @param source the source of the refresh request
* @param source
* the source of the refresh request
* *
* @return a {@link Certificate} with which this user may then perform actions * @return a {@link Certificate} with which this user may then perform actions
* *
* @throws AccessDeniedException * @throws AccessDeniedException if the certificate is now valid, or refreshing is not allowed
* if the certificate is now valid, or refreshing is not allowed
*/ */
Certificate refresh(Certificate certificate, String source) throws AccessDeniedException; Certificate refresh(Certificate certificate, String source) throws AccessDeniedException;
@ -754,8 +576,7 @@ public interface PrivilegeHandler {
* Invalidates the session for the given {@link Certificate}, effectively logging out the user who was authenticated * Invalidates the session for the given {@link Certificate}, effectively logging out the user who was authenticated
* with the credentials associated to the given {@link Certificate} * with the credentials associated to the given {@link Certificate}
* *
* @param certificate * @param certificate the {@link Certificate} for which the session is to be invalidated
* the {@link Certificate} for which the session is to be invalidated
* *
* @return true if the session was still valid and is now invalidated, false otherwise * @return true if the session was still valid and is now invalidated, false otherwise
*/ */
@ -769,15 +590,12 @@ public interface PrivilegeHandler {
* encapsulated state of a user's privileges so that for the duration of a user's call, the user can perform their * encapsulated state of a user's privileges so that for the duration of a user's call, the user can perform their
* actions and do not need to access the {@link PrivilegeHandler} anymore * actions and do not need to access the {@link PrivilegeHandler} anymore
* *
* @param certificate * @param certificate the {@link Certificate} to check
* the {@link Certificate} to check
* *
* @return the {@link PrivilegeContext} for the given {@link Certificate} * @return the {@link PrivilegeContext} for the given {@link Certificate}
* *
* @throws PrivilegeException * @throws PrivilegeException if there is anything wrong with this certificate
* if there is anything wrong with this certificate * @throws NotAuthenticatedException if the certificate has expired
* @throws NotAuthenticatedException
* if the certificate has expired
*/ */
PrivilegeContext validate(Certificate certificate) throws PrivilegeException; PrivilegeContext validate(Certificate certificate) throws PrivilegeException;
@ -786,13 +604,10 @@ public interface PrivilegeHandler {
* system user session and that the user exists for the certificate. This method checks if the {@link Certificate} * system user session and that the user exists for the certificate. This method checks if the {@link Certificate}
* has been tampered with * has been tampered with
* *
* @param ctx * @param ctx the {@link PrivilegeContext} to check
* the {@link PrivilegeContext} to check
* *
* @throws PrivilegeException * @throws PrivilegeException if there is anything wrong with this privilege context
* if there is anything wrong with this privilege context * @throws NotAuthenticatedException if the privilege context has expired
* @throws NotAuthenticatedException
* if the privilege context has expired
*/ */
void validateSystemSession(PrivilegeContext ctx) throws PrivilegeException; void validateSystemSession(PrivilegeContext ctx) throws PrivilegeException;
@ -804,17 +619,13 @@ public interface PrivilegeHandler {
* encapsulated state of a user's privileges so that for the duration of a user's call, the user can perform their * encapsulated state of a user's privileges so that for the duration of a user's call, the user can perform their
* actions and do not need to access the {@link PrivilegeHandler} anymore * actions and do not need to access the {@link PrivilegeHandler} anymore
* *
* @param certificate * @param certificate the {@link Certificate} to check
* the {@link Certificate} to check * @param source the source, e.g. remote IP for this validation request
* @param source
* the source, e.g. remote IP for this validation request
* *
* @return the {@link PrivilegeContext} for the given {@link Certificate} * @return the {@link PrivilegeContext} for the given {@link Certificate}
* *
* @throws PrivilegeException * @throws PrivilegeException if there is anything wrong with this certificate
* if there is anything wrong with this certificate * @throws NotAuthenticatedException if the certificate has expired
* @throws NotAuthenticatedException
* if the certificate has expired
*/ */
PrivilegeContext validate(Certificate certificate, String source) throws PrivilegeException; PrivilegeContext validate(Certificate certificate, String source) throws PrivilegeException;
@ -840,15 +651,13 @@ public interface PrivilegeHandler {
* *
* <b>Note:</b> It depends on the underlying {@link PersistenceHandler} implementation if data really is read * <b>Note:</b> It depends on the underlying {@link PersistenceHandler} implementation if data really is read
* *
* @param certificate * @param certificate the {@link Certificate} of the user which has the privilege to perform this action
* the {@link Certificate} of the user which has the privilege to perform this action * @param source the source of the request
* @param source
* the source of the request
* *
* @return true if the reload was successful, false if something went wrong * @return true if the reload was successful, false if something went wrong
* *
* @throws AccessDeniedException * @throws AccessDeniedException if the users of the given certificate does not have the privilege to perform this
* if the users of the given certificate does not have the privilege to perform this action * action
*/ */
boolean reload(Certificate certificate, String source); boolean reload(Certificate certificate, String source);
@ -856,28 +665,25 @@ public interface PrivilegeHandler {
* Persists any changes to the privilege data model. Changes are thus not persisted immediately, but must be * Persists any changes to the privilege data model. Changes are thus not persisted immediately, but must be
* actively performed * actively performed
* *
* @param certificate * @param certificate the {@link Certificate} of the user which has the privilege to perform this action
* the {@link Certificate} of the user which has the privilege to perform this action
* *
* @return true if changes were persisted, false if no changes were persisted * @return true if changes were persisted, false if no changes were persisted
* *
* @throws AccessDeniedException * @throws AccessDeniedException if the users of the given certificate does not have the privilege to perform this
* if the users of the given certificate does not have the privilege to perform this action * action
*/ */
boolean persist(Certificate certificate) throws AccessDeniedException; boolean persist(Certificate certificate) throws AccessDeniedException;
/** /**
* Persists all currently active sessions * Persists all currently active sessions
* *
* @param certificate * @param certificate the {@link Certificate} of the user which has the privilege to perform this action
* the {@link Certificate} of the user which has the privilege to perform this action * @param source the source of the request
* @param source
* the source of the request
* *
* @return true if changes were persisted, false if not (i.e. not enabled) * @return true if changes were persisted, false if not (i.e. not enabled)
* *
* @throws AccessDeniedException * @throws AccessDeniedException if the users of the given certificate does not have the privilege to perform this
* if the users of the given certificate does not have the privilege to perform this action * action
*/ */
boolean persistSessions(Certificate certificate, String source) throws AccessDeniedException; boolean persistSessions(Certificate certificate, String source) throws AccessDeniedException;
@ -886,15 +692,11 @@ public interface PrivilegeHandler {
* has the state {@link UserState#SYSTEM} and this user must have privilege to perform the concrete implementation * has the state {@link UserState#SYSTEM} and this user must have privilege to perform the concrete implementation
* of the given {@link SystemAction} instance * of the given {@link SystemAction} instance
* *
* @param systemUsername * @param systemUsername the username of the system user to perform the action as
* the username of the system user to perform the action as * @param action the action to be performed as the system user
* @param action
* the action to be performed as the system user
* *
* @throws PrivilegeException * @throws PrivilegeException if the user does not exist, or the system action is not allowed
* if the user does not exist, or the system action is not allowed * @throws Exception if anything else goes wrong during execution
* @throws Exception
* if anything else goes wrong during execution
*/ */
void runAs(String systemUsername, SystemAction action) throws PrivilegeException, Exception; void runAs(String systemUsername, SystemAction action) throws PrivilegeException, Exception;
@ -903,17 +705,13 @@ public interface PrivilegeHandler {
* has the state {@link UserState#SYSTEM} and this user must have privilege to perform the concrete implementation * has the state {@link UserState#SYSTEM} and this user must have privilege to perform the concrete implementation
* of the given {@link SystemAction} instance * of the given {@link SystemAction} instance
* *
* @param systemUsername * @param systemUsername the username of the system user to perform the action as
* the username of the system user to perform the action as * @param action the action to be performed as the system user
* @param action
* the action to be performed as the system user
* *
* @return the action * @return the action
* *
* @throws PrivilegeException * @throws PrivilegeException if the user does not exist, or the system action is not allowed
* if the user does not exist, or the system action is not allowed * @throws Exception if anything else goes wrong during execution
* @throws Exception
* if anything else goes wrong during execution
*/ */
<T> T runWithResult(String systemUsername, SystemActionWithResult<T> action) throws PrivilegeException, Exception; <T> T runWithResult(String systemUsername, SystemActionWithResult<T> action) throws PrivilegeException, Exception;
@ -923,13 +721,11 @@ public interface PrivilegeHandler {
* {@link PrivilegeContext} should be open for a longer period of time, or where opening many * {@link PrivilegeContext} should be open for a longer period of time, or where opening many
* {@link PrivilegeContext} is resource intensive e.g. on low power devices. * {@link PrivilegeContext} is resource intensive e.g. on low power devices.
* *
* @param systemUsername * @param systemUsername the username of the system user to perform the action as
* the username of the system user to perform the action as
* *
* @return the action * @return the action
* *
* @throws PrivilegeException * @throws PrivilegeException if the user does not exist, or the system action is not allowed
* if the user does not exist, or the system action is not allowed
*/ */
PrivilegeContext openSystemUserContext(String systemUsername) throws PrivilegeException; PrivilegeContext openSystemUserContext(String systemUsername) throws PrivilegeException;

View File

@ -1,128 +0,0 @@
package li.strolch.privilege.handler;
import static li.strolch.privilege.base.PrivilegeConstants.*;
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;
import li.strolch.utils.dbc.DBC;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SimpleLdapPrivilegeHandler extends BaseLdapPrivilegeHandler {
protected static final Logger logger = LoggerFactory.getLogger(SimpleLdapPrivilegeHandler.class);
private Locale defaultLocale;
private String adminUsers;
private Map<String, Set<String>> rolesForLdapGroups;
private String organisation;
private String location;
private String realm;
@Override
public 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(executorService, parameterMap, encryptionHandler, passwordStrengthHandler, persistenceHandler,
userChallengeHandler, ssoHandler, policyMap);
this.organisation = parameterMap.getOrDefault(ORGANISATION, "");
this.location = parameterMap.getOrDefault(LOCATION, "");
this.realm = parameterMap.getOrDefault(REALM, "");
this.defaultLocale = parameterMap.containsKey("defaultLocale") ?
Locale.forLanguageTag(parameterMap.get("defaultLocale")) :
Locale.getDefault();
this.adminUsers = parameterMap.get("adminUsers");
this.rolesForLdapGroups = getLdapGroupToRolesMappingFromConfig(parameterMap);
}
@Override
protected String getFirstName(String username, Attributes attrs) throws NamingException {
String value = getLdapString(attrs, "givenName");
return isEmpty(value) ? username : value;
}
@Override
protected String getLastName(String username, Attributes attrs) throws NamingException {
String value = getLdapString(attrs, "sn");
return isEmpty(value) ? username : value;
}
@Override
protected Map<String, String> buildProperties(String username, Attributes attrs, Set<String> ldapGroups,
Set<String> strolchRoles) {
Map<String, String> properties = new HashMap<>();
properties.put(ORGANISATION, this.organisation);
properties.put(LOCATION, this.location);
properties.put(REALM, this.realm);
return properties;
}
@Override
protected Locale getLocale(Attributes attrs) {
return this.defaultLocale;
}
@Override
protected Set<String> getLdapGroups(String username, Attributes attrs) throws NamingException {
Set<String> ldapGroups = LdapHelper.getLdapGroups(attrs);
logger.info("User " + username + " has LDAP Groups: ");
ldapGroups.forEach(s -> logger.info("- " + s));
return ldapGroups;
}
@Override
protected Set<String> mapToStrolchRoles(String username, Set<String> ldapGroups) {
Set<String> strolchRoles = new HashSet<>();
for (String ldapRole : ldapGroups) {
Set<String> foundStrolchRoles = this.rolesForLdapGroups.get(ldapRole);
if (foundStrolchRoles != null)
strolchRoles.addAll(foundStrolchRoles);
}
// see if this is an admin user
if (this.adminUsers.contains(username))
strolchRoles = this.rolesForLdapGroups.get("admin");
return strolchRoles;
}
private Map<String, Set<String>> getLdapGroupToRolesMappingFromConfig(Map<String, String> params) {
String rolesForLdapGroups = params.get("rolesForLdapGroups");
DBC.PRE.assertNotEmpty("No roles mapping for ldap directory groups defined (param: rolesForLdapGroups)",
rolesForLdapGroups);
// rolesForLdapGroups = admin=StrolchAdmin,UserPrivileges;user=UserPrivileges
String[] ldapGroupRoles = rolesForLdapGroups.split(";");
Map<String, Set<String>> result = new HashMap<>();
for (String ldapGroupRole : ldapGroupRoles) {
ldapGroupRole = ldapGroupRole.trim();
String[] splitGroupRoles = ldapGroupRole.split("=");
String ldapGroupName = splitGroupRoles[0];
String[] strolchRoles = splitGroupRoles[1].split(",");
Set<String> roleNames = new HashSet<>();
for (String strolchRole : strolchRoles) {
roleNames.add(strolchRole.trim());
}
result.put(ldapGroupName, roleNames);
}
return result;
}
}

View File

@ -15,29 +15,31 @@
*/ */
package li.strolch.privilege.handler; package li.strolch.privilege.handler;
import static java.text.MessageFormat.format; import li.strolch.privilege.base.PrivilegeException;
import static li.strolch.privilege.handler.PrivilegeHandler.PARAM_CASE_INSENSITIVE_USERNAME; import li.strolch.privilege.helper.XmlConstants;
import static li.strolch.privilege.helper.XmlConstants.*; import li.strolch.privilege.model.internal.Group;
import static li.strolch.utils.helper.StringHelper.formatNanoDuration; import li.strolch.privilege.model.internal.Role;
import li.strolch.privilege.model.internal.User;
import li.strolch.privilege.xml.*;
import li.strolch.utils.helper.XmlHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.xml.stream.XMLStreamException;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import li.strolch.privilege.base.PrivilegeException; import static java.lang.Boolean.parseBoolean;
import li.strolch.privilege.helper.XmlConstants; import static java.text.MessageFormat.format;
import li.strolch.privilege.model.internal.Role; import static li.strolch.privilege.handler.PrivilegeHandler.PARAM_CASE_INSENSITIVE_USERNAME;
import li.strolch.privilege.model.internal.User; import static li.strolch.privilege.helper.XmlConstants.*;
import li.strolch.privilege.xml.PrivilegeRolesDomWriter; import static li.strolch.utils.helper.StringHelper.formatNanoDuration;
import li.strolch.privilege.xml.PrivilegeRolesSaxReader; import static li.strolch.utils.helper.StringHelper.isEmpty;
import li.strolch.privilege.xml.PrivilegeUsersDomWriter;
import li.strolch.privilege.xml.PrivilegeUsersSaxReader;
import li.strolch.utils.helper.StringHelper;
import li.strolch.utils.helper.XmlHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** /**
* {@link PersistenceHandler} implementation which reads the configuration from XML files. These configuration is passed * {@link PersistenceHandler} implementation which reads the configuration from XML files. These configuration is passed
@ -50,20 +52,24 @@ public class XmlPersistenceHandler implements PersistenceHandler {
protected static final Logger logger = LoggerFactory.getLogger(XmlPersistenceHandler.class); protected static final Logger logger = LoggerFactory.getLogger(XmlPersistenceHandler.class);
private final Map<String, User> userMap; private final Map<String, User> userMap;
private final Map<String, Group> groupMap;
private final Map<String, Role> roleMap; private final Map<String, Role> roleMap;
private boolean userMapDirty; private boolean userMapDirty;
private boolean groupMapDirty;
private boolean roleMapDirty; private boolean roleMapDirty;
private Map<String, String> parameterMap; private Map<String, String> parameterMap;
private File usersPath; private File usersPath;
private File groupsPath;
private File rolesPath; private File rolesPath;
private boolean caseInsensitiveUsername; private boolean caseInsensitiveUsername;
public XmlPersistenceHandler() { public XmlPersistenceHandler() {
this.roleMap = new ConcurrentHashMap<>(); this.roleMap = new ConcurrentHashMap<>();
this.groupMap = new ConcurrentHashMap<>();
this.userMap = new ConcurrentHashMap<>(); this.userMap = new ConcurrentHashMap<>();
} }
@ -79,6 +85,13 @@ public class XmlPersistenceHandler implements PersistenceHandler {
} }
} }
@Override
public List<Group> getAllGroups() {
synchronized (this.groupMap) {
return new LinkedList<>(this.groupMap.values());
}
}
@Override @Override
public List<Role> getAllRoles() { public List<Role> getAllRoles() {
synchronized (this.roleMap) { synchronized (this.roleMap) {
@ -91,6 +104,11 @@ public class XmlPersistenceHandler implements PersistenceHandler {
return this.userMap.get(this.caseInsensitiveUsername ? username.toLowerCase() : username); return this.userMap.get(this.caseInsensitiveUsername ? username.toLowerCase() : username);
} }
@Override
public Group getGroup(String groupName) {
return this.groupMap.get(groupName);
}
@Override @Override
public Role getRole(String roleName) { public Role getRole(String roleName) {
return this.roleMap.get(roleName); return this.roleMap.get(roleName);
@ -103,6 +121,13 @@ public class XmlPersistenceHandler implements PersistenceHandler {
return user; return user;
} }
@Override
public Group removeGroup(String groupName) {
Group group = this.groupMap.remove(groupName);
this.groupMapDirty = group != null;
return group;
}
@Override @Override
public Role removeRole(String roleName) { public Role removeRole(String roleName) {
Role role = this.roleMap.remove(roleName); Role role = this.roleMap.remove(roleName);
@ -129,6 +154,23 @@ public class XmlPersistenceHandler implements PersistenceHandler {
this.userMapDirty = true; this.userMapDirty = true;
} }
@Override
public void addGroup(Group group) {
if (this.groupMap.containsKey(group.name()))
throw new IllegalStateException(format("The group {0} already exists!", group.name()));
this.groupMap.put(group.name(), group);
this.groupMapDirty = true;
}
@Override
public void replaceGroup(Group group) {
if (!this.groupMap.containsKey(group.name()))
throw new IllegalStateException(
format("The group {0} can not be replaced as it does not exist!", group.name()));
this.groupMap.put(group.name(), group);
this.groupMapDirty = true;
}
@Override @Override
public void addRole(Role role) { public void addRole(Role role) {
if (this.roleMap.containsKey(role.getName())) if (this.roleMap.containsKey(role.getName()))
@ -149,9 +191,10 @@ public class XmlPersistenceHandler implements PersistenceHandler {
/** /**
* Initializes this {@link XmlPersistenceHandler} by reading the following parameters: * Initializes this {@link XmlPersistenceHandler} by reading the following parameters:
* <ul> * <ul>
* <li>{@link XmlConstants#XML_PARAM_BASE_PATH}</li> * <li>{@link XmlConstants#PARAM_BASE_PATH}</li>
* <li>{@link XmlConstants#XML_PARAM_USERS_FILE}</li> * <li>{@link XmlConstants#PARAM_USERS_FILE}</li>
* <li>{@link XmlConstants#XML_PARAM_ROLES_FILE}</li> * <li>{@link XmlConstants#PARAM_GROUPS_FILE}</li>
* <li>{@link XmlConstants#PARAM_ROLES_FILE}</li>
* </ul> * </ul>
*/ */
@Override @Override
@ -159,58 +202,50 @@ public class XmlPersistenceHandler implements PersistenceHandler {
this.parameterMap = Map.copyOf(paramsMap); this.parameterMap = Map.copyOf(paramsMap);
// get and validate base bath // get and validate base bath
String basePath = this.parameterMap.get(XML_PARAM_BASE_PATH); String basePath = this.parameterMap.get(PARAM_BASE_PATH);
File basePathF = new File(basePath); File basePathF = new File(basePath);
if (!basePathF.exists() && !basePathF.isDirectory()) { if (!basePathF.exists() && !basePathF.isDirectory()) {
String msg = "[{0}] Defined parameter {1} does not point to a valid path at {2}"; String msg = "[{0}] Defined parameter {1} does not point to a valid path at {2}";
msg = format(msg, PersistenceHandler.class.getName(), XML_PARAM_BASE_PATH, basePathF.getAbsolutePath()); msg = format(msg, PersistenceHandler.class.getName(), PARAM_BASE_PATH, basePathF.getAbsolutePath());
throw new PrivilegeException(msg); throw new PrivilegeException(msg);
} }
// get users file name File usersPath = getFile(basePath, PARAM_USERS_FILE, PARAM_USERS_FILE_DEF, true);
String usersFileName = this.parameterMap.get(XML_PARAM_USERS_FILE); File groupsPath = getFile(basePath, PARAM_GROUPS_FILE, PARAM_GROUPS_FILE_DEF, false);
if (StringHelper.isEmpty(usersFileName)) { File rolesPath = getFile(basePath, PARAM_ROLES_FILE, PARAM_ROLES_FILE_DEF, true);
String msg = "[{0}] Defined parameter {1} is not valid as it is empty!";
msg = format(msg, PersistenceHandler.class.getName(), XML_PARAM_USERS_FILE);
throw new PrivilegeException(msg);
}
// get roles file name
String rolesFileName = this.parameterMap.get(XML_PARAM_ROLES_FILE);
if (StringHelper.isEmpty(rolesFileName)) {
String msg = "[{0}] Defined parameter {1} is not valid as it is empty!";
msg = format(msg, PersistenceHandler.class.getName(), XML_PARAM_ROLES_FILE);
throw new PrivilegeException(msg);
}
// validate users file exists
String usersPathS = basePath + "/" + usersFileName;
File usersPath = new File(usersPathS);
if (!usersPath.exists()) {
String msg = "[{0}] Defined parameter {1} is invalid as users file does not exist at path {2}";
msg = format(msg, PersistenceHandler.class.getName(), XML_PARAM_USERS_FILE, usersPath.getAbsolutePath());
throw new PrivilegeException(msg);
}
// validate roles file exists
String rolesPathS = basePath + "/" + rolesFileName;
File rolesPath = new File(rolesPathS);
if (!rolesPath.exists()) {
String msg = "[{0}] Defined parameter {1} is invalid as roles file does not exist at path {2}";
msg = format(msg, PersistenceHandler.class.getName(), XML_PARAM_ROLES_FILE, rolesPath.getAbsolutePath());
throw new PrivilegeException(msg);
}
// save path to model // save path to model
this.usersPath = usersPath; this.usersPath = usersPath;
this.groupsPath = groupsPath;
this.rolesPath = rolesPath; this.rolesPath = rolesPath;
this.caseInsensitiveUsername = Boolean.parseBoolean(this.parameterMap.get(PARAM_CASE_INSENSITIVE_USERNAME)); this.caseInsensitiveUsername = !this.parameterMap.containsKey(PARAM_CASE_INSENSITIVE_USERNAME) ||
parseBoolean(this.parameterMap.get(PARAM_CASE_INSENSITIVE_USERNAME));
if (reload()) if (reload())
logger.info("Privilege Data loaded."); logger.info("Privilege Data loaded.");
} }
private File getFile(String basePath, String param, String defaultValue, boolean required) {
String fileName = this.parameterMap.get(param);
if (isEmpty(fileName)) {
fileName = defaultValue;
String msg = "[{0}] Parameter {1} is not defined, using default {2}!";
msg = format(msg, PersistenceHandler.class.getName(), param, defaultValue);
logger.warn(msg);
}
String path = basePath + "/" + fileName;
File file = new File(path);
if (required && !file.exists()) {
String msg = "[{0}] Defined parameter {1} is invalid as file does not exist at path {2}";
msg = format(msg, PersistenceHandler.class.getName(), param, file.getAbsolutePath());
throw new PrivilegeException(msg);
}
return file;
}
/** /**
* Reads the XML configuration files which contain the model. Which configuration files are parsed was defined in * Reads the XML configuration files which contain the model. Which configuration files are parsed was defined in
* the while calling {@link #initialize(Map)} * the while calling {@link #initialize(Map)}
@ -224,6 +259,10 @@ public class XmlPersistenceHandler implements PersistenceHandler {
PrivilegeUsersSaxReader usersXmlHandler = new PrivilegeUsersSaxReader(this.caseInsensitiveUsername); PrivilegeUsersSaxReader usersXmlHandler = new PrivilegeUsersSaxReader(this.caseInsensitiveUsername);
XmlHelper.parseDocument(this.usersPath, usersXmlHandler); XmlHelper.parseDocument(this.usersPath, usersXmlHandler);
PrivilegeGroupsSaxReader groupsXmlHandler = new PrivilegeGroupsSaxReader();
if (this.groupsPath.exists())
XmlHelper.parseDocument(this.groupsPath, groupsXmlHandler);
PrivilegeRolesSaxReader rolesXmlHandler = new PrivilegeRolesSaxReader(); PrivilegeRolesSaxReader rolesXmlHandler = new PrivilegeRolesSaxReader();
XmlHelper.parseDocument(this.rolesPath, rolesXmlHandler); XmlHelper.parseDocument(this.rolesPath, rolesXmlHandler);
@ -233,6 +272,12 @@ public class XmlPersistenceHandler implements PersistenceHandler {
this.roleMap.putAll(rolesXmlHandler.getRoles()); this.roleMap.putAll(rolesXmlHandler.getRoles());
} }
// GROUPS
synchronized (this.groupMap) {
this.groupMap.clear();
this.groupMap.putAll(groupsXmlHandler.getGroups());
}
// USERS // USERS
synchronized (this.userMap) { synchronized (this.userMap) {
this.userMap.clear(); this.userMap.clear();
@ -240,21 +285,40 @@ public class XmlPersistenceHandler implements PersistenceHandler {
} }
this.userMapDirty = false; this.userMapDirty = false;
this.groupMapDirty = false;
this.roleMapDirty = false; this.roleMapDirty = false;
logger.info(format("Read {0} Users", this.userMap.size())); logger.info(format("Read {0} Users", this.userMap.size()));
logger.info(format("Read {0} Groups", this.groupMap.size()));
logger.info(format("Read {0} Roles", this.roleMap.size())); logger.info(format("Read {0} Roles", this.roleMap.size()));
// validate referenced roles exist // validate referenced elements exist
for (User user : this.userMap.values()) { for (User user : this.userMap.values()) {
for (String roleName : user.getRoles()) { for (String roleName : user.getRoles()) {
// validate that role exists // validate that role exists
if (getRole(roleName) == null) { if (getRole(roleName) == null) {
logger.error( logger.error(
format("Role {0} does not exist referenced by user {1}", roleName, user.getUsername())); format("Role {0} does not exist referenced by user {1}", roleName, user.getUsername()));
} }
} }
for (String groupName : user.getGroups()) {
// validate that group exists
if (getGroup(groupName) == null) {
logger.error(
format("Group {0} does not exist referenced by user {1}", groupName, user.getUsername()));
}
}
}
// validate referenced roles exist on groups
for (Group group : this.groupMap.values()) {
for (String roleName : group.roles()) {
// validate that role exists
if (getRole(roleName) == null) {
logger.error(format("Role {0} does not exist referenced by group {1}", roleName, group.name()));
}
}
} }
return true; return true;
@ -264,49 +328,34 @@ public class XmlPersistenceHandler implements PersistenceHandler {
* Writes the model to the XML files. Where the files are written to was defined in the {@link #initialize(Map)} * Writes the model to the XML files. Where the files are written to was defined in the {@link #initialize(Map)}
*/ */
@Override @Override
public boolean persist() { public boolean persist() throws XMLStreamException, IOException {
long start = System.nanoTime(); long start = System.nanoTime();
// get users file name
String usersFileName = this.parameterMap.get(XML_PARAM_USERS_FILE);
if (usersFileName == null || usersFileName.isEmpty()) {
String msg = "[{0}] Defined parameter {1} is invalid";
msg = format(msg, PersistenceHandler.class.getName(), XML_PARAM_USERS_FILE);
throw new PrivilegeException(msg);
}
// get roles file name
String rolesFileName = this.parameterMap.get(XML_PARAM_ROLES_FILE);
if (rolesFileName == null || rolesFileName.isEmpty()) {
String msg = "[{0}] Defined parameter {1} is invalid";
msg = format(msg, PersistenceHandler.class.getName(), XML_PARAM_ROLES_FILE);
throw new PrivilegeException(msg);
}
boolean saved = false; boolean saved = false;
// get users file // write users file
if (this.userMapDirty) { if (this.userMapDirty) {
// delegate writing new PrivilegeUsersSaxWriter(getAllUsers(), this.usersPath).write();
PrivilegeUsersDomWriter modelWriter = new PrivilegeUsersDomWriter(getAllUsers(), this.usersPath);
modelWriter.write();
this.userMapDirty = false; this.userMapDirty = false;
saved = true; saved = true;
} }
// get roles file // write groups file
if (this.roleMapDirty) { if (this.groupMapDirty) {
// delegate writing new PrivilegeGroupsSaxWriter(getAllGroups(), this.groupsPath).write();
PrivilegeRolesDomWriter modelWriter = new PrivilegeRolesDomWriter(getAllRoles(), this.rolesPath); this.groupMapDirty = false;
modelWriter.write(); saved = true;
}
// write roles file
if (this.roleMapDirty) {
new PrivilegeRolesSaxWriter(getAllRoles(), this.rolesPath).write();
this.roleMapDirty = false; this.roleMapDirty = false;
saved = true; saved = true;
} }
logger.info("Persist took " + (formatNanoDuration(System.nanoTime() - start))); long tookNanos = System.nanoTime() - start;
if (TimeUnit.NANOSECONDS.toMillis(tookNanos) > 100)
logger.warn("Persist took " + (formatNanoDuration(tookNanos)));
return saved; return saved;
} }
} }

View File

@ -26,7 +26,6 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
import static li.strolch.privilege.base.PrivilegeConstants.*; import static li.strolch.privilege.base.PrivilegeConstants.*;
import static li.strolch.privilege.model.internal.PasswordCrypt.buildPasswordString;
/** /**
* <p> * <p>
@ -111,9 +110,9 @@ public class PasswordCreator {
} }
Map<String, String> parameterMap = new HashMap<>(); Map<String, String> parameterMap = new HashMap<>();
parameterMap.put(XmlConstants.XML_PARAM_HASH_ALGORITHM, hashAlgorithm); parameterMap.put(XmlConstants.PARAM_HASH_ALGORITHM, hashAlgorithm);
parameterMap.put(XmlConstants.XML_PARAM_HASH_ITERATIONS, String.valueOf(iterations)); parameterMap.put(XmlConstants.PARAM_HASH_ITERATIONS, String.valueOf(iterations));
parameterMap.put(XmlConstants.XML_PARAM_HASH_KEY_LENGTH, String.valueOf(keyLength)); parameterMap.put(XmlConstants.PARAM_HASH_KEY_LENGTH, String.valueOf(keyLength));
DefaultEncryptionHandler encryptionHandler = new DefaultEncryptionHandler(); DefaultEncryptionHandler encryptionHandler = new DefaultEncryptionHandler();
encryptionHandler.initialize(parameterMap); encryptionHandler.initialize(parameterMap);
@ -136,15 +135,15 @@ public class PasswordCreator {
byte[] salt = saltS.getBytes(); byte[] salt = saltS.getBytes();
PasswordCrypt passwordCrypt = encryptionHandler.hashPassword(password, salt); PasswordCrypt passwordCrypt = encryptionHandler.hashPassword(password, salt);
String passwordHashS = StringHelper.toHexString(passwordCrypt.getPassword()); String passwordHashS = StringHelper.toHexString(passwordCrypt.password());
System.out.println("Hash is: " + passwordHashS); System.out.println("Hash is: " + passwordHashS);
System.out.println("Salt is: " + saltS); System.out.println("Salt is: " + saltS);
System.out.println(); System.out.println();
System.out.println( System.out.println(
XmlConstants.XML_ATTR_PASSWORD + "=\"" + passwordHashS + "\" " + XmlConstants.XML_ATTR_SALT + XmlConstants.ATTR_PASSWORD + "=\"" + passwordHashS + "\" " + XmlConstants.ATTR_SALT + "=\"" +
"=\"" + saltS + "\""); saltS + "\"");
System.out.println(XmlConstants.XML_ATTR_PASSWORD + "=\"" + passwordCrypt.buildPasswordString() + "\""); System.out.println(XmlConstants.ATTR_PASSWORD + "=\"" + passwordCrypt.buildPasswordString() + "\"");
System.out.println(); System.out.println();
} }
} }

View File

@ -1,18 +1,20 @@
package li.strolch.privilege.helper; package li.strolch.privilege.helper;
import li.strolch.privilege.model.internal.Role;
import li.strolch.privilege.xml.PrivilegeRolesSaxReader;
import li.strolch.privilege.xml.PrivilegeRolesSaxWriter;
import li.strolch.utils.helper.XmlHelper;
import javax.xml.stream.XMLStreamException;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import li.strolch.privilege.model.internal.Role;
import li.strolch.privilege.xml.PrivilegeRolesDomWriter;
import li.strolch.privilege.xml.PrivilegeRolesSaxReader;
import li.strolch.utils.helper.XmlHelper;
public class WriteRolesFileHelper { public class WriteRolesFileHelper {
public static void main(String[] args) { public static void main(String[] args) throws XMLStreamException, IOException {
if (args.length != 2) if (args.length != 2)
throw new IllegalStateException("Usage: <src> <dst>"); throw new IllegalStateException("Usage: <src> <dst>");
@ -31,7 +33,7 @@ public class WriteRolesFileHelper {
Map<String, Role> rolesMap = xmlHandler.getRoles(); Map<String, Role> rolesMap = xmlHandler.getRoles();
List<Role> roles = new ArrayList<>(rolesMap.values()); List<Role> roles = new ArrayList<>(rolesMap.values());
PrivilegeRolesDomWriter configSaxWriter = new PrivilegeRolesDomWriter(roles, dst); PrivilegeRolesSaxWriter configSaxWriter = new PrivilegeRolesSaxWriter(roles, dst);
configSaxWriter.write(); configSaxWriter.write();
} }
} }

View File

@ -23,298 +23,69 @@ package li.strolch.privilege.helper;
@SuppressWarnings("nls") @SuppressWarnings("nls")
public class XmlConstants { public class XmlConstants {
/** public static final String ROOT_PRIVILEGE = "Privilege";
* XML_ROOT_PRIVILEGE_CONTAINER = "PrivilegeContainer" : public static final String CONTAINER = "Container";
*/ public static final String POLICIES = "Policies";
public static final String XML_ROOT_PRIVILEGE = "Privilege"; public static final String PRIVILEGES = "Privileges";
public static final String ROOT_PRIVILEGE_USERS_AND_ROLES = "UsersAndRoles";
/** public static final String ROOT_CERTIFICATES = "Certificates";
* XML_CONTAINER = "Container" : public static final String HANDLER_USER_CHALLENGE = "UserChallengeHandler";
*/ public static final String HANDLER_PERSISTENCE = "PersistenceHandler";
public static final String XML_CONTAINER = "Container"; public static final String HANDLER_ENCRYPTION = "EncryptionHandler";
public static final String HANDLER_PASSWORD_STRENGTH = "PasswordStrengthHandler";
/** public static final String HANDLER_SSO = "SsoHandler";
* XML_POLICIES = "Policies" : public static final String HANDLER_PRIVILEGE = "PrivilegeHandler";
*/ public static final String ROLES = "Roles";
public static final String XML_POLICIES = "Policies"; public static final String ROLE = "Role";
public static final String USERS = "Users";
/** public static final String GROUPS = "Groups";
* XML_PRIVILEGES = "Privileges" : public static final String GROUP = "Group";
*/ public static final String CERTIFICATE = "Certificate";
public static final String XML_PRIVILEGES = "Privileges"; public static final String SESSION_DATA = "SessionData";
public static final String USER = "User";
/** public static final String HISTORY = "History";
* XML_ROOT_PRIVILEGE_USERS_AND_ROLES = "UsersAndRoles" : public static final String FIRST_LOGIN = "FirstLogin";
*/ public static final String LAST_LOGIN = "LastLogin";
public static final String XML_ROOT_PRIVILEGE_USERS_AND_ROLES = "UsersAndRoles"; public static final String LAST_PASSWORD_CHANGE = "LastPasswordChange";
public static final String PASSWORD_CHANGE_REQUESTED = "PasswordChangeRequested";
/** public static final String PRIVILEGE = "Privilege";
* XML_ROOT_CERTIFICATES = "Certificates" : public static final String POLICY = "Policy";
*/ public static final String PARAMETERS = "Parameters";
public static final String XML_ROOT_CERTIFICATES = "Certificates"; public static final String PARAMETER = "Parameter";
public static final String PROPERTIES = "Properties";
/** public static final String PROPERTY = "Property";
* XML_HANDLER_USER_CHALLENGE = "UserChallengeHandler" : public static final String ALL_ALLOWED = "AllAllowed";
*/ public static final String DENY = "Deny";
public static final String XML_HANDLER_USER_CHALLENGE = "UserChallengeHandler"; public static final String ALLOW = "Allow";
public static final String FIRSTNAME = "Firstname";
/** public static final String LASTNAME = "Lastname";
* XML_HANDLER_PERSISTENCE = "PersistenceHandler" : public static final String STATE = "State";
*/ public static final String LOCALE = "Locale";
public static final String XML_HANDLER_PERSISTENCE = "PersistenceHandler"; public static final String ATTR_CLASS = "class";
public static final String ATTR_LOGIN_TIME = "loginTime";
/** public static final String ATTR_KEEP_ALIVE = "keepAlive";
* XML_HANDLER_ENCRYPTION = "EncryptionHandler" : public static final String ATTR_LAST_ACCESS = "lastAccess";
*/ public static final String ATTR_NAME = "name";
public static final String XML_HANDLER_ENCRYPTION = "EncryptionHandler"; public static final String ATTR_VALUE = "value";
public static final String ATTR_POLICY = "policy";
/** public static final String ATTR_USER_ID = "userId";
* XML_HANDLER_ENCRYPTION = "PasswordStrengthHandler" : public static final String ATTR_SESSION_ID = "sessionId";
*/ public static final String ATTR_USAGE = "usage";
public static final String XML_HANDLER_PASSWORD_STRENGTH = "PasswordStrengthHandler"; public static final String ATTR_USERNAME = "username";
public static final String ATTR_AUTH_TOKEN = "authToken";
/** public static final String ATTR_SOURCE = "source";
* XML_HANDLER_ENCRYPTION = "SsoHandler" : public static final String ATTR_LOCALE = "locale";
*/ public static final String ATTR_PASSWORD = "password";
public static final String XML_HANDLER_SSO = "SsoHandler"; public static final String ATTR_SALT = "salt";
public static final String PARAM_HASH_ALGORITHM = "hashAlgorithm";
/** public static final String PARAM_HASH_ALGORITHM_NON_SALT = "hashAlgorithmNonSalt";
* XML_HANDLER_PRIVILEGE = "PrivilegeHandler" : public static final String PARAM_HASH_ITERATIONS = "hashIterations";
*/ public static final String PARAM_HASH_KEY_LENGTH = "hashKeyLength";
public static final String XML_HANDLER_PRIVILEGE = "PrivilegeHandler"; public static final String PARAM_USERS_FILE = "usersXmlFile";
public static final String PARAM_USERS_FILE_DEF = "PrivilegeUsers.xml";
/** public static final String PARAM_GROUPS_FILE = "groupsXmlFile";
* XML_ROLES = "Roles" : public static final String PARAM_GROUPS_FILE_DEF = "PrivilegeGroups.xml";
*/ public static final String PARAM_ROLES_FILE = "rolesXmlFile";
public static final String XML_ROLES = "Roles"; public static final String PARAM_ROLES_FILE_DEF = "PrivilegeRoles.xml";
public static final String PARAM_BASE_PATH = "basePath";
/**
* XML_ROLE = "Role" :
*/
public static final String XML_ROLE = "Role";
/**
* XML_USERS = "Users" :
*/
public static final String XML_USERS = "Users";
/**
* XML_CERTIFICATE = "Certificate" :
*/
public static final String XML_CERTIFICATE = "Certificate";
/**
* XML_SESSION_DATA = "SessionData" :
*/
public static final String XML_SESSION_DATA = "SessionData";
/**
* XML_USER = "User"
*/
public static final String XML_USER = "User";
/**
* XML_USER = "User"
*/
public static final String XML_HISTORY = "History";
/**
* XML_USER = "User"
*/
public static final String XML_FIRST_LOGIN = "FirstLogin";
/**
* XML_USER = "User"
*/
public static final String XML_LAST_LOGIN = "LastLogin";
/**
* XML_USER = "User"
*/
public static final String XML_LAST_PASSWORD_CHANGE = "LastPasswordChange";
/**
* XML_USER = "User"
*/
public static final String XML_PASSWORD_CHANGE_REQUESTED = "PasswordChangeRequested";
/**
* XML_PRIVILEGE = "Privilege" :
*/
public static final String XML_PRIVILEGE = "Privilege";
/**
* XML_POLICY = "Policy" :
*/
public static final String XML_POLICY = "Policy";
/**
* XML_PARAMETERS = "Parameters" :
*/
public static final String XML_PARAMETERS = "Parameters";
/**
* XML_PARAMETER = "Parameter" :
*/
public static final String XML_PARAMETER = "Parameter";
/**
* XML_PROPERTIES = "Properties" :
*/
public static final String XML_PROPERTIES = "Properties";
/**
* XML_PROPERTY = "Property" :
*/
public static final String XML_PROPERTY = "Property";
/**
* XML_ALL_ALLOWED = "AllAllowed" :
*/
public static final String XML_ALL_ALLOWED = "AllAllowed";
/**
* XML_DENY = "Deny" :
*/
public static final String XML_DENY = "Deny";
/**
* XML_ALLOW = "Allow" :
*/
public static final String XML_ALLOW = "Allow";
/**
* XML_FIRSTNAME = "Firstname" :
*/
public static final String XML_FIRSTNAME = "Firstname";
/**
* XML_LASTNAME = "Lastname" :
*/
public static final String XML_LASTNAME = "Lastname";
/**
* XML_STATE = "State" :
*/
public static final String XML_STATE = "State";
/**
* XML_LOCALE = "Locale" :
*/
public static final String XML_LOCALE = "Locale";
/**
* XML_ATTR_CLASS = "class" :
*/
public static final String XML_ATTR_CLASS = "class";
/**
* XML_ATTR_LOGIN_TIME = "loginTime" :
*/
public static final String XML_ATTR_LOGIN_TIME = "loginTime";
/**
* XML_ATTR_KEEP_ALIVE = "keepAlive" :
*/
public static final String XML_ATTR_KEEP_ALIVE = "keepAlive";
/**
* XML_ATTR_LAST_ACCESS = "lastAccess" :
*/
public static final String XML_ATTR_LAST_ACCESS = "lastAccess";
/**
* XML_ATTR_NAME = "name" :
*/
public static final String XML_ATTR_NAME = "name";
/**
* XML_ATTR_VALUE = "value" :
*/
public static final String XML_ATTR_VALUE = "value";
/**
* XML_ATTR_POLICY = "policy" :
*/
public static final String XML_ATTR_POLICY = "policy";
/**
* XML_ATTR_USER_ID = "userId" :
*/
public static final String XML_ATTR_USER_ID = "userId";
/**
* XML_ATTR_SESSION_ID = "sessionId" :
*/
public static final String XML_ATTR_SESSION_ID = "sessionId";
/**
* XML_ATTR_SESSION_ID = "usage" :
*/
public static final String XML_ATTR_USAGE = "usage";
/**
* XML_ATTR_USERNAME = "username" :
*/
public static final String XML_ATTR_USERNAME = "username";
/**
* XML_ATTR_AUTH_TOKEN = "authToken" :
*/
public static final String XML_ATTR_AUTH_TOKEN = "authToken";
/**
* XML_ATTR_SOURCE = "source" :
*/
public static final String XML_ATTR_SOURCE = "source";
/**
* XML_ATTR_LOCALE = "locale" :
*/
public static final String XML_ATTR_LOCALE = "locale";
/**
* XML_ATTR_PASSWORD = "password" :
*/
public static final String XML_ATTR_PASSWORD = "password";
/**
* XML_ATTR_SALT = "salt" :
*/
public static final String XML_ATTR_SALT = "salt";
/**
* XML_PARAM_HASH_ALGORITHM = "hashAlgorithm" :
*/
public static final String XML_PARAM_HASH_ALGORITHM = "hashAlgorithm";
/**
* XML_PARAM_HASH_ALGORITHM_NON_SALT = "hashAlgorithmNonSalt" :
*/
public static final String XML_PARAM_HASH_ALGORITHM_NON_SALT = "hashAlgorithmNonSalt";
/**
* XML_PARAM_HASH_ALGORITHM = "hashAlgorithm" :
*/
public static final String XML_PARAM_HASH_ITERATIONS = "hashIterations";
/**
* XML_PARAM_HASH_ALGORITHM = "hashAlgorithm" :
*/
public static final String XML_PARAM_HASH_KEY_LENGTH = "hashKeyLength";
/**
* XML_PARAM_USERS_FILE = "usersXmlFile" :
*/
public static final String XML_PARAM_USERS_FILE = "usersXmlFile";
/**
* XML_PARAM_ROLES_FILE = "rolesXmlFile" :
*/
public static final String XML_PARAM_ROLES_FILE = "rolesXmlFile";
/**
* XML_PARAM_BASE_PATH = "basePath" :
*/
public static final String XML_PARAM_BASE_PATH = "basePath";
} }

View File

@ -0,0 +1,67 @@
package li.strolch.privilege.helper;
import javanet.staxutils.IndentingXMLStreamWriter;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static li.strolch.privilege.helper.XmlConstants.*;
public class XmlHelper {
public static void writeStringMapElement(XMLStreamWriter xmlWriter, Map<String, String> parameterMap,
String elementName, String valueElementName) throws XMLStreamException {
writeStringMapElement(xmlWriter, parameterMap, elementName, valueElementName, ATTR_VALUE);
}
public static void writeStringMapElement(XMLStreamWriter xmlWriter, Map<String, String> parameterMap,
String elementName, String valueElementName, String valueAttrName) throws XMLStreamException {
if (parameterMap == null || parameterMap.isEmpty())
return;
xmlWriter.writeStartElement(elementName);
List<String> propertyKeys = new ArrayList<>(parameterMap.keySet());
propertyKeys.sort(null);
for (String propertyKey : propertyKeys) {
xmlWriter.writeEmptyElement(valueElementName);
xmlWriter.writeAttribute(ATTR_NAME, propertyKey);
xmlWriter.writeAttribute(valueAttrName, parameterMap.get(propertyKey));
}
xmlWriter.writeEndElement();
}
public static void writeStringList(IndentingXMLStreamWriter xmlWriter, String elementName, Set<String> values)
throws XMLStreamException {
List<String> denyList = new ArrayList<>(values);
denyList.sort(null);
for (String value : denyList) {
writeStringElement(xmlWriter, elementName, value);
}
}
public static void writeStringElement(IndentingXMLStreamWriter xmlWriter, String elementName, String value)
throws XMLStreamException {
xmlWriter.writeStartElement(elementName);
xmlWriter.writeCharacters(value);
xmlWriter.writeEndElement();
}
public static IndentingXMLStreamWriter openXmlStreamWriterDocument(Writer ioWriter) throws XMLStreamException {
XMLOutputFactory factory = XMLOutputFactory.newInstance();
IndentingXMLStreamWriter xmlWriter = new IndentingXMLStreamWriter(factory.createXMLStreamWriter(ioWriter));
xmlWriter.setIndent(" ");
// create document root
xmlWriter.writeStartDocument(StandardCharsets.UTF_8.name(), "1.0");
return xmlWriter;
}
}

View File

@ -15,20 +15,18 @@
*/ */
package li.strolch.privilege.model; package li.strolch.privilege.model;
import static li.strolch.privilege.base.PrivilegeConstants.*; import li.strolch.privilege.base.PrivilegeConstants;
import li.strolch.privilege.handler.PrivilegeHandler;
import li.strolch.privilege.model.internal.User;
import li.strolch.utils.dbc.DBC;
import java.io.Serializable;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import java.util.Collections;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import li.strolch.privilege.base.PrivilegeConstants; import static li.strolch.privilege.base.PrivilegeConstants.*;
import li.strolch.privilege.base.PrivilegeException; import static li.strolch.utils.helper.StringHelper.isNotEmpty;
import li.strolch.privilege.handler.PrivilegeHandler;
import li.strolch.privilege.model.internal.User;
import li.strolch.utils.helper.StringHelper;
/** /**
* The {@link Certificate} is the object a client keeps when accessing a Privilege enabled system. This object is the * The {@link Certificate} is the object a client keeps when accessing a Privilege enabled system. This object is the
@ -37,7 +35,7 @@ import li.strolch.utils.helper.StringHelper;
* *
* @author Robert von Burg <eitch@eitchnet.ch> * @author Robert von Burg <eitch@eitchnet.ch>
*/ */
public final class Certificate implements Serializable { public final class Certificate {
private final Usage usage; private final Usage usage;
private final String sessionId; private final String sessionId;
@ -50,6 +48,7 @@ public final class Certificate implements Serializable {
private final ZonedDateTime loginTime; private final ZonedDateTime loginTime;
private final boolean keepAlive; private final boolean keepAlive;
private final Set<String> userGroups;
private final Set<String> userRoles; private final Set<String> userRoles;
private final Map<String, String> propertyMap; private final Map<String, String> propertyMap;
@ -64,49 +63,28 @@ public final class Certificate implements Serializable {
* by the {@link PrivilegeHandler} * by the {@link PrivilegeHandler}
* </p> * </p>
* *
* @param usage * @param usage the usage allowed for this certificate
* the usage allowed for this certificate * @param sessionId the users session id
* @param sessionId * @param username the users login name
* the users session id * @param firstName the users first name
* @param username * @param lastName the users last name
* the users login name * @param authToken the authentication token defining the users unique session and is a private field of this
* @param firstName * certificate.
* the users first name * @param locale the users {@link Locale}
* @param lastName * @param userRoles the user's roles
* the users last name * @param propertyMap a {@link Map} containing string value pairs of properties for the logged in user. These
* @param authToken * properties can be edited and can be used for the user to change settings of this session
* the authentication token defining the users unique session and is a private field of this certificate.
* @param locale
* the users {@link Locale}
* @param userRoles
* the user's roles
* @param propertyMap
* a {@link Map} containing string value pairs of properties for the logged in user. These properties can be
* edited and can be used for the user to change settings of this session
*/ */
public Certificate(Usage usage, String sessionId, String username, String firstName, String lastName, public Certificate(Usage usage, String sessionId, String username, String firstName, String lastName,
UserState userState, String authToken, String source, ZonedDateTime loginTime, boolean keepAlive, UserState userState, String authToken, String source, ZonedDateTime loginTime, boolean keepAlive,
Locale locale, Set<String> userRoles, Map<String, String> propertyMap) { Locale locale, Set<String> userGroups, Set<String> userRoles, Map<String, String> propertyMap) {
// validate arguments are not null DBC.PRE.assertNotEmpty("sessionId must not be empty", sessionId);
if (StringHelper.isEmpty(sessionId)) { DBC.PRE.assertNotEmpty("username must not be empty", username);
throw new PrivilegeException("sessionId is null!"); DBC.PRE.assertNotEmpty("authToken must not be empty", authToken);
} DBC.PRE.assertNotNull("userState must not be empty", userState);
if (StringHelper.isEmpty(username)) { DBC.PRE.assertNotNull("usage must not be empty", usage);
throw new PrivilegeException("username is null!"); DBC.PRE.assertNotNull("source must not be null", source);
}
if (StringHelper.isEmpty(authToken)) {
throw new PrivilegeException("authToken is null!");
}
if (userState == null) {
throw new PrivilegeException("userState is null!");
}
if (usage == null) {
throw new PrivilegeException("usage is null!");
}
if (source == null) {
throw new PrivilegeException("source is null!");
}
this.usage = usage; this.usage = usage;
this.sessionId = sessionId; this.sessionId = sessionId;
@ -126,11 +104,12 @@ public final class Certificate implements Serializable {
this.locale = locale; this.locale = locale;
if (propertyMap == null) if (propertyMap == null)
this.propertyMap = Collections.emptyMap(); this.propertyMap = Map.of();
else else
this.propertyMap = Collections.unmodifiableMap(propertyMap); this.propertyMap = Map.copyOf(propertyMap);
this.userRoles = Collections.unmodifiableSet(userRoles); this.userGroups = Set.copyOf(userGroups);
this.userRoles = Set.copyOf(userRoles);
this.lastAccess = ZonedDateTime.now(); this.lastAccess = ZonedDateTime.now();
} }
@ -150,15 +129,29 @@ public final class Certificate implements Serializable {
return this.usage; return this.usage;
} }
public Set<String> getUserGroups() {
return this.userGroups;
}
public Set<String> getUserRoles() { public Set<String> getUserRoles() {
return this.userRoles; return this.userRoles;
} }
/**
* Returns true if the user of this certificate has the given group
*
* @param group the group to check for
*
* @return true if the user of this certificate has the given group
*/
public boolean hasGroup(String group) {
return this.userGroups.contains(group);
}
/** /**
* Returns true if the user of this certificate has the given role * Returns true if the user of this certificate has the given role
* *
* @param role * @param role the role to check for
* the role to check for
* *
* @return true if the user of this certificate has the given role * @return true if the user of this certificate has the given role
*/ */
@ -178,8 +171,7 @@ public final class Certificate implements Serializable {
/** /**
* Returns the property with the given key * Returns the property with the given key
* *
* @param key * @param key the key for which the property is to be returned
* the key for which the property is to be returned
* *
* @return the value of the property with the given key, or null if it does not exist * @return the value of the property with the given key, or null if it does not exist
*/ */
@ -283,12 +275,12 @@ public final class Certificate implements Serializable {
builder.append(", username="); builder.append(", username=");
builder.append(this.username); builder.append(this.username);
if (StringHelper.isNotEmpty(this.firstname)) { if (isNotEmpty(this.firstname)) {
builder.append(", firstname="); builder.append(", firstname=");
builder.append(this.firstname); builder.append(this.firstname);
} }
if (StringHelper.isNotEmpty(this.lastname)) { if (isNotEmpty(this.lastname)) {
builder.append(", lastname="); builder.append(", lastname=");
builder.append(this.lastname); builder.append(this.lastname);
} }

View File

@ -1,85 +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.model;
import java.util.Set;
import li.strolch.privilege.model.internal.Role;
import li.strolch.privilege.policy.PrivilegePolicy;
/**
* <p>
* {@link IPrivilege} is the main model object for Privilege. A {@link Role} has a set of Privileges assigned to it
* which defines the privileges a logged in user with that role has. If the {@link IPrivilege} has a {@link
* PrivilegePolicy} defined, then that policy will be used for finer granularity and with the deny and allow lists
* configured which is used to evaluate if privilege is granted to a {@link Restrictable}
* </p>
*
* @author Robert von Burg <eitch@eitchnet.ch>
*/
public interface IPrivilege {
/**
* @return a {@link PrivilegeRep} which is a representation of this object used to serialize and view on clients
*/
PrivilegeRep asPrivilegeRep();
/**
* @return the name
*/
String getName();
/**
* @return the policy
*/
String getPolicy();
/**
* @return the allAllowed
*/
boolean isAllAllowed();
/**
* @return the allowList
*/
Set<String> getAllowList();
/**
* @return the denyList
*/
Set<String> getDenyList();
/**
* @return true if there are values in the allow list
*/
boolean hasAllowed();
/**
* @return if the value is in the allow list
*/
boolean isAllowed(String value);
/**
* @return true if there are values in the deny list
*/
boolean hasDenied();
/**
* @return true if the value is in the deny list
*/
boolean isDenied(String value);
}

View File

@ -0,0 +1,140 @@
/*
* 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.model;
import li.strolch.privilege.handler.PrivilegeHandler;
import li.strolch.privilege.model.internal.Role;
import li.strolch.privilege.policy.PrivilegePolicy;
import li.strolch.utils.dbc.DBC;
import java.util.HashSet;
import java.util.Set;
/**
* <p>
* {@link Privilege} is the main model object for Privilege. A {@link Role} has a set of Privileges assigned to it which
* defines the privileges a logged in user with that role has. If the {@link Privilege} has a {@link PrivilegePolicy}
* defined, then that policy will be used for finer granularity and with the deny and allow lists configured which is
* used to evaluate if privilege is granted to a {@link Restrictable}
* </p>
*
* @param name the name of this privilege, which is unique to all privileges known in the
* {@link PrivilegeHandler}
* @param policy the {@link PrivilegePolicy} configured to evaluate if the privilege is granted. If null, then
* privilege is granted
* @param allAllowed a boolean defining if a {@link Role} with this {@link Privilege} has unrestricted access to a
* {@link Restrictable} in which case the deny and allow lists are ignored and can be null
* @param denyList a list of deny rules for this {@link Privilege}, can be null if all allowed
* @param allowList a list of allow rules for this {@link Privilege}, can be null if all allowed
*
* @author Robert von Burg <eitch@eitchnet.ch>
*/
public record Privilege(String name, String policy, boolean allAllowed, Set<String> denyList, Set<String> allowList) {
public Privilege(String name, String policy, boolean allAllowed, Set<String> denyList, Set<String> allowList) {
DBC.PRE.assertNotEmpty("name must not be empty", name);
DBC.PRE.assertNotEmpty("policy must not be empty", policy);
DBC.PRE.assertNotNull("denyList must not be null", denyList);
DBC.PRE.assertNotNull("allowList must not be null", allowList);
this.name = name;
this.allAllowed = allAllowed;
this.policy = policy;
this.denyList = Set.copyOf(denyList);
this.allowList = Set.copyOf(allowList);
}
/**
* @return a {@link PrivilegeRep} which is a representation of this object used to serialize and view on clients
*/
public PrivilegeRep asPrivilegeRep() {
return new PrivilegeRep(this.name, this.policy, this.allAllowed, new HashSet<>(this.denyList),
new HashSet<>(this.allowList));
}
public String getName() {
return this.name;
}
public String getPolicy() {
return this.policy;
}
public boolean isAllAllowed() {
return this.allAllowed;
}
public Set<String> getAllowList() {
return this.allowList;
}
public Set<String> getDenyList() {
return this.denyList;
}
public boolean hasAllowed() {
return !this.allowList.isEmpty();
}
public boolean isAllowed(String value) {
return this.allowList.contains(value);
}
public boolean hasDenied() {
return !this.allowList.isEmpty();
}
public boolean isDenied(String value) {
return this.denyList.contains(value);
}
@Override
public String toString() {
return "Privilege [name=" + this.name + ", policy=" + this.policy + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((this.name == null) ? 0 : this.name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Privilege other = (Privilege) obj;
if (this.name == null)
return other.name == null;
return this.name.equals(other.name);
}
/**
* Constructs a {@link Privilege} from the {@link PrivilegeRep}
*
* @param privilegeRep the {@link PrivilegeRep} from which to create the {@link Privilege}
*/
public static Privilege of(PrivilegeRep privilegeRep) {
return new Privilege(privilegeRep.getName(), privilegeRep.getPolicy(), privilegeRep.isAllAllowed(),
privilegeRep.getDenyList(), privilegeRep.getAllowList());
}
}

View File

@ -15,14 +15,16 @@
*/ */
package li.strolch.privilege.model; package li.strolch.privilege.model;
import java.text.MessageFormat; import li.strolch.privilege.base.AccessDeniedException;
import li.strolch.privilege.base.PrivilegeException;
import li.strolch.privilege.policy.PrivilegePolicy;
import li.strolch.utils.dbc.DBC;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import li.strolch.privilege.base.AccessDeniedException; import static java.text.MessageFormat.format;
import li.strolch.privilege.base.PrivilegeException; import static li.strolch.privilege.i18n.PrivilegeMessages.getString;
import li.strolch.privilege.i18n.PrivilegeMessages;
import li.strolch.privilege.policy.PrivilegePolicy;
/** /**
* <p> * <p>
@ -36,19 +38,15 @@ import li.strolch.privilege.policy.PrivilegePolicy;
* *
* @author Robert von Burg <eitch@eitchnet.ch> * @author Robert von Burg <eitch@eitchnet.ch>
*/ */
public class PrivilegeContext { public record PrivilegeContext(UserRep userRep, Certificate certificate, Map<String, Privilege> privileges,
Map<String, PrivilegePolicy> policies) {
// public PrivilegeContext(UserRep userRep, Certificate certificate, Map<String, Privilege> privileges,
// object state
//
private final UserRep userRep;
private final Certificate certificate;
private final Map<String, IPrivilege> privileges;
private final Map<String, PrivilegePolicy> policies;
public PrivilegeContext(UserRep userRep, Certificate certificate, Map<String, IPrivilege> privileges,
Map<String, PrivilegePolicy> policies) { Map<String, PrivilegePolicy> policies) {
DBC.PRE.assertNotNull("userRep must not be null", userRep);
DBC.PRE.assertNotNull("certificate must not be null", certificate);
DBC.PRE.assertNotNull("privileges must not be null", privileges);
DBC.PRE.assertNotNull("policies must not be null", policies);
this.userRep = userRep; this.userRep = userRep;
this.certificate = certificate; this.certificate = certificate;
this.privileges = Map.copyOf(privileges); this.privileges = Map.copyOf(privileges);
@ -85,35 +83,64 @@ public class PrivilegeContext {
public void assertHasPrivilege(String privilegeName) throws AccessDeniedException { public void assertHasPrivilege(String privilegeName) throws AccessDeniedException {
if (!this.privileges.containsKey(privilegeName)) { if (!this.privileges.containsKey(privilegeName)) {
String msg = MessageFormat.format(PrivilegeMessages.getString("Privilege.noprivilege.user"), String msg = format(getString("Privilege.noprivilege.user"), userRep.getUsername(), privilegeName);
userRep.getUsername(), privilegeName);
throw new AccessDeniedException(msg); throw new AccessDeniedException(msg);
} }
} }
public boolean hasGroup(String groupName) {
return this.userRep.hasGroup(groupName);
}
public boolean hasRole(String roleName) { public boolean hasRole(String roleName) {
return this.userRep.hasRole(roleName); return this.userRep.hasRole(roleName);
} }
public void assertHasRole(String roleName) throws AccessDeniedException { public void assertHasGroup(String groupName) throws AccessDeniedException {
if (!this.userRep.hasRole(roleName)) { if (!this.userRep.hasGroup(groupName)) {
String msg = MessageFormat.format(PrivilegeMessages.getString("Privilege.noprivilege.role"), String msg = format(getString("Privilege.noprivilege.group"), userRep.getUsername(), groupName);
userRep.getUsername(), roleName);
throw new AccessDeniedException(msg); throw new AccessDeniedException(msg);
} }
} }
public void assertHasRole(String roleName) throws AccessDeniedException {
if (!this.userRep.hasRole(roleName)) {
String msg = format(getString("Privilege.noprivilege.role"), userRep.getUsername(), roleName);
throw new AccessDeniedException(msg);
}
}
public void assertHasAnyGroup(String... groupNames) throws AccessDeniedException {
for (String groupName : groupNames) {
if (this.userRep.hasGroup(groupName))
return;
}
String msg = format(getString("Privilege.noprivilege.group"), userRep.getUsername(),
String.join(", ", groupNames));
throw new AccessDeniedException(msg);
}
public void assertHasAnyRole(String... roleNames) throws AccessDeniedException { public void assertHasAnyRole(String... roleNames) throws AccessDeniedException {
for (String roleName : roleNames) { for (String roleName : roleNames) {
if (this.userRep.hasRole(roleName)) if (this.userRep.hasRole(roleName))
return; return;
} }
String msg = MessageFormat.format(PrivilegeMessages.getString("Privilege.noprivilege.role"), String msg = format(getString("Privilege.noprivilege.role"), userRep.getUsername(),
userRep.getUsername(), String.join(", ", roleNames)); String.join(", ", roleNames));
throw new AccessDeniedException(msg); throw new AccessDeniedException(msg);
} }
public boolean hasAnyGroup(String... groupNames) throws AccessDeniedException {
for (String groupName : groupNames) {
if (this.userRep.hasGroup(groupName))
return true;
}
return false;
}
public boolean hasAnyRole(String... roleNames) throws AccessDeniedException { public boolean hasAnyRole(String... roleNames) throws AccessDeniedException {
for (String roleName : roleNames) { for (String roleName : roleNames) {
if (this.userRep.hasRole(roleName)) if (this.userRep.hasRole(roleName))
@ -123,7 +150,7 @@ public class PrivilegeContext {
return false; return false;
} }
public IPrivilege getPrivilege(String privilegeName) throws AccessDeniedException { public Privilege getPrivilege(String privilegeName) throws AccessDeniedException {
assertHasPrivilege(privilegeName); assertHasPrivilege(privilegeName);
return this.privileges.get(privilegeName); return this.privileges.get(privilegeName);
} }
@ -132,7 +159,7 @@ public class PrivilegeContext {
PrivilegePolicy policy = this.policies.get(policyName); PrivilegePolicy policy = this.policies.get(policyName);
if (policy == null) { if (policy == null) {
String msg = "The PrivilegePolicy {0} does not exist on the PrivilegeContext!"; String msg = "The PrivilegePolicy {0} does not exist on the PrivilegeContext!";
throw new PrivilegeException(MessageFormat.format(msg, policyName)); throw new PrivilegeException(format(msg, policyName));
} }
return policy; return policy;
} }
@ -148,15 +175,12 @@ public class PrivilegeContext {
* *
* <p>This method uses the {@link SimpleRestrictable} to verify access</p> * <p>This method uses the {@link SimpleRestrictable} to verify access</p>
* *
* @param privilegeName * @param privilegeName the name of the privilege to verify
* the name of the privilege to verify * @param privilegeValue the value
* @param privilegeValue
* the value
* *
* @throws AccessDeniedException * @throws AccessDeniedException if the user does not have access
* if the user does not have access * @throws PrivilegeException if there is an internal error due to wrongly configured privileges or programming
* @throws PrivilegeException * errors
* if there is an internal error due to wrongly configured privileges or programming errors
*/ */
public void validateAction(String privilegeName, String privilegeValue) public void validateAction(String privilegeName, String privilegeValue)
throws PrivilegeException, AccessDeniedException { throws PrivilegeException, AccessDeniedException {
@ -168,22 +192,20 @@ public class PrivilegeContext {
* has the privilege, then this method returns with no exception and void, if the user does not have the privilege, * has the privilege, then this method returns with no exception and void, if the user does not have the privilege,
* then a {@link AccessDeniedException} is thrown. * then a {@link AccessDeniedException} is thrown.
* *
* @param restrictable * @param restrictable the {@link Restrictable} which the user wants to access
* the {@link Restrictable} which the user wants to access
* *
* @throws AccessDeniedException * @throws AccessDeniedException if the user does not have access
* if the user does not have access * @throws PrivilegeException if there is an internal error due to wrongly configured privileges or programming
* @throws PrivilegeException * errors
* if there is an internal error due to wrongly configured privileges or programming errors
*/ */
public void validateAction(Restrictable restrictable) throws PrivilegeException, AccessDeniedException { public void validateAction(Restrictable restrictable) throws PrivilegeException, AccessDeniedException {
// the privilege for the restrictable // the privilege for the restrictable
String privilegeName = restrictable.getPrivilegeName(); String privilegeName = restrictable.getPrivilegeName();
IPrivilege privilege = this.privileges.get(privilegeName); Privilege privilege = this.privileges.get(privilegeName);
if (privilege == null) { if (privilege == null) {
String msg = MessageFormat.format(PrivilegeMessages.getString("Privilege.accessdenied.noprivilege"), String msg = format(getString("Privilege.accessdenied.noprivilege"), getUsername(), privilegeName,
getUsername(), privilegeName, restrictable.getClass().getName(), restrictable.getPrivilegeValue()); restrictable.getClass().getName(), restrictable.getPrivilegeValue());
throw new AccessDeniedException(msg); throw new AccessDeniedException(msg);
} }
@ -199,19 +221,18 @@ public class PrivilegeContext {
* Validates if the user for this context has the privilege to access to the given {@link Restrictable}. Returning * Validates if the user for this context has the privilege to access to the given {@link Restrictable}. Returning
* true if the user has the privilege, and false if not * true if the user has the privilege, and false if not
* *
* @param restrictable * @param restrictable the {@link Restrictable} which the user wants to access
* the {@link Restrictable} which the user wants to access
* *
* @return returns true if the user has the privilege, and false if not * @return returns true if the user has the privilege, and false if not
* *
* @throws PrivilegeException * @throws PrivilegeException if there is an internal error due to wrongly configured privileges or programming
* if there is an internal error due to wrongly configured privileges or programming errors * errors
*/ */
public boolean hasPrivilege(Restrictable restrictable) throws PrivilegeException { public boolean hasPrivilege(Restrictable restrictable) throws PrivilegeException {
// the privilege for the restrictable // the privilege for the restrictable
String privilegeName = restrictable.getPrivilegeName(); String privilegeName = restrictable.getPrivilegeName();
IPrivilege privilege = this.privileges.get(privilegeName); Privilege privilege = this.privileges.get(privilegeName);
if (privilege == null) if (privilege == null)
return false; return false;
@ -229,15 +250,13 @@ public class PrivilegeContext {
* *
* <p>This method uses the {@link SimpleRestrictable} to verify access</p> * <p>This method uses the {@link SimpleRestrictable} to verify access</p>
* *
* @param privilegeName * @param privilegeName the name of the privilege to verify
* the name of the privilege to verify * @param privilegeValue the value
* @param privilegeValue
* the value
* *
* @return returns true if the user has the privilege, and false if not * @return returns true if the user has the privilege, and false if not
* *
* @throws PrivilegeException * @throws PrivilegeException if there is an internal error due to wrongly configured privileges or programming
* if there is an internal error due to wrongly configured privileges or programming errors * errors
*/ */
public boolean hasPrivilege(String privilegeName, String privilegeValue) throws PrivilegeException { public boolean hasPrivilege(String privilegeName, String privilegeValue) throws PrivilegeException {
return hasPrivilege(new SimpleRestrictable(privilegeName, privilegeValue)); return hasPrivilege(new SimpleRestrictable(privilegeName, privilegeValue));

View File

@ -15,27 +15,25 @@
*/ */
package li.strolch.privilege.model; package li.strolch.privilege.model;
import static li.strolch.utils.helper.StringHelper.trimOrEmpty;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
import li.strolch.privilege.base.PrivilegeException; import li.strolch.privilege.base.PrivilegeException;
import li.strolch.privilege.handler.PrivilegeHandler; import li.strolch.privilege.handler.PrivilegeHandler;
import li.strolch.privilege.model.internal.Role; import li.strolch.privilege.model.internal.Role;
import li.strolch.privilege.policy.PrivilegePolicy; import li.strolch.privilege.policy.PrivilegePolicy;
import li.strolch.utils.helper.StringHelper;
import java.util.HashSet;
import java.util.Set;
import static li.strolch.utils.helper.StringHelper.isEmpty;
import static li.strolch.utils.helper.StringHelper.trimOrEmpty;
/** /**
* To keep certain details of the {@link IPrivilege} itself hidden from remote clients and make sure instances are only * To keep certain details of the {@link Privilege} itself hidden from remote clients and make sure instances are only
* edited by users with the correct privilege, this representational version is allowed to be viewed by remote clients * edited by users with the correct privilege, this representational version is allowed to be viewed by remote clients
* and simply wraps all public data from the {@link IPrivilege} * and simply wraps all public data from the {@link Privilege}
* *
* @author Robert von Burg <eitch@eitchnet.ch> * @author Robert von Burg <eitch@eitchnet.ch>
*/ */
public class PrivilegeRep implements Serializable { public class PrivilegeRep {
private String name; private String name;
private String policy; private String policy;
@ -43,48 +41,57 @@ public class PrivilegeRep implements Serializable {
private Set<String> denyList; private Set<String> denyList;
private Set<String> allowList; private Set<String> allowList;
private boolean readOnly;
/** /**
* Default constructor * Default constructor
* *
* @param name * @param name the name of this privilege, which is unique to all privileges known in the
* the name of this privilege, which is unique to all privileges known in the {@link PrivilegeHandler} * {@link PrivilegeHandler}
* @param policy * @param policy the {@link PrivilegePolicy} configured to evaluate if the privilege is granted
* the {@link PrivilegePolicy} configured to evaluate if the privilege is granted * @param allAllowed a boolean defining if a {@link Role} with this {@link Privilege} has unrestricted access to a
* @param allAllowed * {@link Restrictable}
* a boolean defining if a {@link Role} with this {@link IPrivilege} has unrestricted access to a {@link * @param denyList a list of deny rules for this {@link Privilege}
* Restrictable} * @param allowList a list of allow rules for this {@link Privilege}
* @param denyList
* a list of deny rules for this {@link IPrivilege}
* @param allowList
* a list of allow rules for this {@link IPrivilege}
*/ */
public PrivilegeRep(String name, String policy, boolean allAllowed, Set<String> denyList, Set<String> allowList) { public PrivilegeRep(String name, String policy, boolean allAllowed, Set<String> denyList, Set<String> allowList) {
this.name = trimOrEmpty(name); this.name = trimOrEmpty(name);
this.policy = trimOrEmpty(policy); this.policy = trimOrEmpty(policy);
this.allAllowed = allAllowed; this.allAllowed = allAllowed;
this.denyList = denyList; setDenyList(denyList == null ? Set.of() : denyList);
this.allowList = allowList; setAllowList(allowList == null ? Set.of() : allowList);
}
public boolean isReadOnly() {
return readOnly;
}
public PrivilegeRep readOnly() {
if (this.readOnly)
return this;
this.readOnly = true;
this.denyList = Set.copyOf(this.denyList);
this.allowList = Set.copyOf(this.allowList);
return this;
}
protected void assertNotReadonly() {
if (this.readOnly)
throw new IllegalStateException("Privilege is currently readOnly, to modify get a copy!");
} }
/** /**
* Validates that all required fields are set * Validates that all required fields are set
*/ */
public void validate() { public void validate() {
if (isEmpty(this.name))
if (StringHelper.isEmpty(this.name)) {
throw new PrivilegeException("No name defined!"); throw new PrivilegeException("No name defined!");
} if (isEmpty(this.policy))
throw new PrivilegeException("No policy defined!");
if (StringHelper.isEmpty(this.policy)) { if (this.denyList == null)
throw new PrivilegeException("policy is null!");
}
if (this.denyList == null) {
throw new PrivilegeException("denyList is null"); throw new PrivilegeException("denyList is null");
} if (this.allowList == null)
if (this.allowList == null) {
throw new PrivilegeException("allowList is null"); throw new PrivilegeException("allowList is null");
}
} }
/** /**
@ -95,10 +102,10 @@ public class PrivilegeRep implements Serializable {
} }
/** /**
* @param name * @param name the name to set
* the name to set
*/ */
public void setName(String name) { public void setName(String name) {
assertNotReadonly();
this.name = trimOrEmpty(name); this.name = trimOrEmpty(name);
} }
@ -110,10 +117,10 @@ public class PrivilegeRep implements Serializable {
} }
/** /**
* @param policy * @param policy the policy to set
* the policy to set
*/ */
public void setPolicy(String policy) { public void setPolicy(String policy) {
assertNotReadonly();
this.policy = trimOrEmpty(policy); this.policy = trimOrEmpty(policy);
} }
@ -125,10 +132,10 @@ public class PrivilegeRep implements Serializable {
} }
/** /**
* @param allAllowed * @param allAllowed the allAllowed to set
* the allAllowed to set
*/ */
public void setAllAllowed(boolean allAllowed) { public void setAllAllowed(boolean allAllowed) {
assertNotReadonly();
this.allAllowed = allAllowed; this.allAllowed = allAllowed;
} }
@ -136,30 +143,30 @@ public class PrivilegeRep implements Serializable {
* @return the denyList * @return the denyList
*/ */
public Set<String> getDenyList() { public Set<String> getDenyList() {
return this.denyList == null ? new HashSet<>() : this.denyList; return this.denyList;
} }
/** /**
* @param denyList * @param denyList the denyList to set
* the denyList to set
*/ */
public void setDenyList(Set<String> denyList) { public void setDenyList(Set<String> denyList) {
this.denyList = denyList.stream().map(String::trim).collect(Collectors.toSet()); assertNotReadonly();
this.denyList = denyList.stream().map(String::trim).collect(HashSet::new, HashSet::add, HashSet::addAll);
} }
/** /**
* @return the allowList * @return the allowList
*/ */
public Set<String> getAllowList() { public Set<String> getAllowList() {
return this.allowList == null ? new HashSet<>() : this.allowList; return this.allowList;
} }
/** /**
* @param allowList * @param allowList the allowList to set
* the allowList to set
*/ */
public void setAllowList(Set<String> allowList) { public void setAllowList(Set<String> allowList) {
this.allowList = allowList.stream().map(String::trim).collect(Collectors.toSet()); assertNotReadonly();
this.allowList = allowList.stream().map(String::trim).collect(HashSet::new, HashSet::add, HashSet::addAll);
} }
/** /**
@ -169,11 +176,8 @@ public class PrivilegeRep implements Serializable {
*/ */
@Override @Override
public String toString() { public String toString() {
return "PrivilegeRep [name=" + this.name + ", policy=" + this.policy + ", allAllowed=" + this.allAllowed return "PrivilegeRep [name=" + this.name + ", policy=" + this.policy + ", allAllowed=" + this.allAllowed +
+ ", denyList=" + (this.denyList == null ? "null" : this.denyList.size()) + ", allowList=" + ( ", denyList=" + this.denyList.size() + ", allowList=" + this.allowList.size() + "]";
this.allowList == null ?
"null" :
this.allowList.size()) + "]";
} }
@Override @Override
@ -193,10 +197,13 @@ public class PrivilegeRep implements Serializable {
if (getClass() != obj.getClass()) if (getClass() != obj.getClass())
return false; return false;
PrivilegeRep other = (PrivilegeRep) obj; PrivilegeRep other = (PrivilegeRep) obj;
if (this.name == null) { if (this.name == null)
return other.name == null; return other.name == null;
} else return this.name.equals(other.name);
return this.name.equals(other.name); }
public PrivilegeRep getCopy() {
return new PrivilegeRep(this.name, this.policy, this.allAllowed, this.denyList, this.allowList);
} }
public <T> T accept(PrivilegeElementVisitor<T> visitor) { public <T> T accept(PrivilegeElementVisitor<T> visitor) {

View File

@ -21,7 +21,7 @@ import li.strolch.privilege.policy.PrivilegePolicy;
* <p> * <p>
* Objects implementing this interface are used to grant/restrict privileges to them. A {@link PrivilegePolicy} * Objects implementing this interface are used to grant/restrict privileges to them. A {@link PrivilegePolicy}
* implements the logic on granting/restricting privileges for a {@link Restrictable} and the {@link * implements the logic on granting/restricting privileges for a {@link Restrictable} and the {@link
* #getPrivilegeName()} is used to find the {@link IPrivilege} which has the associated {@link PrivilegePolicy} for * #getPrivilegeName()} is used to find the {@link Privilege} which has the associated {@link PrivilegePolicy} for
* evaluating access * evaluating access
* </p> * </p>
* *
@ -30,9 +30,9 @@ import li.strolch.privilege.policy.PrivilegePolicy;
public interface Restrictable { public interface Restrictable {
/** /**
* Returns the name of the {@link IPrivilege} which is to be used to validate privileges against * Returns the name of the {@link Privilege} which is to be used to validate privileges against
* *
* @return the name of the {@link IPrivilege} which is to be used to validate privileges against * @return the name of the {@link Privilege} which is to be used to validate privileges against
*/ */
String getPrivilegeName(); String getPrivilegeName();

View File

@ -15,16 +15,16 @@
*/ */
package li.strolch.privilege.model; package li.strolch.privilege.model;
import static li.strolch.utils.helper.StringHelper.trimOrEmpty;
import java.io.Serializable;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import li.strolch.privilege.base.PrivilegeException; import li.strolch.privilege.base.PrivilegeException;
import li.strolch.privilege.model.internal.Role; import li.strolch.privilege.model.internal.Role;
import li.strolch.utils.helper.StringHelper; import li.strolch.utils.dbc.DBC;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;
import static li.strolch.utils.helper.StringHelper.isEmpty;
import static li.strolch.utils.helper.StringHelper.trimOrEmpty;
/** /**
* To keep certain details of the {@link Role} itself hidden from remote clients and make sure instances are only edited * To keep certain details of the {@link Role} itself hidden from remote clients and make sure instances are only edited
@ -33,40 +33,55 @@ import li.strolch.utils.helper.StringHelper;
* *
* @author Robert von Burg <eitch@eitchnet.ch> * @author Robert von Burg <eitch@eitchnet.ch>
*/ */
public class RoleRep implements Serializable { public class RoleRep {
private String name; private String name;
private List<PrivilegeRep> privileges; private Map<String, PrivilegeRep> privileges;
private boolean readOnly;
/** /**
* Default constructor * Default constructor
* *
* @param name * @param name the name of this role
* the name of this role * @param privileges the list of privileges granted to this role
* @param privileges
* the list of privileges granted to this role
*/ */
public RoleRep(String name, List<PrivilegeRep> privileges) { public RoleRep(String name, Map<String, PrivilegeRep> privileges) {
this.name = trimOrEmpty(name); this.name = trimOrEmpty(name);
this.privileges = privileges; setPrivileges(privileges == null ? Map.of() : privileges);
}
public boolean isReadOnly() {
return readOnly;
}
public RoleRep readOnly() {
if (this.readOnly)
return this;
this.readOnly = true;
this.privileges = Map.copyOf(this.privileges);
return this;
}
protected void assertNotReadonly() {
if (this.readOnly)
throw new IllegalStateException("Role is currently readOnly, to modify get a copy!");
} }
/** /**
* validates that all required fields are set * validates that all required fields are set
*/ */
public void validate() { public void validate() {
if (StringHelper.isEmpty(this.name)) if (isEmpty(this.name))
throw new PrivilegeException("name is null"); throw new PrivilegeException("name is null");
if (this.privileges != null && !this.privileges.isEmpty()) { for (PrivilegeRep privilege : this.privileges.values()) {
for (PrivilegeRep privilege : this.privileges) { try {
try { privilege.validate();
privilege.validate(); } catch (Exception e) {
} catch (Exception e) { String msg = "Privilege {0} is invalid on role {1}";
String msg = "Privilege {0} is invalid on role {1}"; msg = MessageFormat.format(msg, privilege.getName(), this.name);
msg = MessageFormat.format(msg, privilege.getName(), this.name); throw new PrivilegeException(msg, e);
throw new PrivilegeException(msg, e);
}
} }
} }
} }
@ -79,30 +94,29 @@ public class RoleRep implements Serializable {
} }
/** /**
* @param name * @param name the name to set
* the name to set
*/ */
public void setName(String name) { public void setName(String name) {
assertNotReadonly();
this.name = trimOrEmpty(name); this.name = trimOrEmpty(name);
} }
/** public Map<String, PrivilegeRep> getPrivileges() {
* Returns the privileges assigned to this Role as a list if (this.privileges == null)
* return null;
* @return the privileges assigned to this Role as a list return this.privileges;
*/
public List<PrivilegeRep> getPrivileges() {
return this.privileges == null ? new ArrayList<>() : this.privileges;
} }
/** public void setPrivileges(Map<String, PrivilegeRep> privileges) {
* Sets the privileges on this from a list assertNotReadonly();
* DBC.PRE.assertNotNull("privileges must not be null!", privileges);
* @param privileges this.privileges = new HashMap<>(privileges);
* the list of privileges to assign to this role }
*/
public void setPrivileges(List<PrivilegeRep> privileges) { public void addPrivilege(PrivilegeRep privilegeRep) {
this.privileges = privileges; DBC.PRE.assertFalse(() -> "Privilege " + privilegeRep.getName() + " already on role " + this.name,
privileges.containsKey(privilegeRep.getName()));
this.privileges.put(privilegeRep.getName(), privilegeRep);
} }
/** /**
@ -112,8 +126,7 @@ public class RoleRep implements Serializable {
*/ */
@Override @Override
public String toString() { public String toString() {
return "RoleRep [name=" + this.name + ", privilegeMap=" + (this.privileges == null ? "null" : this.privileges) return "RoleRep [name=" + this.name + ", privilegeMap=" + this.privileges + "]";
+ "]";
} }
@Override @Override
@ -133,10 +146,13 @@ public class RoleRep implements Serializable {
if (getClass() != obj.getClass()) if (getClass() != obj.getClass())
return false; return false;
RoleRep other = (RoleRep) obj; RoleRep other = (RoleRep) obj;
if (this.name == null) { if (this.name == null)
return other.name == null; return other.name == null;
} else return this.name.equals(other.name);
return this.name.equals(other.name); }
public RoleRep getCopy() {
return new RoleRep(this.name, this.privileges);
} }
public <T> T accept(PrivilegeElementVisitor<T> visitor) { public <T> T accept(PrivilegeElementVisitor<T> visitor) {

View File

@ -26,10 +26,8 @@ public class SimpleRestrictable implements Restrictable {
private final Object value; private final Object value;
/** /**
* @param name * @param name the name of the privilege
* the name of the privilege * @param value the value allowed on the privilege
* @param value
* the value allowed on the privilege
*/ */
public SimpleRestrictable(String name, Object value) { public SimpleRestrictable(String name, Object value) {
DBC.PRE.assertNotEmpty("name must not be empty", name); DBC.PRE.assertNotEmpty("name must not be empty", name);

View File

@ -15,20 +15,19 @@
*/ */
package li.strolch.privilege.model; package li.strolch.privilege.model;
import static li.strolch.privilege.base.PrivilegeConstants.*;
import static li.strolch.utils.helper.StringHelper.trimOrEmpty;
import java.io.Serializable;
import java.text.MessageFormat;
import java.util.*;
import java.util.stream.Collectors;
import li.strolch.privilege.base.PrivilegeConstants; import li.strolch.privilege.base.PrivilegeConstants;
import li.strolch.privilege.base.PrivilegeException; import li.strolch.privilege.base.PrivilegeException;
import li.strolch.privilege.model.internal.Role; import li.strolch.privilege.model.internal.Role;
import li.strolch.privilege.model.internal.User; import li.strolch.privilege.model.internal.User;
import li.strolch.privilege.model.internal.UserHistory; import li.strolch.privilege.model.internal.UserHistory;
import li.strolch.utils.helper.StringHelper; import li.strolch.utils.dbc.DBC;
import java.text.MessageFormat;
import java.util.*;
import static li.strolch.privilege.base.PrivilegeConstants.*;
import static li.strolch.utils.helper.StringHelper.isEmpty;
import static li.strolch.utils.helper.StringHelper.trimOrEmpty;
/** /**
* To keep certain details of the {@link User} itself hidden from remote clients and make sure instances are only edited * To keep certain details of the {@link User} itself hidden from remote clients and make sure instances are only edited
@ -37,7 +36,7 @@ import li.strolch.utils.helper.StringHelper;
* *
* @author Robert von Burg <eitch@eitchnet.ch> * @author Robert von Burg <eitch@eitchnet.ch>
*/ */
public class UserRep implements Serializable { public class UserRep {
private String userId; private String userId;
private String username; private String username;
@ -45,47 +44,40 @@ public class UserRep implements Serializable {
private String lastname; private String lastname;
private UserState userState; private UserState userState;
private Locale locale; private Locale locale;
private Set<String> groups;
private Set<String> roles; private Set<String> roles;
private Map<String, String> properties; private Map<String, String> properties;
private UserHistory history; private UserHistory history;
private boolean readOnly;
/** /**
* Default constructor * Default constructor
* *
* @param userId * @param userId the user's id
* the user's id * @param username the user's login name
* @param username * @param firstname the user's first name
* the user's login name * @param lastname the user's last name
* @param firstname * @param userState the user's {@link UserState}
* the user's first name * @param groups the set of {@link li.strolch.privilege.model.internal.Group}s assigned to this user
* @param lastname * @param roles the set of {@link Role}s assigned to this user
* the user's last name * @param locale the user's {@link Locale}
* @param userState * @param propertyMap a {@link Map} containing string value pairs of properties for this user
* the user's {@link UserState}
* @param roles
* the set of {@link Role}s assigned to this user
* @param locale
* the user's {@link Locale}
* @param propertyMap
* a {@link Map} containing string value pairs of properties for this user
*/ */
public UserRep(String userId, String username, String firstname, String lastname, UserState userState, public UserRep(String userId, String username, String firstname, String lastname, UserState userState,
Set<String> roles, Locale locale, Map<String, String> propertyMap, UserHistory history) { Set<String> groups, Set<String> roles, Locale locale, Map<String, String> propertyMap,
UserHistory history) {
this.userId = trimOrEmpty(userId); this.userId = trimOrEmpty(userId);
this.username = trimOrEmpty(username); this.username = trimOrEmpty(username);
this.firstname = trimOrEmpty(firstname); this.firstname = trimOrEmpty(firstname);
this.lastname = trimOrEmpty(lastname); this.lastname = trimOrEmpty(lastname);
this.userState = userState; this.userState = userState;
this.roles = roles == null ? null : roles.stream().map(String::trim).collect(Collectors.toSet());
this.locale = locale; this.locale = locale;
setGroups(groups == null ? Set.of() : groups);
if (propertyMap != null) { setRoles(roles == null ? Set.of() : roles);
this.properties = new HashMap<>(); setProperties(properties == null ? Map.of() : properties);
propertyMap.forEach((key, value) -> this.properties.put(key.trim(), value.trim())); this.history = history == null ? UserHistory.EMPTY : history;
}
this.history = history;
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")
@ -97,30 +89,53 @@ public class UserRep implements Serializable {
* Validates that all required fields are set * Validates that all required fields are set
*/ */
public void validate() { public void validate() {
if (isEmpty(this.userId))
throw new PrivilegeException("userId must not be empty");
if (isEmpty(this.username))
throw new PrivilegeException("username must not be empty");
if (StringHelper.isEmpty(this.userId)) // username must be at least 3 characters in length
throw new PrivilegeException("userId is null or empty"); if (this.username.length() < 3) {
String msg = MessageFormat.format("The given username ''{0}'' is shorter than 3 characters", this.username);
if (StringHelper.isEmpty(this.username))
throw new PrivilegeException("username is null or empty");
// username must be at least 2 characters in length
if (this.username.length() < 2) {
String msg = MessageFormat.format("The given username ''{0}'' is shorter than 2 characters", this.username);
throw new PrivilegeException(msg); throw new PrivilegeException(msg);
} }
if (this.userState == null) if (this.userState == null)
throw new PrivilegeException("userState is null"); throw new PrivilegeException("userState may not be null");
if (StringHelper.isEmpty(this.firstname)) if (this.userState != UserState.SYSTEM) {
throw new PrivilegeException("firstname is null or empty"); if (isEmpty(this.firstname))
throw new PrivilegeException("firstname may not be empty for non-system users");
if (isEmpty(this.lastname))
throw new PrivilegeException("lastname may not be empty for non-system users");
}
if (StringHelper.isEmpty(this.lastname)) if (this.groups == null)
throw new PrivilegeException("lastname is null or empty"); throw new PrivilegeException("groups may not be null");
if (this.roles == null)
throw new PrivilegeException("roles may not be null");
if (this.roles == null || this.roles.isEmpty()) if (this.groups.isEmpty() && this.roles.isEmpty())
throw new PrivilegeException("roles is null or empty"); throw new PrivilegeException("User must have at least one group or role assigned!");
}
public boolean isReadOnly() {
return readOnly;
}
public UserRep readOnly() {
if (this.readOnly)
return this;
this.readOnly = true;
this.groups = Set.copyOf(this.groups);
this.roles = Set.copyOf(this.roles);
this.properties = Map.copyOf(this.properties);
return this;
}
protected void assertNotReadonly() {
if (this.readOnly)
throw new IllegalStateException("User is currently readOnly, to modify get a copy!");
} }
public boolean isSystemUser() { public boolean isSystemUser() {
@ -145,10 +160,10 @@ public class UserRep implements Serializable {
/** /**
* Set the userId * Set the userId
* *
* @param userId * @param userId to set
* to set
*/ */
public void setUserId(String userId) { public void setUserId(String userId) {
assertNotReadonly();
this.userId = trimOrEmpty(userId); this.userId = trimOrEmpty(userId);
} }
@ -160,10 +175,10 @@ public class UserRep implements Serializable {
} }
/** /**
* @param username * @param username the username to set
* the username to set
*/ */
public void setUsername(String username) { public void setUsername(String username) {
assertNotReadonly();
this.username = trimOrEmpty(username); this.username = trimOrEmpty(username);
} }
@ -175,10 +190,10 @@ public class UserRep implements Serializable {
} }
/** /**
* @param firstname * @param firstname the firstname to set
* the firstname to set
*/ */
public void setFirstname(String firstname) { public void setFirstname(String firstname) {
assertNotReadonly();
this.firstname = trimOrEmpty(firstname); this.firstname = trimOrEmpty(firstname);
} }
@ -190,10 +205,10 @@ public class UserRep implements Serializable {
} }
/** /**
* @param lastname * @param lastname the lastname to set
* the lastname to set
*/ */
public void setLastname(String lastname) { public void setLastname(String lastname) {
assertNotReadonly();
this.lastname = trimOrEmpty(lastname); this.lastname = trimOrEmpty(lastname);
} }
@ -205,13 +220,27 @@ public class UserRep implements Serializable {
} }
/** /**
* @param userState * @param userState the userState to set
* the userState to set
*/ */
public void setUserState(UserState userState) { public void setUserState(UserState userState) {
assertNotReadonly();
this.userState = userState; this.userState = userState;
} }
public Set<String> getGroups() {
return this.groups;
}
public void setGroups(Set<String> groups) {
DBC.PRE.assertNotNull("groups must not be null!", groups);
assertNotReadonly();
this.groups = groups.stream().map(String::trim).collect(HashSet::new, HashSet::add, HashSet::addAll);
}
public boolean hasGroup(String group) {
return this.groups.contains(group);
}
/** /**
* @return the roles * @return the roles
*/ */
@ -220,21 +249,19 @@ public class UserRep implements Serializable {
} }
/** /**
* @param roles * @param roles the roles to set
* the roles to set
*/ */
public void setRoles(Set<String> roles) { public void setRoles(Set<String> roles) {
this.roles = roles.stream().map(String::trim).collect(Collectors.toSet()); DBC.PRE.assertNotNull("roles must not be null!", roles);
assertNotReadonly();
this.roles = roles.stream().map(String::trim).collect(HashSet::new, HashSet::add, HashSet::addAll);
}
public void addRole(String role) {
assertNotReadonly();
this.roles.add(role);
} }
/**
* Returns true if this user has the given role
*
* @param role
* the role to check for
*
* @return returns true if this user has the given role
*/
public boolean hasRole(String role) { public boolean hasRole(String role) {
return this.roles.contains(role); return this.roles.contains(role);
} }
@ -247,10 +274,10 @@ public class UserRep implements Serializable {
} }
/** /**
* @param locale * @param locale the locale to set
* the locale to set
*/ */
public void setLocale(Locale locale) { public void setLocale(Locale locale) {
assertNotReadonly();
this.locale = locale; this.locale = locale;
} }
@ -260,18 +287,15 @@ public class UserRep implements Serializable {
* @return the user history * @return the user history
*/ */
public UserHistory getHistory() { public UserHistory getHistory() {
if (this.history == null)
return new UserHistory();
return this.history; return this.history;
} }
/** /**
* Returns true if the the given property exists * Returns true if the given property exists
* *
* @param key * @param key the property key to check
* the property key to check
* *
* @return true if the the given property exists * @return true if the given property exists
*/ */
public boolean hasProperty(String key) { public boolean hasProperty(String key) {
return this.properties.containsKey(key); return this.properties.containsKey(key);
@ -280,40 +304,41 @@ public class UserRep implements Serializable {
/** /**
* Returns the property with the given key * Returns the property with the given key
* *
* @param key * @param key the key for which the property is to be returned
* the key for which the property is to be returned
* *
* @return the property with the given key, or null if the property is not defined * @return the property with the given key, or null if the property is not defined
*/ */
public String getProperty(String key) { public String getProperty(String key) {
if (this.properties == null)
return null;
return this.properties.get(key); return this.properties.get(key);
} }
/** /**
* Set the property with the key to the value * Set the property with the key to the value
* *
* @param key * @param key the key of the property to set
* the key of the property to set * @param value the value of the property to set
* @param value
* the value of the property to set
*/ */
public void setProperty(String key, String value) { public void setProperty(String key, String value) {
if (this.properties == null) DBC.PRE.assertNotEmpty("key must not be empty!", key);
this.properties = new HashMap<>(1); DBC.PRE.assertNotEmpty("value must not be empty!", value);
assertNotReadonly();
this.properties.put(key.trim(), value.trim()); this.properties.put(key.trim(), value.trim());
} }
public void setProperties(Map<String, String> properties) {
DBC.PRE.assertNotNull("properties must not be null!", properties);
assertNotReadonly();
this.properties = new HashMap<>();
properties.forEach((key, value) -> this.properties.put(key.trim(), value.trim()));
}
/** /**
* Returns the {@link Set} of keys of all properties * Returns the {@link Set} of keys of all properties
* *
* @return the {@link Set} of keys of all properties * @return the {@link Set} of keys of all properties
*/ */
public Set<String> getPropertyKeySet() { public Set<String> getPropertyKeySet() {
if (this.properties == null) return this.properties.keySet();
return new HashSet<>();
return new HashSet<>(this.properties.keySet());
} }
/** /**
@ -322,9 +347,7 @@ public class UserRep implements Serializable {
* @return the map of properties * @return the map of properties
*/ */
public Map<String, String> getProperties() { public Map<String, String> getProperties() {
if (this.properties == null) return this.properties;
return new HashMap<>();
return new HashMap<>(this.properties);
} }
/** /**
@ -361,9 +384,9 @@ public class UserRep implements Serializable {
*/ */
@Override @Override
public String toString() { public String toString() {
return "UserRep [userId=" + this.userId + ", username=" + this.username + ", firstname=" + this.firstname return "UserRep [userId=" + this.userId + ", username=" + this.username + ", firstname=" + this.firstname +
+ ", lastname=" + this.lastname + ", userState=" + this.userState + ", locale=" + this.locale ", lastname=" + this.lastname + ", userState=" + this.userState + ", locale=" + this.locale +
+ ", roles=" + this.roles + "]"; ", roles=" + this.roles + "]";
} }
@Override @Override
@ -390,12 +413,8 @@ public class UserRep implements Serializable {
} }
public UserRep getCopy() { public UserRep getCopy() {
return new UserRep(this.userId, this.username, this.firstname, this.lastname, this.userState, this.groups,
Set<String> roles = new HashSet<>(this.roles); this.roles, this.locale, this.properties, this.history);
Map<String, String> propertyMap = this.properties == null ? null : new HashMap<>(this.properties);
return new UserRep(this.userId, this.username, this.firstname, this.lastname, this.userState, roles,
this.locale, propertyMap, this.history == null ? new UserHistory() : this.history.getClone());
} }
public <T> T accept(PrivilegeElementVisitor<T> visitor) { public <T> T accept(PrivilegeElementVisitor<T> visitor) {

View File

@ -0,0 +1,92 @@
package li.strolch.privilege.model.internal;
import li.strolch.privilege.base.PrivilegeConstants;
import li.strolch.utils.dbc.DBC;
import java.util.Map;
import java.util.Set;
import static li.strolch.privilege.base.PrivilegeConstants.*;
/**
* This entity represents a group with which {@link User Users} can be associated. This allows to put roles and
* properties which are always duplicated on users on to the group, and the User is then in the group, eliminating
* duplication.
*/
public record Group(String name, Set<String> roles, Map<String, String> propertyMap) {
public Group(String name, Set<String> roles, Map<String, String> propertyMap) {
DBC.PRE.assertNotEmpty("name must not be empty", name);
DBC.PRE.assertNotNull("roles must not be null", roles);
DBC.PRE.assertNotNull("propertyMap must not be null", propertyMap);
this.name = name;
this.roles = Set.copyOf(roles);
this.propertyMap = Map.copyOf(propertyMap);
}
/**
* Returns true if this group has the specified role
*
* @param role the name of the {@link Role} to check for
*
* @return true if this group has the specified role
*/
public boolean hasRole(String role) {
return this.roles.contains(role);
}
/**
* Returns the property with the given key
*
* @param key the key for which the property is to be returned
*
* @return the property with the given key, or null if the property is not defined
*/
public String getProperty(String key) {
return this.propertyMap.get(key);
}
/**
* Returns the {@link Set} of keys of all properties
*
* @return the {@link Set} of keys of all properties
*/
public Set<String> getPropertyKeySet() {
return this.propertyMap.keySet();
}
/**
* Returns the map of properties
*
* @return the map of properties
*/
public Map<String, String> getProperties() {
return this.propertyMap;
}
/**
* Returns the value of the property {@link PrivilegeConstants#REALM}
*
* @return the value of the property {@link PrivilegeConstants#REALM}
*/
public String getRealm() {
return getProperty(REALM);
}
/**
* Returns the value of the property {@link PrivilegeConstants#ORGANISATION}
*
* @return the value of the property {@link PrivilegeConstants#ORGANISATION}
*/
public String getOrganisation() {
return getProperty(ORGANISATION);
}
/**
* Returns the value of the property {@link PrivilegeConstants#LOCATION}
*
* @return the value of the property {@link PrivilegeConstants#LOCATION}
*/
public String getLocation() {
return getProperty(LOCATION);
}
}

View File

@ -2,48 +2,33 @@ package li.strolch.privilege.model.internal;
import static li.strolch.utils.helper.StringHelper.*; import static li.strolch.utils.helper.StringHelper.*;
public class PasswordCrypt { public record PasswordCrypt(byte[] password, byte[] salt, String hashAlgorithm, int hashIterations, int hashKeyLength) {
private final byte[] password; @Override
private final byte[] salt; public String toString() {
private final String hashAlgorithm; return buildPasswordString();
private final int hashIterations;
private final int hashKeyLength;
public PasswordCrypt(byte[] password, byte[] salt) {
this.password = password;
this.salt = salt;
this.hashAlgorithm = null;
this.hashIterations = -1;
this.hashKeyLength = -1;
} }
public PasswordCrypt(byte[] password, byte[] salt, String hashAlgorithm, int hashIterations, int hashKeyLength) { public String buildPasswordString() {
this.password = password; if (this.password == null || this.salt == null || this.hashAlgorithm == null || this.hashIterations == -1 ||
this.salt = salt; this.hashKeyLength == -1) {
this.hashAlgorithm = hashAlgorithm; return null;
this.hashIterations = hashIterations; }
this.hashKeyLength = hashKeyLength;
return buildPasswordString(this.hashAlgorithm, this.hashIterations, this.hashKeyLength, this.salt,
this.password);
} }
public byte[] getPassword() { public static String buildPasswordString(String hashAlgorithm, int hashIterations, int hashKeyLength, byte[] salt,
return password; byte[] passwordArr) {
String algo = hashAlgorithm + "," + hashIterations + "," + hashKeyLength;
String hash = toHexString(salt);
String password = toHexString(passwordArr);
return "$" + algo + "$" + hash + "$" + password;
} }
public byte[] getSalt() { public static PasswordCrypt of(byte[] password, byte[] salt) {
return salt; return new PasswordCrypt(password, salt, null, -1, -1);
}
public String getHashAlgorithm() {
return hashAlgorithm;
}
public int getHashIterations() {
return hashIterations;
}
public int getHashKeyLength() {
return hashKeyLength;
} }
public static PasswordCrypt parse(String passwordS, String saltS) { public static PasswordCrypt parse(String passwordS, String saltS) {
@ -55,14 +40,14 @@ public class PasswordCrypt {
salt = fromHexString(saltS.trim()); salt = fromHexString(saltS.trim());
if (isEmpty(passwordS)) if (isEmpty(passwordS))
return new PasswordCrypt(null, salt); return PasswordCrypt.of(null, salt);
passwordS = passwordS.trim(); passwordS = passwordS.trim();
byte[] password; byte[] password;
if (!passwordS.startsWith("$")) { if (!passwordS.startsWith("$")) {
password = fromHexString(passwordS); password = fromHexString(passwordS);
return new PasswordCrypt(password, salt); return PasswordCrypt.of(password, salt);
} }
String[] parts = passwordS.split("\\$"); String[] parts = passwordS.split("\\$");
@ -86,28 +71,4 @@ public class PasswordCrypt {
return new PasswordCrypt(password, salt, hashAlgorithm, hashIterations, hashKeyLength); return new PasswordCrypt(password, salt, hashAlgorithm, hashIterations, hashKeyLength);
} }
@Override
public String toString() {
return buildPasswordString();
}
public String buildPasswordString() {
if (this.password == null || this.salt == null || this.hashAlgorithm == null || this.hashIterations == -1 ||
this.hashKeyLength == -1) {
return null;
}
return buildPasswordString(getHashAlgorithm(), getHashIterations(), getHashKeyLength(), getSalt(),
getPassword());
}
public static String buildPasswordString(String hashAlgorithm, int hashIterations, int hashKeyLength, byte[] salt,
byte[] passwordArr) {
String algo = hashAlgorithm + "," + hashIterations + "," + hashKeyLength;
String hash = toHexString(salt);
String password = toHexString(passwordArr);
return "$" + algo + "$" + hash + "$" + password;
}
} }

View File

@ -1,223 +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.model.internal;
import java.text.MessageFormat;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import li.strolch.privilege.base.PrivilegeException;
import li.strolch.privilege.handler.PrivilegeHandler;
import li.strolch.privilege.model.IPrivilege;
import li.strolch.privilege.model.PrivilegeRep;
import li.strolch.privilege.model.Restrictable;
import li.strolch.privilege.policy.PrivilegePolicy;
import li.strolch.utils.helper.StringHelper;
/**
* <p>
* {@link IPrivilege} is the main model object for Privilege. A {@link Role} has a set of Privileges assigned to it
* which defines the privileges a logged in user with that role has. If the {@link IPrivilege} has a {@link
* PrivilegePolicy} defined, then that policy will be used for finer granularity and with the deny and allow lists
* configured which is used to evaluate if privilege is granted to a {@link Restrictable}
* </p>
*
* <p>
* {@link IPrivilege}s have allow and deny rules which the configured {@link PrivilegeHandler} uses to
* </p>
*
* <p>
* Note: This is an internal object which is not to be serialized or passed to clients, {@link PrivilegeRep}s are used
* for that
* </p>
*
* @author Robert von Burg <eitch@eitchnet.ch>
*/
public final class PrivilegeImpl implements IPrivilege {
private final String name;
private final String policy;
private final boolean allAllowed;
private final Set<String> denyList;
private final Set<String> allowList;
/**
* Default constructor
*
* @param name
* the name of this privilege, which is unique to all privileges known in the {@link PrivilegeHandler}
* @param policy
* the {@link PrivilegePolicy} configured to evaluate if the privilege is granted. If null, then privilege is
* granted
* @param allAllowed
* a boolean defining if a {@link Role} with this {@link PrivilegeImpl} has unrestricted access to a {@link
* Restrictable} in which case the deny and allow lists are ignored and can be null
* @param denyList
* a list of deny rules for this {@link PrivilegeImpl}, can be null if all allowed
* @param allowList
* a list of allow rules for this {@link PrivilegeImpl}, can be null if all allowed
*/
public PrivilegeImpl(String name, String policy, boolean allAllowed, Set<String> denyList, Set<String> allowList) {
if (StringHelper.isEmpty(name)) {
throw new PrivilegeException("No name defined!");
}
if (StringHelper.isEmpty(policy)) {
throw new PrivilegeException(
MessageFormat.format("Policy may not be empty for Privilege {0}!", name));
}
if (denyList == null) {
throw new PrivilegeException(
MessageFormat.format("denyList is null for Privilege {0}!", name));
}
if (allowList == null) {
throw new PrivilegeException(
MessageFormat.format("allowList is null for Privilege {0}!", name));
}
this.name = name;
this.allAllowed = allAllowed;
this.policy = policy;
this.denyList = Collections.unmodifiableSet(denyList);
this.allowList = Collections.unmodifiableSet(allowList);
}
/**
* Constructs a {@link PrivilegeImpl} from the {@link PrivilegeRep}
*
* @param privilegeRep
* the {@link PrivilegeRep} from which to create the {@link PrivilegeImpl}
*/
public PrivilegeImpl(PrivilegeRep privilegeRep) {
this(privilegeRep.getName(), privilegeRep.getPolicy(), privilegeRep.isAllAllowed(), privilegeRep.getDenyList(),
privilegeRep.getAllowList());
}
/**
* @return a {@link PrivilegeRep} which is a representation of this object used to serialize and view on clients
*/
@Override
public PrivilegeRep asPrivilegeRep() {
return new PrivilegeRep(this.name, this.policy, this.allAllowed, new HashSet<>(this.denyList),
new HashSet<>(this.allowList));
}
/**
* @return the name
*/
@Override
public String getName() {
return this.name;
}
/**
* @return the policy
*/
@Override
public String getPolicy() {
return this.policy;
}
/**
* @return the allAllowed
*/
@Override
public boolean isAllAllowed() {
return this.allAllowed;
}
/**
* @return the allowList
*/
@Override
public Set<String> getAllowList() {
return this.allowList;
}
/**
* @return the denyList
*/
@Override
public Set<String> getDenyList() {
return this.denyList;
}
/**
* @return true if there are values in the allow list
*/
@Override
public boolean hasAllowed() {
return !this.allowList.isEmpty();
}
/**
* @return if the value is in the allow list
*/
@Override
public boolean isAllowed(String value) {
return this.allowList.contains(value);
}
/**
* @return true if there are values in the deny list
*/
@Override
public boolean hasDenied() {
return !this.allowList.isEmpty();
}
/**
* @return true if the value is in the deny list
*/
@Override
public boolean isDenied(String value) {
return this.denyList.contains(value);
}
/**
* Returns a string representation of this object displaying its concrete type and its values
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "Privilege [name=" + this.name + ", policy=" + this.policy + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((this.name == null) ? 0 : this.name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
PrivilegeImpl other = (PrivilegeImpl) obj;
if (this.name == null) {
return other.name == null;
} else
return this.name.equals(other.name);
}
}

View File

@ -15,14 +15,17 @@
*/ */
package li.strolch.privilege.model.internal; package li.strolch.privilege.model.internal;
import java.util.*;
import java.util.Map.Entry;
import li.strolch.privilege.base.PrivilegeException; import li.strolch.privilege.base.PrivilegeException;
import li.strolch.privilege.model.IPrivilege; import li.strolch.privilege.model.Privilege;
import li.strolch.privilege.model.PrivilegeRep; import li.strolch.privilege.model.PrivilegeRep;
import li.strolch.privilege.model.RoleRep; import li.strolch.privilege.model.RoleRep;
import li.strolch.utils.helper.StringHelper; import li.strolch.utils.dbc.DBC;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import static li.strolch.utils.helper.StringHelper.isEmpty;
/** /**
* <p> * <p>
@ -37,57 +40,32 @@ import li.strolch.utils.helper.StringHelper;
* *
* @author Robert von Burg <eitch@eitchnet.ch> * @author Robert von Burg <eitch@eitchnet.ch>
*/ */
public final class Role { public record Role(String name, Map<String, Privilege> privilegeMap) {
private final String name;
private final Map<String, IPrivilege> privilegeMap;
/**
* Default constructor
*
* @param name
* the name of the role
* @param privilegeMap
* a map of {@link IPrivilege}s granted to this role
*/
public Role(String name, Map<String, IPrivilege> privilegeMap) {
if (StringHelper.isEmpty(name)) {
throw new PrivilegeException("No name defined!");
}
if (privilegeMap == null) {
throw new PrivilegeException("No privileges defined!");
}
public Role(String name, Map<String, Privilege> privilegeMap) {
DBC.PRE.assertNotEmpty("name must not be empty", name);
DBC.PRE.assertNotNull("privilegeMap must not be null", privilegeMap);
this.name = name; this.name = name;
this.privilegeMap = Collections.unmodifiableMap(privilegeMap); this.privilegeMap = Map.copyOf(privilegeMap);
} }
/** /**
* Construct {@link Role} from its representation {@link RoleRep} * Construct {@link Role} from its representation {@link RoleRep}
* *
* @param roleRep * @param roleRep the representation from which to create the {@link Role}
* the representation from which to create the {@link Role}
*/ */
public Role(RoleRep roleRep) { public static Role of(RoleRep roleRep) {
String name = roleRep.getName(); String name = roleRep.getName();
if (StringHelper.isEmpty(name)) { if (isEmpty(name))
throw new PrivilegeException("No name defined!"); throw new PrivilegeException("No name defined!");
} if (roleRep.getPrivileges() == null)
if (roleRep.getPrivileges() == null) {
throw new PrivilegeException("Privileges may not be null!"); throw new PrivilegeException("Privileges may not be null!");
}
// build privileges from rep // build privileges from rep
Map<String, IPrivilege> privilegeMap = new HashMap<>(roleRep.getPrivileges().size()); Map<String, Privilege> privilegeMap = new HashMap<>(roleRep.getPrivileges().size());
for (PrivilegeRep privilege : roleRep.getPrivileges()) { roleRep.getPrivileges().values().forEach(p -> privilegeMap.put(p.getName(), Privilege.of(p)));
privilegeMap.put(privilege.getName(), new PrivilegeImpl(privilege));
}
this.name = name; return new Role(name, privilegeMap);
this.privilegeMap = Collections.unmodifiableMap(privilegeMap);
} }
/** /**
@ -98,30 +76,29 @@ public final class Role {
} }
/** /**
* Returns the {@link Set} of names for the currently stored {@link IPrivilege Privileges} * Returns the {@link Set} of names for the currently stored {@link Privilege Privileges}
* *
* @return the {@link Set} of names for the currently stored {@link IPrivilege Privileges} * @return the {@link Set} of names for the currently stored {@link Privilege Privileges}
*/ */
public Set<String> getPrivilegeNames() { public Set<String> getPrivilegeNames() {
return this.privilegeMap.keySet(); return this.privilegeMap.keySet();
} }
/** /**
* Returns the {@link IPrivilege} for the given name, null if it does not exist * Returns the {@link Privilege} for the given name, null if it does not exist
* *
* @return the {@link IPrivilege} for the given name, null if it does not exist * @return the {@link Privilege} for the given name, null if it does not exist
*/ */
public IPrivilege getPrivilege(String name) { public Privilege getPrivilege(String name) {
return this.privilegeMap.get(name); return this.privilegeMap.get(name);
} }
/** /**
* Determines if this {@link Role} has the {@link IPrivilege} with the given name * Determines if this {@link Role} has the {@link Privilege} with the given name
* *
* @param name * @param name the name of the {@link Privilege}
* the name of the {@link IPrivilege}
* *
* @return true if this {@link Role} has the {@link IPrivilege} with the given name * @return true if this {@link Role} has the {@link Privilege} with the given name
*/ */
public boolean hasPrivilege(String name) { public boolean hasPrivilege(String name) {
return this.privilegeMap.containsKey(name); return this.privilegeMap.containsKey(name);
@ -131,10 +108,8 @@ public final class Role {
* @return a {@link RoleRep} which is a representation of this object used to serialize and view on clients * @return a {@link RoleRep} which is a representation of this object used to serialize and view on clients
*/ */
public RoleRep asRoleRep() { public RoleRep asRoleRep() {
List<PrivilegeRep> privileges = new ArrayList<>(); Map<String, PrivilegeRep> privileges = new HashMap<>();
for (Entry<String, IPrivilege> entry : this.privilegeMap.entrySet()) { this.privilegeMap.values().forEach(p -> privileges.put(p.getName(), p.asPrivilegeRep()));
privileges.add(entry.getValue().asPrivilegeRep());
}
return new RoleRep(this.name, privileges); return new RoleRep(this.name, privileges);
} }

View File

@ -16,12 +16,14 @@
package li.strolch.privilege.model.internal; package li.strolch.privilege.model.internal;
import li.strolch.privilege.base.PrivilegeConstants; import li.strolch.privilege.base.PrivilegeConstants;
import li.strolch.privilege.base.PrivilegeException;
import li.strolch.privilege.model.UserRep; import li.strolch.privilege.model.UserRep;
import li.strolch.privilege.model.UserState; import li.strolch.privilege.model.UserState;
import li.strolch.utils.helper.StringHelper; import li.strolch.utils.dbc.DBC;
import java.util.*; import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import static li.strolch.privilege.base.PrivilegeConstants.*; import static li.strolch.privilege.base.PrivilegeConstants.*;
@ -34,59 +36,36 @@ import static li.strolch.privilege.base.PrivilegeConstants.*;
* that * that
* </p> * </p>
* *
* @param userId the user's id
* @param username the user's login name
* @param passwordCrypt the {@link PasswordCrypt} containing user's password information
* @param firstname the user's first name
* @param lastname the user's lastname
* @param userState the user's {@link UserState}
* @param groups the set of {@link Group}s assigned to this user
* @param roles the set of {@link Role}s assigned to this user
* @param locale the user's {@link Locale}
* @param propertyMap a {@link Map} containing string value pairs of properties for this user
*
* @author Robert von Burg <eitch@eitchnet.ch> * @author Robert von Burg <eitch@eitchnet.ch>
*/ */
public final class User { public record User(String userId, String username, PasswordCrypt passwordCrypt, String firstname, String lastname,
UserState userState, Set<String> groups, Set<String> roles, Locale locale,
Map<String, String> propertyMap, boolean passwordChangeRequested, UserHistory history) {
private final String userId;
private final String username;
private final PasswordCrypt passwordCrypt;
private final String firstname;
private final String lastname;
private final Set<String> roles;
private final UserState userState;
private final Map<String, String> propertyMap;
private final Locale locale;
private final boolean passwordChangeRequested;
private final UserHistory history;
/**
* Default constructor
*
* @param userId the user's id
* @param username the user's login name
* @param passwordCrypt the {@link PasswordCrypt} containing user's password information
* @param firstname the user's first name
* @param lastname the user's lastname
* @param userState the user's {@link UserState}
* @param roles the set of {@link Role}s assigned to this user
* @param locale the user's {@link Locale}
* @param propertyMap a {@link Map} containing string value pairs of properties for this user
*/
public User(String userId, String username, PasswordCrypt passwordCrypt, String firstname, String lastname, public User(String userId, String username, PasswordCrypt passwordCrypt, String firstname, String lastname,
UserState userState, Set<String> roles, Locale locale, Map<String, String> propertyMap, UserState userState, Set<String> groups, Set<String> roles, Locale locale, Map<String, String> propertyMap,
boolean passwordChangeRequested, UserHistory history) { boolean passwordChangeRequested, UserHistory history) {
if (StringHelper.isEmpty(userId)) DBC.PRE.assertNotEmpty("userId must not be empty", userId);
throw new PrivilegeException("No UserId defined!"); DBC.PRE.assertNotEmpty("username must not be empty", username);
if (userState == null) DBC.PRE.assertNotNull("userState must not be null", userState);
throw new PrivilegeException("No userState defined!"); DBC.PRE.assertNotNull("history must not be null", history);
if (StringHelper.isEmpty(username))
throw new PrivilegeException("No username defined!");
if (userState != UserState.SYSTEM) {
if (StringHelper.isEmpty(lastname))
throw new PrivilegeException("No lastname defined!");
if (StringHelper.isEmpty(firstname))
throw new PrivilegeException("No firstname defined!");
}
if (history == null) if (userState != UserState.SYSTEM) {
throw new PrivilegeException("History must not be null!"); DBC.PRE.assertNotEmpty("lastname must not be empty when not system user!", username);
DBC.PRE.assertNotEmpty("firstname must not be empty when not system user!", firstname);
}
// passwordCrypt may be null, meaning not able to login // passwordCrypt may be null, meaning not able to login
// roles may be null, meaning not able to login and must be added later // roles may be null, meaning not able to login and must be added later
@ -103,35 +82,19 @@ public final class User {
this.firstname = firstname; this.firstname = firstname;
this.lastname = lastname; this.lastname = lastname;
if (roles == null) this.groups = groups == null ? Set.of() : Set.copyOf(groups);
this.roles = Collections.emptySet(); this.roles = roles == null ? Set.of() : Set.copyOf(roles);
else this.locale = locale == null ? Locale.getDefault() : locale;
this.roles = Set.copyOf(roles); this.propertyMap = propertyMap == null ? Map.of() : Map.copyOf(propertyMap);
if (locale == null)
this.locale = Locale.getDefault();
else
this.locale = locale;
if (propertyMap == null)
this.propertyMap = Collections.emptyMap();
else
this.propertyMap = Map.copyOf(propertyMap);
this.passwordChangeRequested = passwordChangeRequested; this.passwordChangeRequested = passwordChangeRequested;
this.history = history; this.history = history;
} }
/**
* @return the userId
*/
public String getUserId() { public String getUserId() {
return this.userId; return this.userId;
} }
/**
* @return the username
*/
public String getUsername() { public String getUsername() {
return this.username; return this.username;
} }
@ -145,48 +108,34 @@ public final class User {
return this.passwordCrypt; return this.passwordCrypt;
} }
/**
* @return the first name
*/
public String getFirstname() { public String getFirstname() {
return this.firstname; return this.firstname;
} }
/**
* @return the last name
*/
public String getLastname() { public String getLastname() {
return this.lastname; return this.lastname;
} }
/**
* @return the userState
*/
public UserState getUserState() { public UserState getUserState() {
return this.userState; return this.userState;
} }
/** public Set<String> getGroups() {
* @return the roles return this.groups;
*/ }
public Set<String> getRoles() { public Set<String> getRoles() {
return this.roles; return this.roles;
} }
/** public boolean hasGroup(String group) {
* Returns true if this user has the specified role return this.groups.contains(group);
* }
* @param role the name of the {@link Role} to check for
*
* @return true if the this user has the specified role
*/
public boolean hasRole(String role) { public boolean hasRole(String role) {
return this.roles.contains(role); return this.roles.contains(role);
} }
/**
* @return the locale
*/
public Locale getLocale() { public Locale getLocale() {
return this.locale; return this.locale;
} }
@ -195,11 +144,6 @@ public final class User {
return this.passwordChangeRequested; return this.passwordChangeRequested;
} }
/**
* Returns the History object
*
* @return the History object
*/
public UserHistory getHistory() { public UserHistory getHistory() {
return this.history; return this.history;
} }
@ -282,8 +226,8 @@ public final class User {
* @return a {@link UserRep} which is a representation of this object used to serialize and view on clients * @return a {@link UserRep} which is a representation of this object used to serialize and view on clients
*/ */
public UserRep asUserRep() { public UserRep asUserRep() {
return new UserRep(this.userId, this.username, this.firstname, this.lastname, this.userState, return new UserRep(this.userId, this.username, this.firstname, this.lastname, this.userState, this.groups,
new HashSet<>(this.roles), this.locale, new HashMap<>(this.propertyMap), this.history.getClone()); this.roles, this.locale, new HashMap<>(this.propertyMap), this.history);
} }
/** /**
@ -315,9 +259,13 @@ public final class User {
if (getClass() != obj.getClass()) if (getClass() != obj.getClass())
return false; return false;
User other = (User) obj; User other = (User) obj;
if (this.userId == null) { if (this.userId == null)
return other.userId == null; return other.userId == null;
} else return this.userId.equals(other.userId);
return this.userId.equals(other.userId); }
public User withHistory(UserHistory history) {
return new User(this.userId, this.username, this.passwordCrypt, this.firstname, this.lastname, this.userState,
this.groups, this.roles, this.locale, this.propertyMap, this.passwordChangeRequested, history);
} }
} }

View File

@ -1,10 +1,11 @@
package li.strolch.privilege.model.internal; package li.strolch.privilege.model.internal;
import li.strolch.privilege.model.Usage;
import li.strolch.utils.dbc.DBC;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import li.strolch.privilege.model.Usage; public final class UserChallenge {
public class UserChallenge {
private final User user; private final User user;
private final String challenge; private final String challenge;
private final String source; private final String source;
@ -13,6 +14,10 @@ public class UserChallenge {
private boolean fulfilled; private boolean fulfilled;
public UserChallenge(Usage usage, User user, String challenge, String source) { public UserChallenge(Usage usage, User user, String challenge, String source) {
DBC.PRE.assertNotNull("usage may not be null", usage);
DBC.PRE.assertNotNull("user may not be null", user);
DBC.PRE.assertNotNull("challenge may not be empty", challenge);
DBC.PRE.assertNotNull("source may not be empty", source);
this.usage = usage; this.usage = usage;
this.user = user; this.user = user;
this.challenge = challenge; this.challenge = challenge;

View File

@ -1,32 +1,20 @@
package li.strolch.privilege.model.internal; package li.strolch.privilege.model.internal;
import java.io.Serializable;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import li.strolch.utils.iso8601.ISO8601; import static li.strolch.utils.iso8601.ISO8601.EMPTY_VALUE_ZONED_DATE;
public class UserHistory implements Serializable { public record UserHistory(ZonedDateTime firstLogin, ZonedDateTime lastLogin, ZonedDateTime lastPasswordChange) {
private ZonedDateTime firstLogin; public static final UserHistory EMPTY = new UserHistory(EMPTY_VALUE_ZONED_DATE, EMPTY_VALUE_ZONED_DATE,
private ZonedDateTime lastLogin; EMPTY_VALUE_ZONED_DATE);
private ZonedDateTime lastPasswordChange;
public UserHistory() {
this.firstLogin = ISO8601.EMPTY_VALUE_ZONED_DATE;
this.lastLogin = ISO8601.EMPTY_VALUE_ZONED_DATE;
this.lastPasswordChange = ISO8601.EMPTY_VALUE_ZONED_DATE;
}
public ZonedDateTime getFirstLogin() { public ZonedDateTime getFirstLogin() {
return this.firstLogin; return this.firstLogin;
} }
public boolean isFirstLoginEmpty() { public boolean isFirstLoginEmpty() {
return this.firstLogin.equals(ISO8601.EMPTY_VALUE_ZONED_DATE); return this.firstLogin.equals(EMPTY_VALUE_ZONED_DATE);
}
public void setFirstLogin(ZonedDateTime firstLogin) {
this.firstLogin = firstLogin;
} }
public ZonedDateTime getLastLogin() { public ZonedDateTime getLastLogin() {
@ -34,11 +22,7 @@ public class UserHistory implements Serializable {
} }
public boolean isLastLoginEmpty() { public boolean isLastLoginEmpty() {
return this.lastLogin.equals(ISO8601.EMPTY_VALUE_ZONED_DATE); return this.lastLogin.equals(EMPTY_VALUE_ZONED_DATE);
}
public void setLastLogin(ZonedDateTime lastLogin) {
this.lastLogin = lastLogin;
} }
public ZonedDateTime getLastPasswordChange() { public ZonedDateTime getLastPasswordChange() {
@ -46,22 +30,26 @@ public class UserHistory implements Serializable {
} }
public boolean isLastPasswordChangeEmpty() { public boolean isLastPasswordChangeEmpty() {
return this.lastPasswordChange.equals(ISO8601.EMPTY_VALUE_ZONED_DATE); return this.lastPasswordChange.equals(EMPTY_VALUE_ZONED_DATE);
}
public void setLastPasswordChange(ZonedDateTime lastPasswordChange) {
this.lastPasswordChange = lastPasswordChange;
} }
public boolean isEmpty() { public boolean isEmpty() {
return isFirstLoginEmpty() && isLastLoginEmpty() && isLastPasswordChangeEmpty(); return isFirstLoginEmpty() && isLastLoginEmpty() && isLastPasswordChangeEmpty();
} }
public UserHistory getClone() { public UserHistory withFirstLogin(ZonedDateTime firstLogin) {
UserHistory clone = new UserHistory(); return new UserHistory(firstLogin, this.lastLogin, lastPasswordChange);
clone.firstLogin = this.firstLogin; }
clone.lastLogin = this.lastLogin;
clone.lastPasswordChange = this.lastPasswordChange; public UserHistory withLastLogin(ZonedDateTime lastLogin) {
return clone; return new UserHistory(this.firstLogin, lastLogin, this.lastPasswordChange);
}
public UserHistory withLogin(ZonedDateTime lastLogin) {
return new UserHistory(isFirstLoginEmpty() ? lastLogin : this.firstLogin, lastLogin, this.lastPasswordChange);
}
public UserHistory withLastPasswordChange(ZonedDateTime lastPasswordChange) {
return new UserHistory(this.firstLogin, this.lastLogin, lastPasswordChange);
} }
} }

View File

@ -22,7 +22,7 @@ import java.text.MessageFormat;
import li.strolch.privilege.base.AccessDeniedException; import li.strolch.privilege.base.AccessDeniedException;
import li.strolch.privilege.base.PrivilegeException; import li.strolch.privilege.base.PrivilegeException;
import li.strolch.privilege.i18n.PrivilegeMessages; import li.strolch.privilege.i18n.PrivilegeMessages;
import li.strolch.privilege.model.IPrivilege; import li.strolch.privilege.model.Privilege;
import li.strolch.privilege.model.PrivilegeContext; import li.strolch.privilege.model.PrivilegeContext;
import li.strolch.privilege.model.Restrictable; import li.strolch.privilege.model.Restrictable;
import li.strolch.privilege.model.internal.Role; import li.strolch.privilege.model.internal.Role;
@ -38,10 +38,10 @@ public class DefaultPrivilege implements PrivilegePolicy {
/** /**
* The value of {@link Restrictable#getPrivilegeValue()} is used to check if the {@link Role} has this privilege * The value of {@link Restrictable#getPrivilegeValue()} is used to check if the {@link Role} has this privilege
* *
* @see li.strolch.privilege.policy.PrivilegePolicy#validateAction(PrivilegeContext, IPrivilege, Restrictable) * @see li.strolch.privilege.policy.PrivilegePolicy#validateAction(PrivilegeContext, Privilege, Restrictable)
*/ */
@Override @Override
public void validateAction(PrivilegeContext ctx, IPrivilege privilege, Restrictable restrictable) public void validateAction(PrivilegeContext ctx, Privilege privilege, Restrictable restrictable)
throws AccessDeniedException { throws AccessDeniedException {
String privilegeValue = validatePrivilegeValue(privilege, restrictable); String privilegeValue = validatePrivilegeValue(privilege, restrictable);
@ -54,7 +54,7 @@ public class DefaultPrivilege implements PrivilegePolicy {
} }
@Override @Override
public boolean hasPrivilege(PrivilegeContext ctx, IPrivilege privilege, Restrictable restrictable) { public boolean hasPrivilege(PrivilegeContext ctx, Privilege privilege, Restrictable restrictable) {
String privilegeValue = validatePrivilegeValue(privilege, restrictable); String privilegeValue = validatePrivilegeValue(privilege, restrictable);
@ -65,7 +65,7 @@ public class DefaultPrivilege implements PrivilegePolicy {
return checkByAllowDenyValues(ctx, privilege, restrictable, privilegeValue, false); return checkByAllowDenyValues(ctx, privilege, restrictable, privilegeValue, false);
} }
private String validatePrivilegeValue(IPrivilege privilege, Restrictable restrictable) { private String validatePrivilegeValue(Privilege privilege, Restrictable restrictable) {
PrivilegePolicyHelper.preValidate(privilege, restrictable); PrivilegePolicyHelper.preValidate(privilege, restrictable);
// get the value on which the action is to be performed // get the value on which the action is to be performed

View File

@ -17,7 +17,7 @@ package li.strolch.privilege.policy;
import li.strolch.privilege.base.AccessDeniedException; import li.strolch.privilege.base.AccessDeniedException;
import li.strolch.privilege.base.PrivilegeException; import li.strolch.privilege.base.PrivilegeException;
import li.strolch.privilege.model.IPrivilege; import li.strolch.privilege.model.Privilege;
import li.strolch.privilege.model.PrivilegeContext; import li.strolch.privilege.model.PrivilegeContext;
import li.strolch.privilege.model.Restrictable; import li.strolch.privilege.model.Restrictable;
import li.strolch.privilege.model.internal.Role; import li.strolch.privilege.model.internal.Role;
@ -26,7 +26,7 @@ import li.strolch.privilege.model.internal.User;
/** /**
* <p> * <p>
* {@link PrivilegePolicy} implements logic to determine if a {@link User} which has the given {@link Role} and the * {@link PrivilegePolicy} implements logic to determine if a {@link User} which has the given {@link Role} and the
* given {@link IPrivilege} has access to the given {@link Restrictable} * given {@link Privilege} has access to the given {@link Restrictable}
* </p> * </p>
* *
* <p> * <p>
@ -38,29 +38,29 @@ import li.strolch.privilege.model.internal.User;
public interface PrivilegePolicy { public interface PrivilegePolicy {
/** /**
* Checks if the given {@link Role} and the given {@link IPrivilege} has access to the given {@link Restrictable} * Checks if the given {@link Role} and the given {@link Privilege} has access to the given {@link Restrictable}
* *
* @param context * @param context
* the privilege context * the privilege context
* @param privilege * @param privilege
* the {@link IPrivilege} containing the permissions * the {@link Privilege} containing the permissions
* @param restrictable * @param restrictable
* the {@link Restrictable} to which the user wants access * the {@link Restrictable} to which the user wants access
* *
* @throws AccessDeniedException * @throws AccessDeniedException
* if action not allowed * if action not allowed
*/ */
void validateAction(PrivilegeContext context, IPrivilege privilege, Restrictable restrictable) void validateAction(PrivilegeContext context, Privilege privilege, Restrictable restrictable)
throws AccessDeniedException; throws AccessDeniedException;
/** /**
* Returns true if the given {@link Role} and the given {@link IPrivilege} has access to the given {@link * Returns true if the given {@link Role} and the given {@link Privilege} has access to the given {@link
* Restrictable} * Restrictable}
* *
* @param context * @param context
* the privilege context * the privilege context
* @param privilege * @param privilege
* the {@link IPrivilege} containing the permissions * the {@link Privilege} containing the permissions
* @param restrictable * @param restrictable
* the {@link Restrictable} to which the user wants access * the {@link Restrictable} to which the user wants access
* *
@ -69,6 +69,6 @@ public interface PrivilegePolicy {
* @throws AccessDeniedException * @throws AccessDeniedException
* if something goes wrong with the validate * if something goes wrong with the validate
*/ */
boolean hasPrivilege(PrivilegeContext context, IPrivilege privilege, Restrictable restrictable) boolean hasPrivilege(PrivilegeContext context, Privilege privilege, Restrictable restrictable)
throws PrivilegeException; throws PrivilegeException;
} }

View File

@ -20,7 +20,7 @@ import java.text.MessageFormat;
import li.strolch.privilege.base.AccessDeniedException; import li.strolch.privilege.base.AccessDeniedException;
import li.strolch.privilege.base.PrivilegeException; import li.strolch.privilege.base.PrivilegeException;
import li.strolch.privilege.i18n.PrivilegeMessages; import li.strolch.privilege.i18n.PrivilegeMessages;
import li.strolch.privilege.model.IPrivilege; import li.strolch.privilege.model.Privilege;
import li.strolch.privilege.model.PrivilegeContext; import li.strolch.privilege.model.PrivilegeContext;
import li.strolch.privilege.model.Restrictable; import li.strolch.privilege.model.Restrictable;
import li.strolch.utils.helper.StringHelper; import li.strolch.utils.helper.StringHelper;
@ -34,7 +34,7 @@ public class PrivilegePolicyHelper {
* Validates the given values and returns the privilege name * Validates the given values and returns the privilege name
* *
* @param privilege * @param privilege
* the {@link IPrivilege} * the {@link Privilege}
* @param restrictable * @param restrictable
* the {@link Restrictable} * the {@link Restrictable}
* *
@ -43,7 +43,7 @@ public class PrivilegePolicyHelper {
* @throws PrivilegeException * @throws PrivilegeException
* if something is wrong * if something is wrong
*/ */
public static String preValidate(IPrivilege privilege, Restrictable restrictable) throws PrivilegeException { public static String preValidate(Privilege privilege, Restrictable restrictable) throws PrivilegeException {
if (privilege == null) if (privilege == null)
throw new PrivilegeException(PrivilegeMessages.getString("Privilege.privilegeNull")); throw new PrivilegeException(PrivilegeMessages.getString("Privilege.privilegeNull"));
if (restrictable == null) if (restrictable == null)
@ -88,7 +88,7 @@ public class PrivilegePolicyHelper {
* @throws AccessDeniedException * @throws AccessDeniedException
* if access is denied * if access is denied
*/ */
public static boolean checkByAllowDenyValues(PrivilegeContext ctx, IPrivilege privilege, Restrictable restrictable, public static boolean checkByAllowDenyValues(PrivilegeContext ctx, Privilege privilege, Restrictable restrictable,
String privilegeValue, boolean assertHasPrivilege) throws AccessDeniedException { String privilegeValue, boolean assertHasPrivilege) throws AccessDeniedException {
// first check values not allowed // first check values not allowed
@ -102,7 +102,7 @@ public class PrivilegePolicyHelper {
return handleAccessDenied(ctx, privilege, restrictable, privilegeValue, assertHasPrivilege); return handleAccessDenied(ctx, privilege, restrictable, privilegeValue, assertHasPrivilege);
} }
private static boolean handleAccessDenied(PrivilegeContext ctx, IPrivilege privilege, Restrictable restrictable, private static boolean handleAccessDenied(PrivilegeContext ctx, Privilege privilege, Restrictable restrictable,
String privilegeValue, boolean assertHasPrivilege) { String privilegeValue, boolean assertHasPrivilege) {
if (assertHasPrivilege) { if (assertHasPrivilege) {

View File

@ -24,7 +24,7 @@ import li.strolch.privilege.base.AccessDeniedException;
import li.strolch.privilege.base.PrivilegeException; import li.strolch.privilege.base.PrivilegeException;
import li.strolch.privilege.handler.PrivilegeHandler; import li.strolch.privilege.handler.PrivilegeHandler;
import li.strolch.privilege.i18n.PrivilegeMessages; import li.strolch.privilege.i18n.PrivilegeMessages;
import li.strolch.privilege.model.IPrivilege; import li.strolch.privilege.model.Privilege;
import li.strolch.privilege.model.PrivilegeContext; import li.strolch.privilege.model.PrivilegeContext;
import li.strolch.privilege.model.Restrictable; import li.strolch.privilege.model.Restrictable;
import li.strolch.privilege.model.internal.Role; import li.strolch.privilege.model.internal.Role;
@ -42,18 +42,18 @@ import li.strolch.utils.dbc.DBC;
public class RoleAccessPrivilege implements PrivilegePolicy { public class RoleAccessPrivilege implements PrivilegePolicy {
@Override @Override
public void validateAction(PrivilegeContext ctx, IPrivilege privilege, Restrictable restrictable) public void validateAction(PrivilegeContext ctx, Privilege privilege, Restrictable restrictable)
throws AccessDeniedException { throws AccessDeniedException {
validateAction(ctx, privilege, restrictable, true); validateAction(ctx, privilege, restrictable, true);
} }
@Override @Override
public boolean hasPrivilege(PrivilegeContext ctx, IPrivilege privilege, Restrictable restrictable) public boolean hasPrivilege(PrivilegeContext ctx, Privilege privilege, Restrictable restrictable)
throws PrivilegeException { throws PrivilegeException {
return validateAction(ctx, privilege, restrictable, false); return validateAction(ctx, privilege, restrictable, false);
} }
private boolean validateAction(PrivilegeContext ctx, IPrivilege privilege, Restrictable restrictable, private boolean validateAction(PrivilegeContext ctx, Privilege privilege, Restrictable restrictable,
boolean assertHasPrivilege) throws AccessDeniedException { boolean assertHasPrivilege) throws AccessDeniedException {
String privilegeName = preValidate(privilege, restrictable); String privilegeName = preValidate(privilege, restrictable);

View File

@ -24,7 +24,7 @@ import li.strolch.privilege.base.AccessDeniedException;
import li.strolch.privilege.base.PrivilegeException; import li.strolch.privilege.base.PrivilegeException;
import li.strolch.privilege.handler.PrivilegeHandler; import li.strolch.privilege.handler.PrivilegeHandler;
import li.strolch.privilege.i18n.PrivilegeMessages; import li.strolch.privilege.i18n.PrivilegeMessages;
import li.strolch.privilege.model.IPrivilege; import li.strolch.privilege.model.Privilege;
import li.strolch.privilege.model.PrivilegeContext; import li.strolch.privilege.model.PrivilegeContext;
import li.strolch.privilege.model.Restrictable; import li.strolch.privilege.model.Restrictable;
import li.strolch.privilege.model.internal.User; import li.strolch.privilege.model.internal.User;
@ -41,18 +41,18 @@ import li.strolch.utils.dbc.DBC;
public class UserAccessPrivilege implements PrivilegePolicy { public class UserAccessPrivilege implements PrivilegePolicy {
@Override @Override
public void validateAction(PrivilegeContext ctx, IPrivilege privilege, Restrictable restrictable) public void validateAction(PrivilegeContext ctx, Privilege privilege, Restrictable restrictable)
throws AccessDeniedException { throws AccessDeniedException {
validateAction(ctx, privilege, restrictable, true); validateAction(ctx, privilege, restrictable, true);
} }
@Override @Override
public boolean hasPrivilege(PrivilegeContext ctx, IPrivilege privilege, Restrictable restrictable) public boolean hasPrivilege(PrivilegeContext ctx, Privilege privilege, Restrictable restrictable)
throws PrivilegeException { throws PrivilegeException {
return validateAction(ctx, privilege, restrictable, false); return validateAction(ctx, privilege, restrictable, false);
} }
protected boolean validateAction(PrivilegeContext ctx, IPrivilege privilege, Restrictable restrictable, protected boolean validateAction(PrivilegeContext ctx, Privilege privilege, Restrictable restrictable,
boolean assertHasPrivilege) throws AccessDeniedException { boolean assertHasPrivilege) throws AccessDeniedException {
String privilegeName = preValidate(privilege, restrictable); String privilegeName = preValidate(privilege, restrictable);

View File

@ -28,7 +28,7 @@ import li.strolch.privilege.base.AccessDeniedException;
import li.strolch.privilege.base.PrivilegeException; import li.strolch.privilege.base.PrivilegeException;
import li.strolch.privilege.handler.PrivilegeHandler; import li.strolch.privilege.handler.PrivilegeHandler;
import li.strolch.privilege.i18n.PrivilegeMessages; import li.strolch.privilege.i18n.PrivilegeMessages;
import li.strolch.privilege.model.IPrivilege; import li.strolch.privilege.model.Privilege;
import li.strolch.privilege.model.PrivilegeContext; import li.strolch.privilege.model.PrivilegeContext;
import li.strolch.privilege.model.Restrictable; import li.strolch.privilege.model.Restrictable;
import li.strolch.privilege.model.internal.User; import li.strolch.privilege.model.internal.User;
@ -43,13 +43,13 @@ import li.strolch.utils.dbc.DBC;
public class UserAccessWithSameOrganisationPrivilege extends UserAccessPrivilege { public class UserAccessWithSameOrganisationPrivilege extends UserAccessPrivilege {
@Override @Override
public void validateAction(PrivilegeContext ctx, IPrivilege privilege, Restrictable restrictable) public void validateAction(PrivilegeContext ctx, Privilege privilege, Restrictable restrictable)
throws AccessDeniedException { throws AccessDeniedException {
validateAction(ctx, privilege, restrictable, true); validateAction(ctx, privilege, restrictable, true);
} }
@Override @Override
public boolean hasPrivilege(PrivilegeContext ctx, IPrivilege privilege, Restrictable restrictable) public boolean hasPrivilege(PrivilegeContext ctx, Privilege privilege, Restrictable restrictable)
throws PrivilegeException { throws PrivilegeException {
return validateAction(ctx, privilege, restrictable, false); return validateAction(ctx, privilege, restrictable, false);
} }
@ -59,7 +59,7 @@ public class UserAccessWithSameOrganisationPrivilege extends UserAccessPrivilege
} }
@Override @Override
protected boolean validateAction(PrivilegeContext ctx, IPrivilege privilege, Restrictable restrictable, protected boolean validateAction(PrivilegeContext ctx, Privilege privilege, Restrictable restrictable,
boolean assertHasPrivilege) throws AccessDeniedException { boolean assertHasPrivilege) throws AccessDeniedException {
String privilegeName = preValidate(privilege, restrictable); String privilegeName = preValidate(privilege, restrictable);

View File

@ -24,7 +24,7 @@ import li.strolch.privilege.base.AccessDeniedException;
import li.strolch.privilege.base.PrivilegeException; import li.strolch.privilege.base.PrivilegeException;
import li.strolch.privilege.i18n.PrivilegeMessages; import li.strolch.privilege.i18n.PrivilegeMessages;
import li.strolch.privilege.model.Certificate; import li.strolch.privilege.model.Certificate;
import li.strolch.privilege.model.IPrivilege; import li.strolch.privilege.model.Privilege;
import li.strolch.privilege.model.PrivilegeContext; import li.strolch.privilege.model.PrivilegeContext;
import li.strolch.privilege.model.Restrictable; import li.strolch.privilege.model.Restrictable;
@ -44,18 +44,18 @@ import li.strolch.privilege.model.Restrictable;
public class UsernameFromCertificatePrivilege implements PrivilegePolicy { public class UsernameFromCertificatePrivilege implements PrivilegePolicy {
@Override @Override
public void validateAction(PrivilegeContext ctx, IPrivilege privilege, Restrictable restrictable) public void validateAction(PrivilegeContext ctx, Privilege privilege, Restrictable restrictable)
throws AccessDeniedException { throws AccessDeniedException {
validateAction(ctx, privilege, restrictable, true); validateAction(ctx, privilege, restrictable, true);
} }
@Override @Override
public boolean hasPrivilege(PrivilegeContext ctx, IPrivilege privilege, Restrictable restrictable) public boolean hasPrivilege(PrivilegeContext ctx, Privilege privilege, Restrictable restrictable)
throws PrivilegeException { throws PrivilegeException {
return validateAction(ctx, privilege, restrictable, false); return validateAction(ctx, privilege, restrictable, false);
} }
protected boolean validateAction(PrivilegeContext ctx, IPrivilege privilege, Restrictable restrictable, protected boolean validateAction(PrivilegeContext ctx, Privilege privilege, Restrictable restrictable,
boolean assertHasPrivilege) throws AccessDeniedException { boolean assertHasPrivilege) throws AccessDeniedException {
preValidate(privilege, restrictable); preValidate(privilege, restrictable);

View File

@ -28,7 +28,7 @@ import li.strolch.privilege.base.AccessDeniedException;
import li.strolch.privilege.base.PrivilegeException; import li.strolch.privilege.base.PrivilegeException;
import li.strolch.privilege.i18n.PrivilegeMessages; import li.strolch.privilege.i18n.PrivilegeMessages;
import li.strolch.privilege.model.Certificate; import li.strolch.privilege.model.Certificate;
import li.strolch.privilege.model.IPrivilege; import li.strolch.privilege.model.Privilege;
import li.strolch.privilege.model.PrivilegeContext; import li.strolch.privilege.model.PrivilegeContext;
import li.strolch.privilege.model.Restrictable; import li.strolch.privilege.model.Restrictable;
@ -48,19 +48,19 @@ import li.strolch.privilege.model.Restrictable;
public class UsernameFromCertificateWithSameOrganisationPrivilege extends UsernameFromCertificatePrivilege { public class UsernameFromCertificateWithSameOrganisationPrivilege extends UsernameFromCertificatePrivilege {
@Override @Override
public void validateAction(PrivilegeContext ctx, IPrivilege privilege, Restrictable restrictable) public void validateAction(PrivilegeContext ctx, Privilege privilege, Restrictable restrictable)
throws AccessDeniedException { throws AccessDeniedException {
validateAction(ctx, privilege, restrictable, true); validateAction(ctx, privilege, restrictable, true);
} }
@Override @Override
public boolean hasPrivilege(PrivilegeContext ctx, IPrivilege privilege, Restrictable restrictable) public boolean hasPrivilege(PrivilegeContext ctx, Privilege privilege, Restrictable restrictable)
throws PrivilegeException { throws PrivilegeException {
return validateAction(ctx, privilege, restrictable, false); return validateAction(ctx, privilege, restrictable, false);
} }
@Override @Override
protected boolean validateAction(PrivilegeContext ctx, IPrivilege privilege, Restrictable restrictable, protected boolean validateAction(PrivilegeContext ctx, Privilege privilege, Restrictable restrictable,
boolean assertHasPrivilege) throws AccessDeniedException { boolean assertHasPrivilege) throws AccessDeniedException {
preValidate(privilege, restrictable); preValidate(privilege, restrictable);

View File

@ -1,87 +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.xml;
import static java.util.Comparator.comparing;
import static li.strolch.privilege.helper.XmlConstants.*;
import java.io.OutputStream;
import java.util.List;
import li.strolch.privilege.model.Certificate;
import li.strolch.utils.helper.XmlHelper;
import li.strolch.utils.iso8601.ISO8601;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*/
public class CertificateStubsDomWriter {
private final List<Certificate> certificates;
private final OutputStream outputStream;
public CertificateStubsDomWriter(List<Certificate> certificates, OutputStream outputStream) {
this.certificates = certificates;
this.outputStream = outputStream;
}
public void write() {
// create document root
Document doc = XmlHelper.createDocument();
Element rootElement = doc.createElement(XML_ROOT_CERTIFICATES);
doc.appendChild(rootElement);
this.certificates.stream().sorted(comparing(Certificate::getSessionId)).forEach(cert -> {
// create the certificate element
Element certElement = doc.createElement(XML_CERTIFICATE);
rootElement.appendChild(certElement);
// sessionId;
certElement.setAttribute(XML_ATTR_SESSION_ID, cert.getSessionId());
// usage;
certElement.setAttribute(XML_ATTR_USAGE, cert.getUsage().name());
// username;
certElement.setAttribute(XML_ATTR_USERNAME, cert.getUsername());
// authToken;
certElement.setAttribute(XML_ATTR_AUTH_TOKEN, cert.getAuthToken());
// source;
certElement.setAttribute(XML_ATTR_SOURCE, cert.getSource());
// locale;
certElement.setAttribute(XML_ATTR_LOCALE, cert.getLocale().toLanguageTag());
// loginTime;
certElement.setAttribute(XML_ATTR_LOGIN_TIME, ISO8601.toString(cert.getLoginTime()));
// lastAccess;
certElement.setAttribute(XML_ATTR_LAST_ACCESS, ISO8601.toString(cert.getLastAccess()));
// keepAlive;
certElement.setAttribute(XML_ATTR_KEEP_ALIVE, String.valueOf(cert.isKeepAlive()));
});
// write the container file to disk
XmlHelper.writeDocument(doc, this.outputStream);
}
}

View File

@ -15,16 +15,6 @@
*/ */
package li.strolch.privilege.xml; package li.strolch.privilege.xml;
import static li.strolch.privilege.handler.DefaultPrivilegeHandler.SOURCE_UNKNOWN;
import static li.strolch.privilege.helper.XmlConstants.*;
import static li.strolch.utils.helper.StringHelper.isEmpty;
import java.io.InputStream;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import li.strolch.privilege.base.PrivilegeException; import li.strolch.privilege.base.PrivilegeException;
import li.strolch.privilege.model.Usage; import li.strolch.privilege.model.Usage;
import li.strolch.utils.dbc.DBC; import li.strolch.utils.dbc.DBC;
@ -33,6 +23,16 @@ import li.strolch.utils.iso8601.ISO8601;
import org.xml.sax.Attributes; import org.xml.sax.Attributes;
import org.xml.sax.helpers.DefaultHandler; import org.xml.sax.helpers.DefaultHandler;
import java.io.InputStream;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import static li.strolch.privilege.handler.DefaultPrivilegeHandler.SOURCE_UNKNOWN;
import static li.strolch.privilege.helper.XmlConstants.*;
import static li.strolch.utils.helper.StringHelper.isEmpty;
/** /**
* @author Robert von Burg <eitch@eitchnet.ch> * @author Robert von Burg <eitch@eitchnet.ch>
*/ */
@ -55,33 +55,27 @@ public class CertificateStubsSaxReader extends DefaultHandler {
public void startElement(String uri, String localName, String qName, Attributes attributes) { public void startElement(String uri, String localName, String qName, Attributes attributes) {
switch (qName) { switch (qName) {
case XML_ROOT_CERTIFICATES: case ROOT_CERTIFICATES -> {
break; }
case XML_CERTIFICATE: case CERTIFICATE -> {
CertificateStub stub = new CertificateStub();
CertificateStub stub = new CertificateStub(); stub.usage = Usage.valueOf(attributes.getValue(ATTR_USAGE).trim());
stub.usage = Usage.valueOf(attributes.getValue(XML_ATTR_USAGE).trim()); stub.sessionId = attributes.getValue(ATTR_SESSION_ID).trim();
stub.sessionId = attributes.getValue(XML_ATTR_SESSION_ID).trim(); stub.username = attributes.getValue(ATTR_USERNAME).trim();
stub.username = attributes.getValue(XML_ATTR_USERNAME).trim(); stub.authToken = attributes.getValue(ATTR_AUTH_TOKEN).trim();
stub.authToken = attributes.getValue(XML_ATTR_AUTH_TOKEN).trim(); stub.source = attributes.getValue(ATTR_SOURCE).trim();
stub.source = attributes.getValue(XML_ATTR_SOURCE).trim(); stub.locale = Locale.forLanguageTag(attributes.getValue(ATTR_LOCALE).trim());
stub.locale = Locale.forLanguageTag(attributes.getValue(XML_ATTR_LOCALE).trim()); stub.loginTime = ISO8601.parseToZdt(attributes.getValue(ATTR_LOGIN_TIME).trim());
stub.loginTime = ISO8601.parseToZdt(attributes.getValue(XML_ATTR_LOGIN_TIME).trim()); stub.lastAccess = ISO8601.parseToZdt(attributes.getValue(ATTR_LAST_ACCESS).trim());
stub.lastAccess = ISO8601.parseToZdt(attributes.getValue(XML_ATTR_LAST_ACCESS).trim()); stub.keepAlive = Boolean.parseBoolean(attributes.getValue(ATTR_KEEP_ALIVE).trim());
stub.keepAlive = Boolean.parseBoolean(attributes.getValue(XML_ATTR_KEEP_ALIVE).trim()); DBC.INTERIM.assertNotEmpty("sessionId missing on sessions data!", stub.sessionId);
DBC.INTERIM.assertNotEmpty("username missing on sessions data!", stub.username);
DBC.INTERIM.assertNotEmpty("sessionId missing on sessions data!", stub.sessionId); DBC.INTERIM.assertNotEmpty("authToken missing on sessions data!", stub.authToken);
DBC.INTERIM.assertNotEmpty("username missing on sessions data!", stub.username); if (isEmpty(stub.source))
DBC.INTERIM.assertNotEmpty("authToken missing on sessions data!", stub.authToken); stub.source = SOURCE_UNKNOWN;
this.stubs.add(stub);
if (isEmpty(stub.source)) }
stub.source = SOURCE_UNKNOWN; default -> throw new PrivilegeException("Unhandled tag " + qName);
this.stubs.add(stub);
break;
default:
throw new PrivilegeException("Unhandled tag " + qName);
} }
} }

View File

@ -0,0 +1,94 @@
/*
* 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.xml;
import javanet.staxutils.IndentingXMLStreamWriter;
import li.strolch.privilege.model.Certificate;
import li.strolch.utils.iso8601.ISO8601;
import javax.xml.stream.XMLStreamException;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import static java.util.Comparator.comparing;
import static li.strolch.privilege.helper.XmlConstants.*;
import static li.strolch.privilege.helper.XmlHelper.openXmlStreamWriterDocument;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*/
public class CertificateStubsSaxWriter {
private final List<Certificate> certificates;
private final OutputStream outputStream;
public CertificateStubsSaxWriter(List<Certificate> certificates, OutputStream outputStream) {
this.certificates = certificates;
this.outputStream = outputStream;
}
public void write() throws IOException, XMLStreamException {
Writer ioWriter = new OutputStreamWriter(this.outputStream, StandardCharsets.UTF_8);
IndentingXMLStreamWriter xmlWriter = openXmlStreamWriterDocument(ioWriter);
xmlWriter.writeStartElement(ROOT_CERTIFICATES);
List<Certificate> certificates = new ArrayList<>(this.certificates);
certificates.sort(comparing(Certificate::getSessionId));
for (Certificate cert : certificates) {
// create the certificate element
xmlWriter.writeStartElement(CERTIFICATE);
// sessionId;
xmlWriter.writeAttribute(ATTR_SESSION_ID, cert.getSessionId());
// usage;
xmlWriter.writeAttribute(ATTR_USAGE, cert.getUsage().name());
// username;
xmlWriter.writeAttribute(ATTR_USERNAME, cert.getUsername());
// authToken;
xmlWriter.writeAttribute(ATTR_AUTH_TOKEN, cert.getAuthToken());
// source;
xmlWriter.writeAttribute(ATTR_SOURCE, cert.getSource());
// locale;
xmlWriter.writeAttribute(ATTR_LOCALE, cert.getLocale().toLanguageTag());
// loginTime;
xmlWriter.writeAttribute(ATTR_LOGIN_TIME, ISO8601.toString(cert.getLoginTime()));
// lastAccess;
xmlWriter.writeAttribute(ATTR_LAST_ACCESS, ISO8601.toString(cert.getLastAccess()));
// keepAlive;
xmlWriter.writeAttribute(ATTR_KEEP_ALIVE, String.valueOf(cert.isKeepAlive()));
}
// and now end
xmlWriter.writeEndDocument();
xmlWriter.flush();
}
}

View File

@ -17,7 +17,6 @@ package li.strolch.privilege.xml;
import org.xml.sax.Attributes; import org.xml.sax.Attributes;
// TODO write JavaDoc...
public interface ElementParser { public interface ElementParser {
void startElement(String uri, String localName, String qName, Attributes attributes); void startElement(String uri, String localName, String qName, Attributes attributes);

View File

@ -1,129 +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.xml;
import static li.strolch.privilege.helper.XmlConstants.*;
import java.io.File;
import java.util.Map;
import java.util.Map.Entry;
import li.strolch.privilege.model.internal.PrivilegeContainerModel;
import li.strolch.utils.helper.XmlHelper;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*/
public class PrivilegeConfigDomWriter {
private final PrivilegeContainerModel containerModel;
private final File configFile;
/**
*
*/
public PrivilegeConfigDomWriter(PrivilegeContainerModel containerModel, File configFile) {
this.containerModel = containerModel;
this.configFile = configFile;
}
/**
*
*/
public void write() {
// create document root
Document doc = XmlHelper.createDocument();
Element rootElement = doc.createElement(XML_ROOT_PRIVILEGE);
doc.appendChild(rootElement);
Element containerElement = doc.createElement(XML_CONTAINER);
rootElement.appendChild(containerElement);
// create EncryptionHandler
Element encryptionHandlerElem = doc.createElement(XML_HANDLER_ENCRYPTION);
containerElement.appendChild(encryptionHandlerElem);
encryptionHandlerElem.setAttribute(XML_ATTR_CLASS, this.containerModel.getEncryptionHandlerClassName());
// Parameters
fillParameterMap(doc, encryptionHandlerElem, this.containerModel.getEncryptionHandlerParameterMap());
// create PersistenceHandler
Element persistenceHandlerElem = doc.createElement(XML_HANDLER_PERSISTENCE);
containerElement.appendChild(persistenceHandlerElem);
persistenceHandlerElem.setAttribute(XML_ATTR_CLASS, this.containerModel.getPersistenceHandlerClassName());
// Parameters
fillParameterMap(doc, persistenceHandlerElem, this.containerModel.getPersistenceHandlerParameterMap());
// Parameters
fillParameterMap(doc, containerElement, this.containerModel.getParameterMap());
// create UserChallengeHandler
Element userChallengeHandlerElem = doc.createElement(XML_HANDLER_USER_CHALLENGE);
containerElement.appendChild(userChallengeHandlerElem);
userChallengeHandlerElem.setAttribute(XML_ATTR_CLASS, this.containerModel.getUserChallengeHandlerClassName());
// Parameters
fillParameterMap(doc, userChallengeHandlerElem, this.containerModel.getUserChallengeHandlerParameterMap());
// create SingleSignOnHandler
if (this.containerModel.getSsoHandlerClassName() != null) {
Element ssoHandlerElem = doc.createElement(XML_HANDLER_SSO);
containerElement.appendChild(ssoHandlerElem);
ssoHandlerElem.setAttribute(XML_ATTR_CLASS, this.containerModel.getSsoHandlerClassName());
// Parameters
fillParameterMap(doc, ssoHandlerElem, this.containerModel.getSsoHandlerParameterMap());
}
// create PrivilegeHandler
if (this.containerModel.getSsoHandlerClassName() != null) {
Element privilegeHandlerElem = doc.createElement(XML_HANDLER_PRIVILEGE);
containerElement.appendChild(privilegeHandlerElem);
privilegeHandlerElem.setAttribute(XML_ATTR_CLASS, this.containerModel.getPrivilegeHandlerClassName());
// Parameters
fillParameterMap(doc, privilegeHandlerElem, this.containerModel.getPrivilegeHandlerParameterMap());
}
// Policies
Element policiesElem = doc.createElement(XML_POLICIES);
rootElement.appendChild(policiesElem);
this.containerModel.getPolicies().entrySet().stream().sorted(Entry.comparingByKey())
.forEach(entry -> {
Element policyElem = doc.createElement(XML_POLICY);
policyElem.setAttribute(XML_ATTR_NAME, entry.getKey());
policyElem.setAttribute(XML_ATTR_CLASS, entry.getValue().getName());
policiesElem.appendChild(policyElem);
});
// write the container file to disk
XmlHelper.writeDocument(doc, this.configFile);
}
private void fillParameterMap(Document doc, Element parent, Map<String, String> parameterMap) {
if (parameterMap != null && !parameterMap.isEmpty()) {
Element parametersElement = doc.createElement(XML_PARAMETERS);
for (Entry<String, String> entry : parameterMap.entrySet()) {
Element parameterElement = doc.createElement(XML_PARAMETER);
parameterElement.setAttribute(XML_ATTR_NAME, entry.getKey());
parameterElement.setAttribute(XML_ATTR_VALUE, entry.getValue());
parametersElement.appendChild(parameterElement);
}
parent.appendChild(parametersElement);
}
}
}

View File

@ -15,18 +15,17 @@
*/ */
package li.strolch.privilege.xml; package li.strolch.privilege.xml;
import static li.strolch.privilege.helper.XmlConstants.*; import li.strolch.privilege.model.internal.PrivilegeContainerModel;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import java.util.Deque; import java.util.Deque;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import li.strolch.privilege.helper.XmlConstants; import static li.strolch.privilege.helper.XmlConstants.*;
import li.strolch.privilege.model.internal.PrivilegeContainerModel;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
/** /**
* @author Robert von Burg <eitch@eitchnet.ch> * @author Robert von Burg <eitch@eitchnet.ch>
@ -48,12 +47,12 @@ public class PrivilegeConfigSaxReader extends DefaultHandler {
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
switch (qName) { switch (qName) {
case XML_CONTAINER -> this.buildersStack.push(new ContainerParser()); case CONTAINER -> this.buildersStack.push(new ContainerParser());
case XML_PARAMETERS -> this.buildersStack.push(new ParametersParser()); case PARAMETERS -> this.buildersStack.push(new ParametersParser());
case XML_POLICIES -> this.buildersStack.push(new PoliciesParser()); case POLICIES -> this.buildersStack.push(new PoliciesParser());
default -> { default -> {
// nothing to do, probably handle on stack // nothing to do, probably handle on stack
} }
} }
if (!this.buildersStack.isEmpty()) if (!this.buildersStack.isEmpty())
@ -73,7 +72,7 @@ public class PrivilegeConfigSaxReader extends DefaultHandler {
this.buildersStack.peek().endElement(uri, localName, qName); this.buildersStack.peek().endElement(uri, localName, qName);
ElementParser elementParser = switch (qName) { ElementParser elementParser = switch (qName) {
case XML_CONTAINER, XML_PARAMETERS, XML_POLICIES -> this.buildersStack.pop(); case CONTAINER, PARAMETERS, POLICIES -> this.buildersStack.pop();
default -> null; default -> null;
}; };
@ -89,38 +88,38 @@ public class PrivilegeConfigSaxReader extends DefaultHandler {
public void startElement(String uri, String localName, String qName, Attributes attributes) { public void startElement(String uri, String localName, String qName, Attributes attributes) {
switch (qName) { switch (qName) {
case XML_CONTAINER -> this.currentElement = qName; case CONTAINER -> this.currentElement = qName;
case XML_HANDLER_ENCRYPTION -> { case HANDLER_PRIVILEGE -> {
this.currentElement = qName; this.currentElement = qName;
String className = attributes.getValue(XML_ATTR_CLASS).trim(); String className = attributes.getValue(ATTR_CLASS).trim();
getContainerModel().setEncryptionHandlerClassName(className); getContainerModel().setPrivilegeHandlerClassName(className);
} }
case XML_HANDLER_PASSWORD_STRENGTH -> { case HANDLER_ENCRYPTION -> {
this.currentElement = qName; this.currentElement = qName;
String className = attributes.getValue(XML_ATTR_CLASS).trim(); String className = attributes.getValue(ATTR_CLASS).trim();
getContainerModel().setPasswordStrengthHandlerClassName(className); getContainerModel().setEncryptionHandlerClassName(className);
} }
case XML_HANDLER_PERSISTENCE -> { case HANDLER_PASSWORD_STRENGTH -> {
this.currentElement = qName; this.currentElement = qName;
String className = attributes.getValue(XML_ATTR_CLASS).trim(); String className = attributes.getValue(ATTR_CLASS).trim();
getContainerModel().setPersistenceHandlerClassName(className); getContainerModel().setPasswordStrengthHandlerClassName(className);
} }
case XML_HANDLER_USER_CHALLENGE -> { case HANDLER_PERSISTENCE -> {
this.currentElement = qName; this.currentElement = qName;
String className = attributes.getValue(XML_ATTR_CLASS).trim(); String className = attributes.getValue(ATTR_CLASS).trim();
getContainerModel().setUserChallengeHandlerClassName(className); getContainerModel().setPersistenceHandlerClassName(className);
} }
case XML_HANDLER_SSO -> { case HANDLER_USER_CHALLENGE -> {
this.currentElement = qName; this.currentElement = qName;
String className = attributes.getValue(XML_ATTR_CLASS).trim(); String className = attributes.getValue(ATTR_CLASS).trim();
getContainerModel().setSsoHandlerClassName(className); getContainerModel().setUserChallengeHandlerClassName(className);
} }
case XML_HANDLER_PRIVILEGE -> { case HANDLER_SSO -> {
this.currentElement = qName; this.currentElement = qName;
String className = attributes.getValue(XML_ATTR_CLASS).trim(); String className = attributes.getValue(ATTR_CLASS).trim();
getContainerModel().setPrivilegeHandlerClassName(className); getContainerModel().setSsoHandlerClassName(className);
} }
default -> throw new IllegalStateException("Unexpected value: " + qName); default -> throw new IllegalStateException("Unexpected value: " + qName);
} }
} }
@ -129,20 +128,17 @@ public class PrivilegeConfigSaxReader extends DefaultHandler {
if (!(child instanceof ParametersParser parametersChild)) if (!(child instanceof ParametersParser parametersChild))
return; return;
final Map<String, String> params = parametersChild.getParameterMap();
switch (this.currentElement) { switch (this.currentElement) {
case XML_CONTAINER -> getContainerModel().setParameterMap(parametersChild.getParameterMap()); case CONTAINER -> getContainerModel().setParameterMap(params);
case XML_HANDLER_ENCRYPTION -> case HANDLER_PRIVILEGE -> getContainerModel().setPrivilegeHandlerParameterMap(params);
getContainerModel().setEncryptionHandlerParameterMap(parametersChild.getParameterMap()); case HANDLER_ENCRYPTION -> getContainerModel().setEncryptionHandlerParameterMap(params);
case XML_HANDLER_PASSWORD_STRENGTH -> case HANDLER_PASSWORD_STRENGTH ->
getContainerModel().setPasswordStrengthHandlerParameterMap(parametersChild.getParameterMap()); getContainerModel().setPasswordStrengthHandlerParameterMap(params);
case XML_HANDLER_PERSISTENCE -> case HANDLER_PERSISTENCE -> getContainerModel().setPersistenceHandlerParameterMap(params);
getContainerModel().setPersistenceHandlerParameterMap(parametersChild.getParameterMap()); case HANDLER_USER_CHALLENGE -> getContainerModel().setUserChallengeHandlerParameterMap(params);
case XML_HANDLER_USER_CHALLENGE -> case HANDLER_SSO -> getContainerModel().setSsoHandlerParameterMap(params);
getContainerModel().setUserChallengeHandlerParameterMap(parametersChild.getParameterMap()); default -> throw new IllegalStateException("Unexpected value: " + this.currentElement);
case XML_HANDLER_SSO -> getContainerModel().setSsoHandlerParameterMap(parametersChild.getParameterMap());
case XML_HANDLER_PRIVILEGE ->
getContainerModel().setPrivilegeHandlerParameterMap(parametersChild.getParameterMap());
default -> throw new IllegalStateException("Unexpected value: " + this.currentElement);
} }
} }
} }
@ -155,9 +151,9 @@ public class PrivilegeConfigSaxReader extends DefaultHandler {
@Override @Override
public void startElement(String uri, String localName, String qName, Attributes attributes) { public void startElement(String uri, String localName, String qName, Attributes attributes) {
if (qName.equals(XML_PARAMETER)) { if (qName.equals(PARAMETER)) {
String key = attributes.getValue(XML_ATTR_NAME).trim(); String key = attributes.getValue(ATTR_NAME).trim();
String value = attributes.getValue(XML_ATTR_VALUE).trim(); String value = attributes.getValue(ATTR_VALUE).trim();
this.parameterMap.put(key, value); this.parameterMap.put(key, value);
} }
} }
@ -176,9 +172,9 @@ public class PrivilegeConfigSaxReader extends DefaultHandler {
@Override @Override
public void startElement(String uri, String localName, String qName, Attributes attributes) { public void startElement(String uri, String localName, String qName, Attributes attributes) {
if (qName.equals(XML_POLICY)) { if (qName.equals(POLICY)) {
String policyName = attributes.getValue(XML_ATTR_NAME).trim(); String policyName = attributes.getValue(ATTR_NAME).trim();
String policyClassName = attributes.getValue(XML_ATTR_CLASS).trim(); String policyClassName = attributes.getValue(ATTR_CLASS).trim();
getContainerModel().addPolicy(policyName, policyClassName); getContainerModel().addPolicy(policyName, policyClassName);
} }

View File

@ -0,0 +1,113 @@
/*
* 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.xml;
import javanet.staxutils.IndentingXMLStreamWriter;
import li.strolch.privilege.model.internal.PrivilegeContainerModel;
import javax.xml.stream.XMLStreamException;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import static li.strolch.privilege.helper.XmlConstants.*;
import static li.strolch.privilege.helper.XmlHelper.openXmlStreamWriterDocument;
import static li.strolch.privilege.helper.XmlHelper.writeStringMapElement;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*/
public class PrivilegeConfigSaxWriter {
private final PrivilegeContainerModel containerModel;
private final File configFile;
public PrivilegeConfigSaxWriter(PrivilegeContainerModel containerModel, File configFile) {
this.containerModel = containerModel;
this.configFile = configFile;
}
public void write() throws IOException, XMLStreamException {
try (Writer ioWriter = new OutputStreamWriter(new FileOutputStream(this.configFile), StandardCharsets.UTF_8)) {
IndentingXMLStreamWriter xmlWriter = openXmlStreamWriterDocument(ioWriter);
xmlWriter.writeStartElement(ROOT_PRIVILEGE);
// write container element
xmlWriter.writeStartElement(CONTAINER);
writeStringMapElement(xmlWriter, this.containerModel.getParameterMap(), PARAMETERS, PARAMETER);
{
// write PrivilegeHandler
if (this.containerModel.getPrivilegeHandlerClassName() != null)
writeHandler(xmlWriter, HANDLER_PRIVILEGE, this.containerModel.getPrivilegeHandlerClassName(),
this.containerModel.getPrivilegeHandlerParameterMap());
// write EncryptionHandler
writeHandler(xmlWriter, HANDLER_ENCRYPTION, this.containerModel.getEncryptionHandlerClassName(),
this.containerModel.getEncryptionHandlerParameterMap());
// write PersistenceHandler
writeHandler(xmlWriter, HANDLER_PERSISTENCE, this.containerModel.getPersistenceHandlerClassName(),
this.containerModel.getPersistenceHandlerParameterMap());
// write PasswordStrengthHandler
if (this.containerModel.getPasswordStrengthHandlerClassName() != null)
writeHandler(xmlWriter, HANDLER_PASSWORD_STRENGTH,
this.containerModel.getPasswordStrengthHandlerClassName(),
this.containerModel.getPasswordStrengthHandlerParameterMap());
// write UserChallengeHandler
if (this.containerModel.getUserChallengeHandlerClassName() != null)
writeHandler(xmlWriter, HANDLER_USER_CHALLENGE,
this.containerModel.getUserChallengeHandlerClassName(),
this.containerModel.getUserChallengeHandlerParameterMap());
// write SingleSignOnHandler
if (this.containerModel.getSsoHandlerClassName() != null)
writeHandler(xmlWriter, HANDLER_SSO, this.containerModel.getSsoHandlerClassName(),
this.containerModel.getSsoHandlerParameterMap());
}
xmlWriter.writeEndElement();
// Policies
Map<String, String> policies = new HashMap<>();
this.containerModel.getPolicies().forEach((key, value) -> policies.put(key, value.getName()));
writeStringMapElement(xmlWriter, policies, POLICIES, POLICY, ATTR_CLASS);
// and now end
xmlWriter.writeEndDocument();
xmlWriter.flush();
}
}
private void writeHandler(IndentingXMLStreamWriter xmlWriter, String handleName, String className,
Map<String, String> parameters) throws XMLStreamException {
if (parameters.isEmpty())
xmlWriter.writeEmptyElement(handleName);
else
xmlWriter.writeStartElement(handleName);
xmlWriter.writeAttribute(ATTR_CLASS, className);
writeStringMapElement(xmlWriter, parameters, PARAMETERS, PARAMETER);
if (!parameters.isEmpty())
xmlWriter.writeEndElement();
}
}

View File

@ -0,0 +1,174 @@
/*
* 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.xml;
import li.strolch.privilege.model.internal.Group;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import java.text.MessageFormat;
import java.util.*;
import static li.strolch.privilege.helper.XmlConstants.*;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*/
public class PrivilegeGroupsSaxReader extends DefaultHandler {
protected static final Logger logger = LoggerFactory.getLogger(PrivilegeGroupsSaxReader.class);
private final Deque<ElementParser> buildersStack = new ArrayDeque<>();
private final Map<String, Group> groups;
public PrivilegeGroupsSaxReader() {
this.groups = new HashMap<>();
}
/**
* @return the users
*/
public Map<String, Group> getGroups() {
return this.groups;
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
if (qName.equals(GROUP)) {
if (this.buildersStack.stream().anyMatch(e -> e.getClass().equals(GroupParser.class)))
throw new IllegalArgumentException("Previous Group not closed!");
this.buildersStack.push(new GroupParser());
} else if (qName.equals(PROPERTIES)) {
if (this.buildersStack.stream().anyMatch(e -> e.getClass().equals(PropertyParser.class)))
throw new IllegalArgumentException("Previous Properties not closed!");
this.buildersStack.push(new PropertyParser());
}
if (!this.buildersStack.isEmpty())
this.buildersStack.peek().startElement(uri, localName, qName, attributes);
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
if (!this.buildersStack.isEmpty())
this.buildersStack.peek().characters(ch, start, length);
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
if (!this.buildersStack.isEmpty())
this.buildersStack.peek().endElement(uri, localName, qName);
ElementParser elementParser = null;
if (qName.equals(GROUP)) {
elementParser = this.buildersStack.pop();
} else if (qName.equals(PROPERTIES)) {
elementParser = this.buildersStack.pop();
}
if (!this.buildersStack.isEmpty() && elementParser != null)
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>
public class GroupParser extends ElementParserAdapter {
StringBuilder text;
String name;
final Set<String> roles;
Map<String, String> parameters;
public GroupParser() {
this.roles = new HashSet<>();
this.parameters = new HashMap<>();
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) {
this.text = new StringBuilder();
if (qName.equals(GROUP)) {
this.name = attributes.getValue(ATTR_NAME).trim();
}
}
@Override
public void characters(char[] ch, int start, int length) {
this.text.append(ch, start, length);
}
@Override
public void endElement(String uri, String localName, String qName) {
switch (qName) {
case ROLE -> this.roles.add(getText());
case GROUP -> {
Group group = new Group(this.name, this.roles, this.parameters);
logger.info(MessageFormat.format("New Group: {0}", group));
groups.put(this.name, group);
}
default -> {
if (!(qName.equals(GROUPS) //
|| qName.equals(ROLES) //
|| qName.equals(PARAMETER) //
|| qName.equals(PARAMETERS))) {
throw new IllegalArgumentException("Unhandled tag " + qName);
}
}
}
}
private String getText() {
return this.text.toString().trim();
}
@Override
public void notifyChild(ElementParser child) {
if (child instanceof PropertyParser) {
this.parameters = ((PropertyParser) child).parameterMap;
}
}
}
}

View File

@ -0,0 +1,82 @@
/*
* 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.xml;
import javanet.staxutils.IndentingXMLStreamWriter;
import li.strolch.privilege.model.internal.Group;
import javax.xml.stream.XMLStreamException;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import static java.util.Comparator.comparing;
import static li.strolch.privilege.helper.XmlConstants.*;
import static li.strolch.privilege.helper.XmlHelper.*;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*/
public class PrivilegeGroupsSaxWriter {
private final List<Group> groups;
private final File modelFile;
public PrivilegeGroupsSaxWriter(List<Group> groups, File modelFile) {
this.groups = groups;
this.modelFile = modelFile;
}
public void write() throws IOException, XMLStreamException {
try (Writer ioWriter = new OutputStreamWriter(new FileOutputStream(this.modelFile), StandardCharsets.UTF_8)) {
IndentingXMLStreamWriter xmlWriter = openXmlStreamWriterDocument(ioWriter);
xmlWriter.writeStartElement(GROUPS);
List<Group> groups = new ArrayList<>(this.groups);
groups.sort(comparing(g -> g.name().toLowerCase(Locale.ROOT)));
for (Group group : this.groups) {
// start the user element
xmlWriter.writeStartElement(GROUP);
xmlWriter.writeAttribute(ATTR_NAME, group.name());
// add all the role elements
if (!group.roles().isEmpty()) {
xmlWriter.writeStartElement(ROLES);
writeStringList(xmlWriter, ROLE, group.roles());
xmlWriter.writeEndElement();
}
// add the parameters
Map<String, String> properties = group.getProperties();
if (!properties.isEmpty())
writeStringMapElement(xmlWriter, properties, PROPERTIES, PROPERTY);
xmlWriter.writeEndElement();
}
// and now end
xmlWriter.writeEndDocument();
xmlWriter.flush();
}
}
}

View File

@ -1,87 +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.xml;
import static java.util.Comparator.comparing;
import java.io.File;
import java.util.List;
import java.util.Locale;
import li.strolch.privilege.helper.XmlConstants;
import li.strolch.privilege.model.internal.Role;
import li.strolch.utils.helper.XmlHelper;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*/
public class PrivilegeRolesDomWriter {
private final List<Role> roles;
private final File modelFile;
public PrivilegeRolesDomWriter(List<Role> roles, File modelFile) {
this.roles = roles;
this.modelFile = modelFile;
}
public void write() {
// create document root
Document doc = XmlHelper.createDocument();
Element rootElement = doc.createElement(XmlConstants.XML_ROLES);
doc.appendChild(rootElement);
this.roles.stream().sorted(comparing(role1 -> role1.getName().toLowerCase(Locale.ROOT))).forEach(role -> {
// create the role element
Element roleElement = doc.createElement(XmlConstants.XML_ROLE);
rootElement.appendChild(roleElement);
roleElement.setAttribute(XmlConstants.XML_ATTR_NAME, role.getName());
role.getPrivilegeNames().stream().sorted().map(role::getPrivilege).forEach(privilege -> {
Element privilegeElement = doc.createElement(XmlConstants.XML_PRIVILEGE);
roleElement.appendChild(privilegeElement);
privilegeElement.setAttribute(XmlConstants.XML_ATTR_NAME, privilege.getName());
privilegeElement.setAttribute(XmlConstants.XML_ATTR_POLICY, privilege.getPolicy());
if (privilege.isAllAllowed()) {
Element allAllowedElement = doc.createElement(XmlConstants.XML_ALL_ALLOWED);
allAllowedElement.setTextContent(Boolean.toString(privilege.isAllAllowed()));
privilegeElement.appendChild(allAllowedElement);
}
privilege.getDenyList().stream().sorted().forEach(denyValue -> {
Element denyValueElement = doc.createElement(XmlConstants.XML_DENY);
denyValueElement.setTextContent(denyValue);
privilegeElement.appendChild(denyValueElement);
});
privilege.getAllowList().stream().sorted().forEach(allowValue -> {
Element allowValueElement = doc.createElement(XmlConstants.XML_ALLOW);
allowValueElement.setTextContent(allowValue);
privilegeElement.appendChild(allowValueElement);
});
});
});
// write the container file to disk
XmlHelper.writeDocument(doc, this.modelFile);
}
}

View File

@ -15,12 +15,7 @@
*/ */
package li.strolch.privilege.xml; package li.strolch.privilege.xml;
import java.text.MessageFormat; import li.strolch.privilege.model.Privilege;
import java.util.*;
import li.strolch.privilege.helper.XmlConstants;
import li.strolch.privilege.model.IPrivilege;
import li.strolch.privilege.model.internal.PrivilegeImpl;
import li.strolch.privilege.model.internal.Role; import li.strolch.privilege.model.internal.Role;
import li.strolch.utils.helper.StringHelper; import li.strolch.utils.helper.StringHelper;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -29,6 +24,11 @@ import org.xml.sax.Attributes;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler; import org.xml.sax.helpers.DefaultHandler;
import java.text.MessageFormat;
import java.util.*;
import static li.strolch.privilege.helper.XmlConstants.*;
/** /**
* @author Robert von Burg <eitch@eitchnet.ch> * @author Robert von Burg <eitch@eitchnet.ch>
*/ */
@ -51,9 +51,13 @@ public class PrivilegeRolesSaxReader extends DefaultHandler {
@Override @Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
if (qName.equals(XmlConstants.XML_ROLE)) { if (qName.equals(ROLE)) {
if (this.buildersStack.stream().anyMatch(e -> e.getClass().equals(RoleParser.class)))
throw new IllegalArgumentException("Previous Role not closed!");
this.buildersStack.push(new RoleParser()); this.buildersStack.push(new RoleParser());
} else if (qName.equals(XmlConstants.XML_PROPERTIES)) { } else if (qName.equals(PROPERTIES)) {
if (this.buildersStack.stream().anyMatch(e -> e.getClass().equals(PropertyParser.class)))
throw new IllegalArgumentException("Previous Properties not closed!");
this.buildersStack.push(new PropertyParser()); this.buildersStack.push(new PropertyParser());
} }
@ -74,9 +78,9 @@ public class PrivilegeRolesSaxReader extends DefaultHandler {
this.buildersStack.peek().endElement(uri, localName, qName); this.buildersStack.peek().endElement(uri, localName, qName);
ElementParser elementParser = null; ElementParser elementParser = null;
if (qName.equals(XmlConstants.XML_ROLE)) { if (qName.equals(ROLE)) {
elementParser = this.buildersStack.pop(); elementParser = this.buildersStack.pop();
} else if (qName.equals(XmlConstants.XML_PROPERTIES)) { } else if (qName.equals(PROPERTIES)) {
elementParser = this.buildersStack.pop(); elementParser = this.buildersStack.pop();
} }
@ -109,7 +113,7 @@ public class PrivilegeRolesSaxReader extends DefaultHandler {
private Set<String> denyList; private Set<String> denyList;
private Set<String> allowList; private Set<String> allowList;
private Map<String, IPrivilege> privileges; private Map<String, Privilege> privileges;
public RoleParser() { public RoleParser() {
init(); init();
@ -134,20 +138,15 @@ public class PrivilegeRolesSaxReader extends DefaultHandler {
this.text = new StringBuilder(); this.text = new StringBuilder();
switch (qName) { switch (qName) {
case XmlConstants.XML_ROLE: case ROLE -> this.roleName = attributes.getValue(ATTR_NAME).trim();
this.roleName = attributes.getValue(XmlConstants.XML_ATTR_NAME).trim(); case PRIVILEGE -> {
break; this.privilegeName = attributes.getValue(ATTR_NAME).trim();
case XmlConstants.XML_PRIVILEGE: this.privilegePolicy = attributes.getValue(ATTR_POLICY).trim();
this.privilegeName = attributes.getValue(XmlConstants.XML_ATTR_NAME).trim(); }
this.privilegePolicy = attributes.getValue(XmlConstants.XML_ATTR_POLICY).trim(); case ALLOW, DENY, ALL_ALLOWED -> {
break; }
case XmlConstants.XML_ALLOW:
case XmlConstants.XML_DENY:
case XmlConstants.XML_ALL_ALLOWED:
// no-op // no-op
break; default -> throw new IllegalArgumentException("Unhandled tag " + qName);
default:
throw new IllegalArgumentException("Unhandled tag " + qName);
} }
} }
@ -159,31 +158,33 @@ public class PrivilegeRolesSaxReader extends DefaultHandler {
@Override @Override
public void endElement(String uri, String localName, String qName) { public void endElement(String uri, String localName, String qName) {
switch (qName) { switch (qName) {
case XmlConstants.XML_ALL_ALLOWED -> case ALL_ALLOWED -> this.allAllowed = StringHelper.parseBoolean(getText());
this.allAllowed = StringHelper.parseBoolean(this.text.toString().trim()); case ALLOW -> this.allowList.add(getText());
case XmlConstants.XML_ALLOW -> this.allowList.add(this.text.toString().trim()); case DENY -> this.denyList.add(getText());
case XmlConstants.XML_DENY -> this.denyList.add(this.text.toString().trim()); case PRIVILEGE -> {
case XmlConstants.XML_PRIVILEGE -> { Privilege privilege = new Privilege(this.privilegeName, this.privilegePolicy, this.allAllowed,
IPrivilege privilege = new PrivilegeImpl(this.privilegeName, this.privilegePolicy, this.allAllowed, this.denyList, this.allowList);
this.denyList, this.allowList); this.privileges.put(this.privilegeName, privilege);
this.privileges.put(this.privilegeName, privilege); this.privilegeName = null;
this.privilegeName = null; this.privilegePolicy = null;
this.privilegePolicy = null; this.allAllowed = false;
this.allAllowed = false; this.denyList = new HashSet<>();
this.denyList = new HashSet<>(); this.allowList = new HashSet<>();
this.allowList = new HashSet<>(); }
} case ROLE -> {
case XmlConstants.XML_ROLE -> { Role role = new Role(this.roleName, this.privileges);
Role role = new Role(this.roleName, this.privileges); roles.put(role.getName(), role);
roles.put(role.getName(), role); logger.info(MessageFormat.format("New Role: {0}", role));
logger.info(MessageFormat.format("New Role: {0}", role)); init();
init(); }
} default -> throw new IllegalStateException("Unexpected value: " + qName);
default -> throw new IllegalStateException("Unexpected value: " + qName);
} }
} }
private String getText() {
return this.text.toString().trim();
}
} }
static class PropertyParser extends ElementParserAdapter { static class PropertyParser extends ElementParserAdapter {
@ -194,11 +195,11 @@ public class PrivilegeRolesSaxReader extends DefaultHandler {
@Override @Override
public void startElement(String uri, String localName, String qName, Attributes attributes) { public void startElement(String uri, String localName, String qName, Attributes attributes) {
if (qName.equals(XmlConstants.XML_PROPERTY)) { if (qName.equals(PROPERTY)) {
String key = attributes.getValue(XmlConstants.XML_ATTR_NAME).trim(); String key = attributes.getValue(ATTR_NAME).trim();
String value = attributes.getValue(XmlConstants.XML_ATTR_VALUE).trim(); String value = attributes.getValue(ATTR_VALUE).trim();
this.parameterMap.put(key, value); this.parameterMap.put(key, value);
} else if (!qName.equals(XmlConstants.XML_PROPERTIES)) { } else if (!qName.equals(PROPERTIES)) {
throw new IllegalArgumentException("Unhandled tag " + qName); throw new IllegalArgumentException("Unhandled tag " + qName);
} }
} }

View File

@ -0,0 +1,86 @@
/*
* 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.xml;
import javanet.staxutils.IndentingXMLStreamWriter;
import li.strolch.privilege.model.Privilege;
import li.strolch.privilege.model.internal.Role;
import javax.xml.stream.XMLStreamException;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import static java.util.Comparator.comparing;
import static li.strolch.privilege.helper.XmlConstants.*;
import static li.strolch.privilege.helper.XmlHelper.*;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*/
public class PrivilegeRolesSaxWriter {
private final List<Role> roles;
private final File modelFile;
public PrivilegeRolesSaxWriter(List<Role> roles, File modelFile) {
this.roles = roles;
this.modelFile = modelFile;
}
public void write() throws IOException, XMLStreamException {
try (Writer ioWriter = new OutputStreamWriter(new FileOutputStream(this.modelFile), StandardCharsets.UTF_8)) {
IndentingXMLStreamWriter xmlWriter = openXmlStreamWriterDocument(ioWriter);
xmlWriter.writeStartElement(ROLES);
List<Role> roles = new ArrayList<>(this.roles);
roles.sort(comparing(r -> r.getName().toLowerCase(Locale.ROOT)));
for (Role role : roles) {
// start the role element
xmlWriter.writeStartElement(ROLE);
xmlWriter.writeAttribute(ATTR_NAME, role.getName());
List<String> privilegeNames = new ArrayList<>(role.getPrivilegeNames());
privilegeNames.sort(null);
for (String privilegeName : privilegeNames) {
Privilege privilege = role.getPrivilege(privilegeName);
xmlWriter.writeStartElement(PRIVILEGE);
xmlWriter.writeAttribute(ATTR_NAME, privilege.getName());
xmlWriter.writeAttribute(ATTR_POLICY, privilege.getPolicy());
if (privilege.isAllAllowed())
writeStringElement(xmlWriter, ALL_ALLOWED, "true");
writeStringList(xmlWriter, DENY, privilege.getDenyList());
writeStringList(xmlWriter, ALLOW, privilege.getAllowList());
xmlWriter.writeEndElement();
}
xmlWriter.writeEndElement();
}
// and now end
xmlWriter.writeEndDocument();
xmlWriter.flush();
}
}
}

View File

@ -1,165 +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.xml;
import li.strolch.privilege.helper.XmlConstants;
import li.strolch.privilege.model.internal.PasswordCrypt;
import li.strolch.privilege.model.internal.User;
import li.strolch.privilege.model.internal.UserHistory;
import li.strolch.utils.helper.StringHelper;
import li.strolch.utils.helper.XmlHelper;
import li.strolch.utils.iso8601.ISO8601;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import java.io.File;
import java.util.List;
import java.util.Map;
import static java.util.Comparator.comparing;
import static li.strolch.privilege.helper.XmlConstants.*;
import static li.strolch.privilege.model.internal.PasswordCrypt.buildPasswordString;
import static li.strolch.utils.helper.StringHelper.*;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*/
public class PrivilegeUsersDomWriter {
private final List<User> users;
private final File modelFile;
public PrivilegeUsersDomWriter(List<User> users, File modelFile) {
this.users = users;
this.modelFile = modelFile;
this.users.sort(comparing(User::getUsername));
}
public void write() {
// create document root
Document doc = XmlHelper.createDocument();
Element rootElement = doc.createElement(XML_USERS);
doc.appendChild(rootElement);
this.users.forEach(user -> {
// create the user element
Element userElement = doc.createElement(XML_USER);
rootElement.appendChild(userElement);
userElement.setAttribute(XML_ATTR_USER_ID, user.getUserId());
userElement.setAttribute(XML_ATTR_USERNAME, user.getUsername());
writePassword(user, userElement);
// add first name element
if (isNotEmpty(user.getFirstname())) {
Element firstnameElement = doc.createElement(XML_FIRSTNAME);
firstnameElement.setTextContent(user.getFirstname());
userElement.appendChild(firstnameElement);
}
// add last name element
if (isNotEmpty(user.getLastname())) {
Element lastnameElement = doc.createElement(XML_LASTNAME);
lastnameElement.setTextContent(user.getLastname());
userElement.appendChild(lastnameElement);
}
// add state element
Element stateElement = doc.createElement(XML_STATE);
stateElement.setTextContent(user.getUserState().toString());
userElement.appendChild(stateElement);
// add locale element
Element localeElement = doc.createElement(XML_LOCALE);
localeElement.setTextContent(user.getLocale().toLanguageTag());
userElement.appendChild(localeElement);
// add password change requested element
if (user.isPasswordChangeRequested()) {
Element passwordChangeRequestedElement = doc.createElement(XML_PASSWORD_CHANGE_REQUESTED);
passwordChangeRequestedElement.setTextContent(Boolean.toString(true));
userElement.appendChild(passwordChangeRequestedElement);
}
// add all the role elements
Element rolesElement = doc.createElement(XML_ROLES);
userElement.appendChild(rolesElement);
user.getRoles().stream().sorted().forEach(roleName -> {
Element roleElement = doc.createElement(XML_ROLE);
roleElement.setTextContent(roleName);
rolesElement.appendChild(roleElement);
});
// add the parameters
if (!user.getProperties().isEmpty()) {
Element parametersElement = doc.createElement(XML_PROPERTIES);
userElement.appendChild(parametersElement);
user.getProperties().entrySet().stream().sorted(Map.Entry.comparingByKey()).forEach(entry -> {
Element paramElement = doc.createElement(XML_PROPERTY);
paramElement.setAttribute(XML_ATTR_NAME, entry.getKey());
paramElement.setAttribute(XML_ATTR_VALUE, entry.getValue());
parametersElement.appendChild(paramElement);
});
}
if (!user.isHistoryEmpty()) {
UserHistory history = user.getHistory();
Element historyElement = doc.createElement(XML_HISTORY);
userElement.appendChild(historyElement);
if (!history.isFirstLoginEmpty()) {
Element element = doc.createElement(XML_FIRST_LOGIN);
element.setTextContent(ISO8601.toString(history.getFirstLogin()));
historyElement.appendChild(element);
}
if (!history.isLastLoginEmpty()) {
Element element = doc.createElement(XML_LAST_LOGIN);
element.setTextContent(ISO8601.toString(history.getLastLogin()));
historyElement.appendChild(element);
}
if (!history.isLastPasswordChangeEmpty()) {
Element element = doc.createElement(XML_LAST_PASSWORD_CHANGE);
element.setTextContent(ISO8601.toString(history.getLastPasswordChange()));
historyElement.appendChild(element);
}
}
});
// write the container file to disk
XmlHelper.writeDocument(doc, this.modelFile);
}
private void writePassword(User user, Element userElement) {
PasswordCrypt passwordCrypt = user.getPasswordCrypt();
if (passwordCrypt == null)
return;
String passwordString = passwordCrypt.buildPasswordString();
if (passwordString != null) {
userElement.setAttribute(XML_ATTR_PASSWORD, passwordString);
} else {
if (passwordCrypt.getPassword() != null)
userElement.setAttribute(XML_ATTR_PASSWORD, toHexString(passwordCrypt.getPassword()));
if (passwordCrypt.getSalt() != null)
userElement.setAttribute(XML_ATTR_SALT, toHexString(passwordCrypt.getSalt()));
}
}
}

View File

@ -15,16 +15,11 @@
*/ */
package li.strolch.privilege.xml; package li.strolch.privilege.xml;
import static li.strolch.privilege.helper.XmlConstants.*;
import java.text.MessageFormat;
import java.util.*;
import li.strolch.privilege.model.UserState; import li.strolch.privilege.model.UserState;
import li.strolch.privilege.model.internal.Group;
import li.strolch.privilege.model.internal.PasswordCrypt; import li.strolch.privilege.model.internal.PasswordCrypt;
import li.strolch.privilege.model.internal.User; import li.strolch.privilege.model.internal.User;
import li.strolch.privilege.model.internal.UserHistory; import li.strolch.privilege.model.internal.UserHistory;
import li.strolch.utils.helper.StringHelper;
import li.strolch.utils.iso8601.ISO8601; import li.strolch.utils.iso8601.ISO8601;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -32,6 +27,11 @@ import org.xml.sax.Attributes;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler; import org.xml.sax.helpers.DefaultHandler;
import java.text.MessageFormat;
import java.util.*;
import static li.strolch.privilege.helper.XmlConstants.*;
/** /**
* @author Robert von Burg <eitch@eitchnet.ch> * @author Robert von Burg <eitch@eitchnet.ch>
*/ */
@ -58,9 +58,13 @@ public class PrivilegeUsersSaxReader extends DefaultHandler {
@Override @Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
if (qName.equals(XML_USER)) { if (qName.equals(USER)) {
if (this.buildersStack.stream().anyMatch(e -> e.getClass().equals(UserParser.class)))
throw new IllegalArgumentException("Previous User not closed!");
this.buildersStack.push(new UserParser()); this.buildersStack.push(new UserParser());
} else if (qName.equals(XML_PROPERTIES)) { } else if (qName.equals(PROPERTIES)) {
if (this.buildersStack.stream().anyMatch(e -> e.getClass().equals(PropertyParser.class)))
throw new IllegalArgumentException("Previous Properties not closed!");
this.buildersStack.push(new PropertyParser()); this.buildersStack.push(new PropertyParser());
} }
@ -81,9 +85,9 @@ public class PrivilegeUsersSaxReader extends DefaultHandler {
this.buildersStack.peek().endElement(uri, localName, qName); this.buildersStack.peek().endElement(uri, localName, qName);
ElementParser elementParser = null; ElementParser elementParser = null;
if (qName.equals(XML_USER)) { if (qName.equals(USER)) {
elementParser = this.buildersStack.pop(); elementParser = this.buildersStack.pop();
} else if (qName.equals(XML_PROPERTIES)) { } else if (qName.equals(PROPERTIES)) {
elementParser = this.buildersStack.pop(); elementParser = this.buildersStack.pop();
} }
@ -122,12 +126,14 @@ public class PrivilegeUsersSaxReader extends DefaultHandler {
String lastname; String lastname;
UserState userState; UserState userState;
Locale locale; Locale locale;
final Set<String> groups;
final Set<String> userRoles; final Set<String> userRoles;
Map<String, String> parameters; Map<String, String> parameters;
UserHistory history; UserHistory history;
boolean passwordChangeRequested; boolean passwordChangeRequested;
public UserParser() { public UserParser() {
this.groups = new HashSet<>();
this.userRoles = new HashSet<>(); this.userRoles = new HashSet<>();
} }
@ -136,15 +142,15 @@ public class PrivilegeUsersSaxReader extends DefaultHandler {
this.text = new StringBuilder(); this.text = new StringBuilder();
if (qName.equals(XML_USER)) { if (qName.equals(USER)) {
this.userId = attributes.getValue(XML_ATTR_USER_ID).trim(); this.userId = attributes.getValue(ATTR_USER_ID).trim();
this.username = attributes.getValue(XML_ATTR_USERNAME).trim(); this.username = attributes.getValue(ATTR_USERNAME).trim();
String password = attributes.getValue(XML_ATTR_PASSWORD); String password = attributes.getValue(ATTR_PASSWORD);
String salt = attributes.getValue(XML_ATTR_SALT); String salt = attributes.getValue(ATTR_SALT);
this.passwordCrypt = PasswordCrypt.parse(password, salt); this.passwordCrypt = PasswordCrypt.parse(password, salt);
} else if (qName.equals(XML_HISTORY)) { } else if (qName.equals(HISTORY)) {
this.history = new UserHistory(); this.history = UserHistory.EMPTY;
} }
} }
@ -157,40 +163,45 @@ public class PrivilegeUsersSaxReader extends DefaultHandler {
public void endElement(String uri, String localName, String qName) { public void endElement(String uri, String localName, String qName) {
switch (qName) { switch (qName) {
case XML_FIRSTNAME -> this.firstName = this.text.toString().trim(); case FIRSTNAME -> this.firstName = getText();
case XML_LASTNAME -> this.lastname = this.text.toString().trim(); case LASTNAME -> this.lastname = getText();
case XML_STATE -> this.userState = UserState.valueOf(this.text.toString().trim()); case STATE -> this.userState = UserState.valueOf(getText());
case XML_LOCALE -> this.locale = Locale.forLanguageTag(this.text.toString().trim()); case LOCALE -> this.locale = Locale.forLanguageTag(getText());
case XML_PASSWORD_CHANGE_REQUESTED -> case PASSWORD_CHANGE_REQUESTED -> this.passwordChangeRequested = Boolean.parseBoolean(getText());
this.passwordChangeRequested = Boolean.parseBoolean(this.text.toString().trim()); case FIRST_LOGIN -> this.history = this.history.withFirstLogin(ISO8601.parseToZdt(getText()));
case XML_FIRST_LOGIN -> this.history.setFirstLogin(ISO8601.parseToZdt(this.text.toString().trim())); case LAST_LOGIN -> this.history = this.history.withLastLogin(ISO8601.parseToZdt(getText()));
case XML_LAST_LOGIN -> this.history.setLastLogin(ISO8601.parseToZdt(this.text.toString().trim())); case LAST_PASSWORD_CHANGE ->
case XML_LAST_PASSWORD_CHANGE -> this.history = this.history.withLastPasswordChange(ISO8601.parseToZdt(getText()));
this.history.setLastPasswordChange(ISO8601.parseToZdt(this.text.toString().trim())); case GROUP -> this.groups.add(getText());
case XML_ROLE -> this.userRoles.add(this.text.toString().trim()); case ROLE -> this.userRoles.add(getText());
case XML_USER -> { case USER -> {
if (this.history == null) if (this.history == null)
this.history = new UserHistory(); this.history = UserHistory.EMPTY;
User user = new User(this.userId, this.username, this.passwordCrypt, this.firstName, this.lastname, User user = new User(this.userId, this.username, this.passwordCrypt, this.firstName, this.lastname,
this.userState, this.userRoles, this.locale, this.parameters, this.passwordChangeRequested, this.userState, this.groups, this.userRoles, this.locale, this.parameters,
this.history); this.passwordChangeRequested, this.history);
logger.info(MessageFormat.format("New User: {0}", user)); logger.info(MessageFormat.format("New User: {0}", user));
String username = caseInsensitiveUsername ? user.getUsername().toLowerCase() : user.getUsername(); String username = caseInsensitiveUsername ? user.getUsername().toLowerCase() : user.getUsername();
users.put(username, user); users.put(username, user);
} }
default -> { default -> {
if (!(qName.equals(XML_ROLES) // if (!(qName.equals(ROLES) //
|| qName.equals(XML_PARAMETER) // || qName.equals(GROUPS) //
|| qName.equals(XML_HISTORY) // || qName.equals(PARAMETER) //
|| qName.equals(XML_PARAMETERS))) { || qName.equals(HISTORY) //
|| qName.equals(PARAMETERS))) {
throw new IllegalArgumentException("Unhandled tag " + qName); throw new IllegalArgumentException("Unhandled tag " + qName);
} }
} }
} }
} }
private String getText() {
return this.text.toString().trim();
}
@Override @Override
public void notifyChild(ElementParser child) { public void notifyChild(ElementParser child) {
if (child instanceof PropertyParser) { if (child instanceof PropertyParser) {
@ -198,29 +209,4 @@ public class PrivilegeUsersSaxReader extends DefaultHandler {
} }
} }
} }
static class PropertyParser extends ElementParserAdapter {
// <Property name="organizationalUnit" value="Development" />
public final Map<String, String> parameterMap = new HashMap<>();
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) {
if (qName.equals(XML_PROPERTY)) {
String key = attributes.getValue(XML_ATTR_NAME).trim();
String value = attributes.getValue(XML_ATTR_VALUE).trim();
this.parameterMap.put(key, value);
} else {
if (!qName.equals(XML_PROPERTIES)) {
throw new IllegalArgumentException("Unhandled tag " + qName);
}
}
}
public Map<String, String> getParameterMap() {
return this.parameterMap;
}
}
} }

View File

@ -0,0 +1,148 @@
/*
* 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.xml;
import javanet.staxutils.IndentingXMLStreamWriter;
import li.strolch.privilege.model.internal.PasswordCrypt;
import li.strolch.privilege.model.internal.User;
import li.strolch.privilege.model.internal.UserHistory;
import li.strolch.utils.iso8601.ISO8601;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import static java.util.Comparator.comparing;
import static li.strolch.privilege.helper.XmlConstants.*;
import static li.strolch.privilege.helper.XmlHelper.*;
import static li.strolch.utils.helper.StringHelper.isNotEmpty;
import static li.strolch.utils.helper.StringHelper.toHexString;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*/
public class PrivilegeUsersSaxWriter {
private final List<User> users;
private final File modelFile;
public PrivilegeUsersSaxWriter(List<User> users, File modelFile) {
this.users = users;
this.modelFile = modelFile;
}
public void write() throws IOException, XMLStreamException {
try (Writer ioWriter = new OutputStreamWriter(new FileOutputStream(this.modelFile), StandardCharsets.UTF_8)) {
IndentingXMLStreamWriter xmlWriter = openXmlStreamWriterDocument(ioWriter);
xmlWriter.writeStartElement(USERS);
List<User> users = new ArrayList<>(this.users);
users.sort(comparing(u -> u.getUsername().toLowerCase(Locale.ROOT)));
for (User user : this.users) {
// start the user element
xmlWriter.writeStartElement(USER);
xmlWriter.writeAttribute(ATTR_USER_ID, user.getUserId());
xmlWriter.writeAttribute(ATTR_USERNAME, user.getUsername());
writePassword(user, xmlWriter);
// add first name element
if (isNotEmpty(user.getFirstname()))
writeStringElement(xmlWriter, FIRSTNAME, user.getFirstname());
// add last name element
if (isNotEmpty(user.getLastname()))
writeStringElement(xmlWriter, LASTNAME, user.getLastname());
// add state element
writeStringElement(xmlWriter, STATE, user.getUserState().toString());
// add locale element
writeStringElement(xmlWriter, LOCALE, user.getLocale().toLanguageTag());
// add password change requested element
if (user.isPasswordChangeRequested())
writeStringElement(xmlWriter, PASSWORD_CHANGE_REQUESTED, "true");
// add all the group elements
if (!user.getGroups().isEmpty()) {
xmlWriter.writeStartElement(GROUPS);
writeStringList(xmlWriter, GROUP, user.getGroups());
xmlWriter.writeEndElement();
}
// add all the role elements
if (!user.getRoles().isEmpty()) {
xmlWriter.writeStartElement(ROLES);
writeStringList(xmlWriter, ROLE, user.getRoles());
xmlWriter.writeEndElement();
}
// add the parameters
Map<String, String> properties = user.getProperties();
if (!properties.isEmpty())
writeStringMapElement(xmlWriter, properties, PROPERTIES, PROPERTY);
if (!user.isHistoryEmpty()) {
UserHistory history = user.getHistory();
xmlWriter.writeStartElement(HISTORY);
if (!history.isFirstLoginEmpty())
writeStringElement(xmlWriter, FIRST_LOGIN, ISO8601.toString(history.getFirstLogin()));
if (!history.isLastLoginEmpty())
writeStringElement(xmlWriter, LAST_LOGIN, ISO8601.toString(history.getLastLogin()));
if (!history.isLastPasswordChangeEmpty())
writeStringElement(xmlWriter, LAST_PASSWORD_CHANGE,
ISO8601.toString(history.getLastPasswordChange()));
xmlWriter.writeEndElement();
}
xmlWriter.writeEndElement();
}
// and now end
xmlWriter.writeEndDocument();
xmlWriter.flush();
}
}
private void writePassword(User user, XMLStreamWriter xmlStreamWriter) throws XMLStreamException {
PasswordCrypt passwordCrypt = user.getPasswordCrypt();
if (passwordCrypt == null)
return;
String passwordString = passwordCrypt.buildPasswordString();
if (passwordString != null) {
xmlStreamWriter.writeAttribute(ATTR_PASSWORD, passwordString);
} else {
if (passwordCrypt.password() != null)
xmlStreamWriter.writeAttribute(ATTR_PASSWORD, toHexString(passwordCrypt.password()));
if (passwordCrypt.salt() != null)
xmlStreamWriter.writeAttribute(ATTR_SALT, toHexString(passwordCrypt.salt()));
}
}
}

View File

@ -0,0 +1,33 @@
package li.strolch.privilege.xml;
import org.xml.sax.Attributes;
import java.util.HashMap;
import java.util.Map;
import static li.strolch.privilege.helper.XmlConstants.*;
class PropertyParser extends ElementParserAdapter {
// <Property name="organizationalUnit" value="Development" />
public final Map<String, String> parameterMap = new HashMap<>();
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) {
if (qName.equals(PROPERTY)) {
String key = attributes.getValue(ATTR_NAME).trim();
String value = attributes.getValue(ATTR_VALUE).trim();
this.parameterMap.put(key, value);
} else {
if (!qName.equals(PROPERTIES)) {
throw new IllegalArgumentException("Unhandled tag " + qName);
}
}
}
public Map<String, String> getParameterMap() {
return this.parameterMap;
}
}

View File

@ -11,6 +11,7 @@ Privilege.privilegeNameEmpty=The PrivilegeName for the Restrictable is null or e
Privilege.privilegeNull=Privilege may not be null\! Privilege.privilegeNull=Privilege may not be null\!
Privilege.restrictableNull=Restrictable may not be null\! Privilege.restrictableNull=Restrictable may not be null\!
Privilege.noprivilege=No Privilege exists with name {0} Privilege.noprivilege=No Privilege exists with name {0}
Privilege.noprivilege.group=User {0} does not belong to group {1}
Privilege.noprivilege.role=User {0} does not have the role {1} Privilege.noprivilege.role=User {0} does not have the role {1}
Privilege.noprivilege.user=User {0} does not have the privilege {1} Privilege.noprivilege.user=User {0} does not have the privilege {1}
Privilege.roleAccessPrivilege.unknownPrivilege=Unhandled privilege {0} for policy {1} Privilege.roleAccessPrivilege.unknownPrivilege=Unhandled privilege {0} for policy {1}

View File

@ -1,12 +1,5 @@
package li.strolch.privilege.test; package li.strolch.privilege.test;
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.base.PrivilegeException;
import li.strolch.privilege.handler.PrivilegeHandler; import li.strolch.privilege.handler.PrivilegeHandler;
import li.strolch.privilege.helper.PrivilegeInitializer; import li.strolch.privilege.helper.PrivilegeInitializer;
@ -18,6 +11,13 @@ import org.junit.BeforeClass;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.io.File;
import java.nio.file.Files;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import static org.junit.Assert.assertNotNull;
public class AbstractPrivilegeTest { public class AbstractPrivilegeTest {
protected static final Logger logger = LoggerFactory.getLogger(AbstractPrivilegeTest.class); protected static final Logger logger = LoggerFactory.getLogger(AbstractPrivilegeTest.class);
@ -57,13 +57,14 @@ public class AbstractPrivilegeTest {
} }
} }
protected static void prepareConfigs(String dst, String configFilename, String usersFilename, protected static void prepareConfigs(String dst, String configFilename, String usersFilename, String groupsFilename,
String rolesFilename) { String rolesFilename) {
try { try {
File configPath = new File("src/test/resources/config"); File configPath = new File("src/test/resources/config");
File privilegeConfigFile = new File(configPath, configFilename); File privilegeConfigFile = new File(configPath, configFilename);
File privilegeUsersFile = new File(configPath, usersFilename); File privilegeUsersFile = new File(configPath, usersFilename);
File privilegeGroupsFile = new File(configPath, groupsFilename);
File privilegeRolesFile = new File(configPath, rolesFilename); File privilegeRolesFile = new File(configPath, rolesFilename);
File targetPath = new File("target/" + dst); File targetPath = new File("target/" + dst);
@ -72,6 +73,7 @@ public class AbstractPrivilegeTest {
File dstConfig = new File(targetPath, configFilename); File dstConfig = new File(targetPath, configFilename);
File dstUsers = new File(targetPath, usersFilename); File dstUsers = new File(targetPath, usersFilename);
File dstGroups = new File(targetPath, groupsFilename);
File dstRoles = new File(targetPath, rolesFilename); File dstRoles = new File(targetPath, rolesFilename);
// write config // write config
@ -81,6 +83,7 @@ public class AbstractPrivilegeTest {
// copy model // copy model
Files.copy(privilegeUsersFile.toPath(), dstUsers.toPath()); Files.copy(privilegeUsersFile.toPath(), dstUsers.toPath());
Files.copy(privilegeGroupsFile.toPath(), dstGroups.toPath());
Files.copy(privilegeRolesFile.toPath(), dstRoles.toPath()); Files.copy(privilegeRolesFile.toPath(), dstRoles.toPath());
} catch (Exception e) { } catch (Exception e) {

View File

@ -20,9 +20,9 @@ public class CryptTest {
@BeforeClass @BeforeClass
public static void beforeClass() { public static void beforeClass() {
Map<String, String> parameterMap = new HashMap<>(); Map<String, String> parameterMap = new HashMap<>();
parameterMap.put(XML_PARAM_HASH_ALGORITHM, DEFAULT_ALGORITHM); parameterMap.put(PARAM_HASH_ALGORITHM, DEFAULT_ALGORITHM);
parameterMap.put(XML_PARAM_HASH_ITERATIONS, "" + DEFAULT_SMALL_ITERATIONS); parameterMap.put(PARAM_HASH_ITERATIONS, "" + DEFAULT_SMALL_ITERATIONS);
parameterMap.put(XML_PARAM_HASH_KEY_LENGTH, "" + DEFAULT_KEY_LENGTH); parameterMap.put(PARAM_HASH_KEY_LENGTH, "" + DEFAULT_KEY_LENGTH);
encryptionHandler = new DefaultEncryptionHandler(); encryptionHandler = new DefaultEncryptionHandler();
encryptionHandler.initialize(parameterMap); encryptionHandler.initialize(parameterMap);
@ -41,7 +41,7 @@ public class CryptTest {
PasswordCrypt passwordCrypt = encryptionHandler.hashPassword(password, salt, "PBKDF2WithHmacSHA512", 100000, PasswordCrypt passwordCrypt = encryptionHandler.hashPassword(password, salt, "PBKDF2WithHmacSHA512", 100000,
256); 256);
assertArrayEquals(passwordCrypt.getPassword(), parsedCryptHash.getPassword()); assertArrayEquals(passwordCrypt.password(), parsedCryptHash.password());
} }
@Test @Test
@ -53,9 +53,9 @@ public class CryptTest {
assertNotNull(parsedCryptHash); assertNotNull(parsedCryptHash);
char[] password = "admin".toCharArray(); char[] password = "admin".toCharArray();
PasswordCrypt passwordCrypt = encryptionHandler.hashPassword(password, parsedCryptHash.getSalt(), PasswordCrypt passwordCrypt = encryptionHandler.hashPassword(password, parsedCryptHash.salt(),
"PBKDF2WithHmacSHA512", 100000, 256); "PBKDF2WithHmacSHA512", 100000, 256);
assertArrayEquals(passwordCrypt.getPassword(), parsedCryptHash.getPassword()); assertArrayEquals(passwordCrypt.password(), parsedCryptHash.password());
} }
} }

View File

@ -16,7 +16,7 @@ public class PersistSessionsTest extends AbstractPrivilegeTest {
public static void init() { public static void init() {
removeConfigs(PersistSessionsTest.class.getSimpleName()); removeConfigs(PersistSessionsTest.class.getSimpleName());
prepareConfigs(PersistSessionsTest.class.getSimpleName(), "PrivilegeConfig.xml", "PrivilegeUsers.xml", prepareConfigs(PersistSessionsTest.class.getSimpleName(), "PrivilegeConfig.xml", "PrivilegeUsers.xml",
"PrivilegeRoles.xml"); "PrivilegeGroups.xml", "PrivilegeRoles.xml");
} }
@AfterClass @AfterClass

View File

@ -15,14 +15,16 @@
*/ */
package li.strolch.privilege.test; package li.strolch.privilege.test;
import static org.junit.Assert.*; import li.strolch.privilege.model.Privilege;
import li.strolch.privilege.model.IPrivilege;
import org.junit.AfterClass; import org.junit.AfterClass;
import org.junit.Before; import org.junit.Before;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import java.util.Set;
import static org.junit.Assert.*;
/** /**
* @author Robert von Burg <eitch@eitchnet.ch> * @author Robert von Burg <eitch@eitchnet.ch>
*/ */
@ -32,7 +34,7 @@ public class PrivilegeConflictMergeTest extends AbstractPrivilegeTest {
public static void init() { public static void init() {
removeConfigs(PrivilegeConflictMergeTest.class.getSimpleName()); removeConfigs(PrivilegeConflictMergeTest.class.getSimpleName());
prepareConfigs(PrivilegeConflictMergeTest.class.getSimpleName(), "PrivilegeConfigMerge.xml", prepareConfigs(PrivilegeConflictMergeTest.class.getSimpleName(), "PrivilegeConfigMerge.xml",
"PrivilegeUsersMerge.xml", "PrivilegeRolesMerge.xml"); "PrivilegeUsersMerge.xml", "PrivilegeGroupsMerge.xml", "PrivilegeRolesMerge.xml");
} }
@AfterClass @AfterClass
@ -49,7 +51,16 @@ public class PrivilegeConflictMergeTest extends AbstractPrivilegeTest {
public void shouldMergePrivileges1() { public void shouldMergePrivileges1() {
try { try {
login("userA", "admin".toCharArray()); login("userA", "admin".toCharArray());
IPrivilege privilege = this.ctx.getPrivilege("Foo"); assertEquals(Set.of(), this.ctx.getUserRep().getGroups());
assertFalse(this.ctx.hasGroup("GroupA1"));
assertFalse(this.ctx.hasGroup("GroupA2"));
assertEquals(Set.of("RoleA1", "RoleA2"), this.ctx.getUserRep().getRoles());
assertTrue(this.ctx.hasRole("RoleA1"));
assertTrue(this.ctx.hasRole("RoleA2"));
assertFalse(this.ctx.hasRole("RoleB2"));
assertNull(this.ctx.getUserRep().getLocation());
assertEquals(Set.of(), this.ctx.getUserRep().getPropertyKeySet());
Privilege privilege = this.ctx.getPrivilege("Foo");
assertTrue(privilege.isAllAllowed()); assertTrue(privilege.isAllAllowed());
assertTrue(privilege.getAllowList().isEmpty()); assertTrue(privilege.getAllowList().isEmpty());
assertTrue(privilege.getDenyList().isEmpty()); assertTrue(privilege.getDenyList().isEmpty());
@ -63,7 +74,16 @@ public class PrivilegeConflictMergeTest extends AbstractPrivilegeTest {
public void shouldMergePrivileges2() { public void shouldMergePrivileges2() {
try { try {
login("userB", "admin".toCharArray()); login("userB", "admin".toCharArray());
IPrivilege privilege = this.ctx.getPrivilege("Bar"); assertEquals(Set.of(), this.ctx.getUserRep().getGroups());
assertFalse(this.ctx.hasGroup("GroupB1"));
assertFalse(this.ctx.hasGroup("GroupB2"));
assertEquals(Set.of("RoleB1", "RoleB2"), this.ctx.getUserRep().getRoles());
assertTrue(this.ctx.hasRole("RoleB1"));
assertTrue(this.ctx.hasRole("RoleB2"));
assertFalse(this.ctx.hasRole("RoleA2"));
assertNull(this.ctx.getUserRep().getLocation());
assertEquals(Set.of(), this.ctx.getUserRep().getPropertyKeySet());
Privilege privilege = this.ctx.getPrivilege("Bar");
assertFalse(privilege.isAllAllowed()); assertFalse(privilege.isAllAllowed());
assertEquals(2, privilege.getAllowList().size()); assertEquals(2, privilege.getAllowList().size());
assertEquals(2, privilege.getDenyList().size()); assertEquals(2, privilege.getDenyList().size());
@ -71,4 +91,53 @@ public class PrivilegeConflictMergeTest extends AbstractPrivilegeTest {
logout(); logout();
} }
} }
@Test
public void shouldMergePrivileges3() {
try {
login("userC", "admin".toCharArray());
assertEquals(Set.of("GroupA1", "GroupA2"), this.ctx.getUserRep().getGroups());
assertTrue(this.ctx.hasGroup("GroupA1"));
assertTrue(this.ctx.hasGroup("GroupA2"));
assertFalse(this.ctx.hasGroup("GroupB2"));
assertEquals(Set.of("RoleA1", "RoleA2"), this.ctx.getUserRep().getRoles());
assertTrue(this.ctx.hasRole("RoleA1"));
assertTrue(this.ctx.hasRole("RoleA2"));
assertFalse(this.ctx.hasRole("RoleB2"));
assertEquals("LocationA2", this.ctx.getUserRep().getLocation());
assertEquals(Set.of("location"), this.ctx.getUserRep().getPropertyKeySet());
Privilege privilege = this.ctx.getPrivilege("Foo");
assertTrue(privilege.isAllAllowed());
assertTrue(privilege.getAllowList().isEmpty());
assertTrue(privilege.getDenyList().isEmpty());
} finally {
logout();
}
}
@Test
public void shouldMergePrivileges4() {
try {
login("userD", "admin".toCharArray());
assertEquals(Set.of("GroupB1", "GroupB2"), this.ctx.getUserRep().getGroups());
assertTrue(this.ctx.hasGroup("GroupB1"));
assertTrue(this.ctx.hasGroup("GroupB2"));
assertFalse(this.ctx.hasGroup("GroupA2"));
assertEquals(Set.of("RoleB1", "RoleB2"), this.ctx.getUserRep().getRoles());
assertTrue(this.ctx.hasRole("RoleB1"));
assertTrue(this.ctx.hasRole("RoleB2"));
assertFalse(this.ctx.hasRole("RoleA2"));
assertEquals("LocationB2", this.ctx.getUserRep().getLocation());
assertEquals(Set.of("location"), this.ctx.getUserRep().getPropertyKeySet());
Privilege privilege = this.ctx.getPrivilege("Bar");
assertFalse(privilege.isAllAllowed());
assertEquals(2, privilege.getAllowList().size());
assertEquals(2, privilege.getDenyList().size());
assertEquals(Set.of("allow1", "allow2"), privilege.getAllowList());
assertEquals(Set.of("deny1", "deny2"), privilege.getDenyList());
} finally {
logout();
}
}
} }

View File

@ -15,12 +15,6 @@
*/ */
package li.strolch.privilege.test; package li.strolch.privilege.test;
import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.*;
import java.text.MessageFormat;
import java.util.*;
import li.strolch.privilege.base.AccessDeniedException; import li.strolch.privilege.base.AccessDeniedException;
import li.strolch.privilege.base.InvalidCredentialsException; import li.strolch.privilege.base.InvalidCredentialsException;
import li.strolch.privilege.base.PrivilegeException; import li.strolch.privilege.base.PrivilegeException;
@ -41,9 +35,15 @@ import org.junit.Test;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.text.MessageFormat;
import java.util.*;
import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.*;
/** /**
* JUnit for performing Privilege tests. This JUnit is by no means complete, but checks the bare minimum.br /> * JUnit for performing Privilege tests. This JUnit is by no means complete, but checks the bare minimum.br />
* * <p>
* TODO add more tests, especially with deny and allow lists * TODO add more tests, especially with deny and allow lists
* *
* @author Robert von Burg <eitch@eitchnet.ch> * @author Robert von Burg <eitch@eitchnet.ch>
@ -77,12 +77,12 @@ public class PrivilegeTest extends AbstractPrivilegeTest {
public static void init() { public static void init() {
removeConfigs(PrivilegeTest.class.getSimpleName()); removeConfigs(PrivilegeTest.class.getSimpleName());
prepareConfigs(PrivilegeTest.class.getSimpleName(), "PrivilegeConfig.xml", "PrivilegeUsers.xml", prepareConfigs(PrivilegeTest.class.getSimpleName(), "PrivilegeConfig.xml", "PrivilegeUsers.xml",
"PrivilegeRoles.xml"); "PrivilegeGroups.xml", "PrivilegeRoles.xml");
} }
@AfterClass @AfterClass
public static void destroy() { public static void destroy() {
removeConfigs(PrivilegeTest.class.getSimpleName()); //removeConfigs(PrivilegeTest.class.getSimpleName());
} }
@Before @Before
@ -137,7 +137,7 @@ public class PrivilegeTest extends AbstractPrivilegeTest {
try { try {
login(ADMIN, ArraysHelper.copyOf(PASS_ADMIN)); login(ADMIN, ArraysHelper.copyOf(PASS_ADMIN));
RoleRep roleRep = new RoleRep(ROLE_TEMP, new ArrayList<>()); RoleRep roleRep = new RoleRep(ROLE_TEMP, Map.of());
Certificate certificate = this.ctx.getCertificate(); Certificate certificate = this.ctx.getCertificate();
this.privilegeHandler.addRole(certificate, roleRep); this.privilegeHandler.addRole(certificate, roleRep);
@ -209,7 +209,9 @@ public class PrivilegeTest extends AbstractPrivilegeTest {
} }
}); });
MatcherAssert.assertThat(exception.getMessage(), containsString( MatcherAssert.assertThat(exception.getMessage(), containsString(
"User system_admin2 does not have the privilege li.strolch.privilege.handler.SystemAction with value li.strolch.privilege.test.model.TestSystemUserActionDeny needed for Restrictable li.strolch.privilege.test.model.TestSystemUserActionDeny")); "User system_admin2 does not have the privilege li.strolch.privilege.handler.SystemAction with value " +
"li.strolch.privilege.test.model.TestSystemUserActionDeny needed for Restrictable " +
"li.strolch.privilege.test.model.TestSystemUserActionDeny"));
} }
/** /**
@ -251,9 +253,12 @@ public class PrivilegeTest extends AbstractPrivilegeTest {
assertNotEquals("The", user.getFirstname()); assertNotEquals("The", user.getFirstname());
assertNotEquals("Admin", user.getLastname()); assertNotEquals("Admin", user.getLastname());
// let's add a new user bob // set new name
UserRep userRep = new UserRep(null, ADMIN, "The", "Admin", null, null, null, null, null); user.setFirstname("The");
this.privilegeHandler.updateUser(certificate, userRep); user.setLastname("Admin");
// update user
this.privilegeHandler.updateUser(certificate, user, null);
user = this.privilegeHandler.getUser(certificate, ADMIN); user = this.privilegeHandler.getUser(certificate, ADMIN);
assertEquals("The", user.getFirstname()); assertEquals("The", user.getFirstname());
@ -273,8 +278,9 @@ public class PrivilegeTest extends AbstractPrivilegeTest {
Certificate certificate = this.ctx.getCertificate(); Certificate certificate = this.ctx.getCertificate();
// let's add a new user bob // let's add a new user bob
UserRep userRep = new UserRep(null, BOB, null, null, null, null, null, null, null); UserRep userRep = new UserRep(BOB, BOB, "Bob", "Anderson", UserState.ENABLED,
this.privilegeHandler.updateUser(certificate, userRep); Set.of("AppUserLocationA"), null, null, null, null);
this.privilegeHandler.updateUser(certificate, userRep, null);
} finally { } finally {
logout(); logout();
} }
@ -282,25 +288,6 @@ public class PrivilegeTest extends AbstractPrivilegeTest {
MatcherAssert.assertThat(exception.getMessage(), containsString("User bob does not exist")); MatcherAssert.assertThat(exception.getMessage(), containsString("User bob does not exist"));
} }
@Test
public void shouldFailUpdateAdminNoChanges() {
PrivilegeException exception = assertThrows(PrivilegeException.class, () -> {
try {
login(ADMIN, ArraysHelper.copyOf(PASS_ADMIN));
Certificate certificate = this.ctx.getCertificate();
// let's add a new user bob
UserRep userRep = new UserRep(null, ADMIN, null, null, null, null, null, null, null);
this.privilegeHandler.updateUser(certificate, userRep);
} finally {
logout();
}
});
MatcherAssert.assertThat(exception.getMessage(),
containsString("All updateable fields are empty for update of user admin"));
}
@Test @Test
public void shouldQueryUsers() { public void shouldQueryUsers() {
try { try {
@ -308,7 +295,24 @@ public class PrivilegeTest extends AbstractPrivilegeTest {
Certificate certificate = this.ctx.getCertificate(); Certificate certificate = this.ctx.getCertificate();
UserRep selectorRep = new UserRep(null, ADMIN, null, null, null, null, null, null, null); UserRep selectorRep = new UserRep(null, ADMIN, null, null, null, null, null, null, null, null);
List<UserRep> users = this.privilegeHandler.queryUsers(certificate, selectorRep);
assertEquals(1, users.size());
assertEquals(ADMIN, users.get(0).getUsername());
} finally {
logout();
}
}
@Test
public void shouldQueryUsersByGroups() {
try {
login(ADMIN, ArraysHelper.copyOf(PASS_ADMIN));
Certificate certificate = this.ctx.getCertificate();
UserRep selectorRep = new UserRep(null, null, null, null, null, Set.of("GroupA"), null, null, null, null);
List<UserRep> users = this.privilegeHandler.queryUsers(certificate, selectorRep); List<UserRep> users = this.privilegeHandler.queryUsers(certificate, selectorRep);
assertEquals(1, users.size()); assertEquals(1, users.size());
assertEquals(ADMIN, users.get(0).getUsername()); assertEquals(ADMIN, users.get(0).getUsername());
@ -325,8 +329,8 @@ public class PrivilegeTest extends AbstractPrivilegeTest {
Certificate certificate = this.ctx.getCertificate(); Certificate certificate = this.ctx.getCertificate();
UserRep selectorRep = new UserRep(null, null, null, null, null, UserRep selectorRep = new UserRep(null, null, null, null, null, null, Set.of("PrivilegeAdmin"), null, null,
new HashSet<>(Collections.singletonList("PrivilegeAdmin")), null, null, null); null);
List<UserRep> users = this.privilegeHandler.queryUsers(certificate, selectorRep); List<UserRep> users = this.privilegeHandler.queryUsers(certificate, selectorRep);
assertEquals(2, users.size()); assertEquals(2, users.size());
assertEquals(ADMIN, users.get(0).getUsername()); assertEquals(ADMIN, users.get(0).getUsername());
@ -344,7 +348,7 @@ public class PrivilegeTest extends AbstractPrivilegeTest {
Certificate certificate = this.ctx.getCertificate(); Certificate certificate = this.ctx.getCertificate();
UserRep selectorRep = new UserRep(null, null, null, null, null, UserRep selectorRep = new UserRep(null, null, null, null, null,
new HashSet<>(Collections.singletonList(ROLE_TEMP)), null, null, null); new HashSet<>(Collections.singletonList(ROLE_TEMP)), null, null, null, null);
List<UserRep> users = this.privilegeHandler.queryUsers(certificate, selectorRep); List<UserRep> users = this.privilegeHandler.queryUsers(certificate, selectorRep);
assertEquals(0, users.size()); assertEquals(0, users.size());
@ -360,8 +364,10 @@ public class PrivilegeTest extends AbstractPrivilegeTest {
login(ADMIN, ArraysHelper.copyOf(PASS_ADMIN)); login(ADMIN, ArraysHelper.copyOf(PASS_ADMIN));
Certificate certificate = this.ctx.getCertificate(); Certificate certificate = this.ctx.getCertificate();
PrivilegeRep privilegeRep = new PrivilegeRep(PrivilegeHandler.PRIVILEGE_ACTION, "DefaultPrivilege", PrivilegeRep privilegeRep = new PrivilegeRep(PrivilegeHandler.PRIVILEGE_ACTION, "DefaultPrivilege",
true, Collections.emptySet(), Collections.emptySet()); true, Set.of(), Set.of());
this.privilegeHandler.addOrReplacePrivilegeOnRole(certificate, ROLE_APP_USER, privilegeRep); RoleRep role = this.privilegeHandler.getRole(certificate, ROLE_APP_USER);
role.addPrivilege(privilegeRep);
this.privilegeHandler.replaceRole(certificate, role);
} finally { } finally {
logout(); logout();
} }
@ -375,8 +381,10 @@ public class PrivilegeTest extends AbstractPrivilegeTest {
try { try {
login(ADMIN, ArraysHelper.copyOf(PASS_ADMIN)); login(ADMIN, ArraysHelper.copyOf(PASS_ADMIN));
Certificate certificate = this.ctx.getCertificate(); Certificate certificate = this.ctx.getCertificate();
this.privilegeHandler.addRoleToUser(certificate, ADMIN, ROLE_MY); UserRep user = this.privilegeHandler.getUser(certificate, ADMIN);
this.privilegeHandler.addRoleToUser(certificate, ADMIN, ROLE_MY2); user.addRole(ROLE_MY);
user.addRole(ROLE_MY2);
this.privilegeHandler.updateUser(certificate, user, null);
} finally { } finally {
logout(); logout();
} }
@ -540,15 +548,21 @@ public class PrivilegeTest extends AbstractPrivilegeTest {
login(ADMIN, ArraysHelper.copyOf(PASS_ADMIN)); login(ADMIN, ArraysHelper.copyOf(PASS_ADMIN));
PrivilegeRep passwordRep = new PrivilegeRep(PrivilegeHandler.PRIVILEGE_SET_USER_PASSWORD, PrivilegeRep passwordRep = new PrivilegeRep(PrivilegeHandler.PRIVILEGE_SET_USER_PASSWORD,
PRIVILEGE_USER_ACCESS, false, Collections.emptySet(), Collections.emptySet()); PRIVILEGE_USER_ACCESS, false, Set.of(), Set.of());
PrivilegeRep localeRep = new PrivilegeRep(PrivilegeHandler.PRIVILEGE_SET_USER_LOCALE, PRIVILEGE_USER_ACCESS, PrivilegeRep localeRep = new PrivilegeRep(PrivilegeHandler.PRIVILEGE_SET_USER_LOCALE, PRIVILEGE_USER_ACCESS,
false, Collections.emptySet(), Collections.emptySet()); false, Set.of(), Set.of());
RoleRep roleRep = new RoleRep(ROLE_CHANGE_PW, Arrays.asList(passwordRep, localeRep)); Map<String, PrivilegeRep> privileges = new HashMap<>();
privileges.put(passwordRep.getName(), passwordRep);
privileges.put(localeRep.getName(), localeRep);
RoleRep roleRep = new RoleRep(ROLE_CHANGE_PW, privileges);
Certificate certificate = this.ctx.getCertificate(); Certificate certificate = this.ctx.getCertificate();
this.privilegeHandler.addRole(certificate, roleRep); this.privilegeHandler.addRole(certificate, roleRep);
this.privilegeHandler.addRoleToUser(certificate, TED, ROLE_CHANGE_PW);
UserRep ted = this.privilegeHandler.getUser(certificate, TED);
ted.addRole(ROLE_CHANGE_PW);
this.privilegeHandler.updateUser(certificate, ted, null);
logger.info("Added " + ROLE_CHANGE_PW + " to " + TED); logger.info("Added " + ROLE_CHANGE_PW + " to " + TED);
this.privilegeHandler.persist(certificate); this.privilegeHandler.persist(certificate);
} finally { } finally {
@ -574,7 +588,9 @@ public class PrivilegeTest extends AbstractPrivilegeTest {
// testAddAppRoleToBob // testAddAppRoleToBob
login(ADMIN, ArraysHelper.copyOf(PASS_ADMIN)); login(ADMIN, ArraysHelper.copyOf(PASS_ADMIN));
Certificate certificate = this.ctx.getCertificate(); Certificate certificate = this.ctx.getCertificate();
this.privilegeHandler.addRoleToUser(certificate, BOB, ROLE_APP_USER); UserRep bob = this.privilegeHandler.getUser(certificate, BOB);
bob.addRole(ROLE_APP_USER);
this.privilegeHandler.updateUser(certificate, bob, null);
logger.info("Added " + ROLE_APP_USER + " to " + BOB); logger.info("Added " + ROLE_APP_USER + " to " + BOB);
this.privilegeHandler.persist(certificate); this.privilegeHandler.persist(certificate);
} finally { } finally {
@ -593,7 +609,10 @@ public class PrivilegeTest extends AbstractPrivilegeTest {
this.ctx.validateAction(restrictable); this.ctx.validateAction(restrictable);
fail("Should fail as bob does not have role app"); fail("Should fail as bob does not have role app");
} catch (AccessDeniedException e) { } catch (AccessDeniedException e) {
String msg = "User bob does not have the privilege li.strolch.privilege.test.model.TestRestrictable needed for Restrictable li.strolch.privilege.test.model.TestRestrictable and value li.strolch.privilege.test.model.TestRestrictable"; String msg =
"User bob does not have the privilege li.strolch.privilege.test.model.TestRestrictable needed for " +
"Restrictable li.strolch.privilege.test.model.TestRestrictable and value " +
"li.strolch.privilege.test.model.TestRestrictable";
assertEquals(msg, e.getLocalizedMessage()); assertEquals(msg, e.getLocalizedMessage());
} finally { } finally {
logout(); logout();
@ -656,7 +675,8 @@ public class PrivilegeTest extends AbstractPrivilegeTest {
// let's add a new user ted // let's add a new user ted
HashSet<String> roles = new HashSet<>(); HashSet<String> roles = new HashSet<>();
roles.add(ROLE_USER); roles.add(ROLE_USER);
userRep = new UserRep(null, TED, "Ted", "Newman", UserState.ENABLED, roles, null, new HashMap<>(), null); userRep = new UserRep(null, TED, "Ted", "Newman", UserState.ENABLED, Set.of(), roles, null, new HashMap<>(),
null);
Certificate certificate = this.ctx.getCertificate(); Certificate certificate = this.ctx.getCertificate();
this.privilegeHandler.addUser(certificate, userRep, null); this.privilegeHandler.addUser(certificate, userRep, null);
logger.info("Added user " + TED); logger.info("Added user " + TED);
@ -671,7 +691,9 @@ public class PrivilegeTest extends AbstractPrivilegeTest {
// testAddAdminRoleToBob // testAddAdminRoleToBob
login(ADMIN, ArraysHelper.copyOf(PASS_ADMIN)); login(ADMIN, ArraysHelper.copyOf(PASS_ADMIN));
Certificate certificate = this.ctx.getCertificate(); Certificate certificate = this.ctx.getCertificate();
this.privilegeHandler.addRoleToUser(certificate, BOB, ROLE_PRIVILEGE_ADMIN); UserRep bob = this.privilegeHandler.getUser(certificate, BOB);
bob.addRole(ROLE_PRIVILEGE_ADMIN);
this.privilegeHandler.updateUser(certificate, bob, null);
logger.info("Added " + ROLE_PRIVILEGE_ADMIN + " to " + ADMIN); logger.info("Added " + ROLE_PRIVILEGE_ADMIN + " to " + ADMIN);
this.privilegeHandler.persist(certificate); this.privilegeHandler.persist(certificate);
} finally { } finally {
@ -688,14 +710,14 @@ public class PrivilegeTest extends AbstractPrivilegeTest {
// auth as Bob // auth as Bob
login(BOB, ArraysHelper.copyOf(PASS_BOB)); login(BOB, ArraysHelper.copyOf(PASS_BOB));
// let's add a new user Ted // let's add a new user Ted
userRep = new UserRep("1", TED, "Ted", "And then Some", UserState.NEW, new HashSet<>(), null, userRep = new UserRep("1", TED, "Ted", "And then Some", UserState.NEW, Set.of(), Set.of(), null,
new HashMap<>(), null); new HashMap<>(), null);
certificate = this.ctx.getCertificate(); certificate = this.ctx.getCertificate();
this.privilegeHandler.addUser(certificate, userRep, null); this.privilegeHandler.addUser(certificate, userRep, null);
fail("User bob may not add a user as bob does not have admin rights!"); fail("User bob may not add a user as bob does not have admin rights!");
} catch (PrivilegeException e) { } catch (PrivilegeException e) {
String msg = MessageFormat.format(PrivilegeMessages.getString("Privilege.noprivilege.user"), String msg = MessageFormat.format(PrivilegeMessages.getString("Privilege.noprivilege.user"), BOB,
BOB, PrivilegeHandler.PRIVILEGE_ADD_USER); PrivilegeHandler.PRIVILEGE_ADD_USER);
assertEquals(msg, e.getMessage()); assertEquals(msg, e.getMessage());
} finally { } finally {
logout(); logout();
@ -716,7 +738,9 @@ public class PrivilegeTest extends AbstractPrivilegeTest {
// testAddRoleUserToBob // testAddRoleUserToBob
login(ADMIN, ArraysHelper.copyOf(PASS_ADMIN)); login(ADMIN, ArraysHelper.copyOf(PASS_ADMIN));
Certificate certificate = this.ctx.getCertificate(); Certificate certificate = this.ctx.getCertificate();
this.privilegeHandler.addRoleToUser(certificate, BOB, ROLE_USER); UserRep bob = this.privilegeHandler.getUser(certificate, BOB);
bob.addRole(ROLE_USER);
this.privilegeHandler.updateUser(certificate, bob, null);
this.privilegeHandler.persist(certificate); this.privilegeHandler.persist(certificate);
logout(); logout();
} finally { } finally {
@ -728,7 +752,7 @@ public class PrivilegeTest extends AbstractPrivilegeTest {
try { try {
// add role user // add role user
login(ADMIN, ArraysHelper.copyOf(PASS_ADMIN)); login(ADMIN, ArraysHelper.copyOf(PASS_ADMIN));
RoleRep roleRep = new RoleRep(ROLE_USER, new ArrayList<>()); RoleRep roleRep = new RoleRep(ROLE_USER, Map.of());
Certificate certificate = this.ctx.getCertificate(); Certificate certificate = this.ctx.getCertificate();
this.privilegeHandler.addRole(certificate, roleRep); this.privilegeHandler.addRole(certificate, roleRep);
this.privilegeHandler.persist(certificate); this.privilegeHandler.persist(certificate);
@ -768,8 +792,8 @@ public class PrivilegeTest extends AbstractPrivilegeTest {
login(ADMIN, ArraysHelper.copyOf(PASS_ADMIN)); login(ADMIN, ArraysHelper.copyOf(PASS_ADMIN));
// let's add a new user bob // let's add a new user bob
UserRep userRep = new UserRep(null, BOB, "Bob", "Newman", UserState.NEW, UserRep userRep = new UserRep(null, BOB, "Bob", "Newman", UserState.NEW, Set.of(), Set.of(ROLE_MY), null,
new HashSet<>(Collections.singletonList(ROLE_MY)), null, new HashMap<>(), null); new HashMap<>(), null);
Certificate certificate = this.ctx.getCertificate(); Certificate certificate = this.ctx.getCertificate();
this.privilegeHandler.addUser(certificate, userRep, null); this.privilegeHandler.addUser(certificate, userRep, null);
logger.info("Added user " + BOB); logger.info("Added user " + BOB);

View File

@ -17,7 +17,7 @@ public class SsoHandlerTest extends AbstractPrivilegeTest {
public static void init() { public static void init() {
removeConfigs(SsoHandlerTest.class.getSimpleName()); removeConfigs(SsoHandlerTest.class.getSimpleName());
prepareConfigs(SsoHandlerTest.class.getSimpleName(), "PrivilegeConfig.xml", "PrivilegeUsers.xml", prepareConfigs(SsoHandlerTest.class.getSimpleName(), "PrivilegeConfig.xml", "PrivilegeUsers.xml",
"PrivilegeRoles.xml"); "PrivilegeGroups.xml", "PrivilegeRoles.xml");
} }
@AfterClass @AfterClass
@ -39,6 +39,7 @@ public class SsoHandlerTest extends AbstractPrivilegeTest {
data.put("username", "admin"); data.put("username", "admin");
data.put("firstName", "Admin"); data.put("firstName", "Admin");
data.put("lastName", "Istrator"); data.put("lastName", "Istrator");
data.put("groups", "AppUserLocationA");
data.put("roles", "PrivilegeAdmin, AppUser"); data.put("roles", "PrivilegeAdmin, AppUser");
// auth // auth

View File

@ -4,14 +4,17 @@ import static li.strolch.privilege.test.XmlTest.SRC_TEST;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import java.io.File; import java.io.File;
import java.io.IOException;
import li.strolch.privilege.helper.WriteRolesFileHelper; import li.strolch.privilege.helper.WriteRolesFileHelper;
import org.junit.Test; import org.junit.Test;
import javax.xml.stream.XMLStreamException;
public class WriteRolesFileHelperTest { public class WriteRolesFileHelperTest {
@Test @Test
public void shouldReadAndWriteRolesFile() { public void shouldReadAndWriteRolesFile() throws XMLStreamException, IOException {
String src = SRC_TEST + "PrivilegeRoles.xml"; String src = SRC_TEST + "PrivilegeRoles.xml";
String dst = "target/WriteRolesFileHelperTest_roles.xml"; String dst = "target/WriteRolesFileHelperTest_roles.xml";
@ -19,7 +22,7 @@ public class WriteRolesFileHelperTest {
if (new File(dst).exists() && !new File(dst).delete()) if (new File(dst).exists() && !new File(dst).delete())
throw new IllegalStateException("Could not delete file " + dst); throw new IllegalStateException("Could not delete file " + dst);
WriteRolesFileHelper.main(new String[] { src, dst }); WriteRolesFileHelper.main(new String[]{src, dst});
assertTrue(new File(dst).exists()); assertTrue(new File(dst).exists());
} }

View File

@ -15,11 +15,8 @@
*/ */
package li.strolch.privilege.test; package li.strolch.privilege.test;
import li.strolch.privilege.handler.DefaultEncryptionHandler; import li.strolch.privilege.handler.*;
import li.strolch.privilege.handler.MailUserChallengeHandler; import li.strolch.privilege.model.Privilege;
import li.strolch.privilege.handler.PrivilegeHandler;
import li.strolch.privilege.handler.XmlPersistenceHandler;
import li.strolch.privilege.model.IPrivilege;
import li.strolch.privilege.model.UserState; import li.strolch.privilege.model.UserState;
import li.strolch.privilege.model.internal.*; import li.strolch.privilege.model.internal.*;
import li.strolch.privilege.test.model.DummySsoHandler; import li.strolch.privilege.test.model.DummySsoHandler;
@ -33,7 +30,10 @@ import org.junit.Test;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import javax.xml.stream.XMLStreamException;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.ZoneId; import java.time.ZoneId;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
@ -80,6 +80,11 @@ public class XmlTest {
throw new RuntimeException("Tmp still exists and can not be deleted at " + tmpFile.getAbsolutePath()); throw new RuntimeException("Tmp still exists and can not be deleted at " + tmpFile.getAbsolutePath());
} }
tmpFile = new File(TARGET_TEST + "PrivilegeGroupsTest.xml");
if (tmpFile.exists() && !tmpFile.delete()) {
throw new RuntimeException("Tmp still exists and can not be deleted at " + tmpFile.getAbsolutePath());
}
tmpFile = new File(TARGET_TEST + "PrivilegeRolesTest.xml"); tmpFile = new File(TARGET_TEST + "PrivilegeRolesTest.xml");
if (tmpFile.exists() && !tmpFile.delete()) { if (tmpFile.exists() && !tmpFile.delete()) {
throw new RuntimeException("Tmp still exists and can not be deleted at " + tmpFile.getAbsolutePath()); throw new RuntimeException("Tmp still exists and can not be deleted at " + tmpFile.getAbsolutePath());
@ -115,7 +120,7 @@ public class XmlTest {
} }
@Test @Test
public void canWriteConfig() { public void canWriteConfig() throws XMLStreamException, IOException {
Map<String, String> parameterMap = new HashMap<>(); Map<String, String> parameterMap = new HashMap<>();
Map<String, String> encryptionHandlerParameterMap = new HashMap<>(); Map<String, String> encryptionHandlerParameterMap = new HashMap<>();
@ -128,6 +133,7 @@ public class XmlTest {
PrivilegeContainerModel containerModel = new PrivilegeContainerModel(); PrivilegeContainerModel containerModel = new PrivilegeContainerModel();
containerModel.setParameterMap(parameterMap); containerModel.setParameterMap(parameterMap);
containerModel.setPrivilegeHandlerClassName(DefaultPrivilegeHandler.class.getName());
containerModel.setEncryptionHandlerClassName(DefaultEncryptionHandler.class.getName()); containerModel.setEncryptionHandlerClassName(DefaultEncryptionHandler.class.getName());
containerModel.setEncryptionHandlerParameterMap(encryptionHandlerParameterMap); containerModel.setEncryptionHandlerParameterMap(encryptionHandlerParameterMap);
containerModel.setPersistenceHandlerClassName(XmlPersistenceHandler.class.getName()); containerModel.setPersistenceHandlerClassName(XmlPersistenceHandler.class.getName());
@ -138,11 +144,38 @@ public class XmlTest {
containerModel.addPolicy("DefaultPrivilege", "li.strolch.privilege.policy.DefaultPrivilege"); containerModel.addPolicy("DefaultPrivilege", "li.strolch.privilege.policy.DefaultPrivilege");
File configFile = new File(TARGET_TEST + "PrivilegeTest.xml"); File configFile = new File(TARGET_TEST + "PrivilegeTest.xml");
PrivilegeConfigDomWriter configSaxWriter = new PrivilegeConfigDomWriter(containerModel, configFile); PrivilegeConfigSaxWriter configSaxWriter = new PrivilegeConfigSaxWriter(containerModel, configFile);
configSaxWriter.write(); configSaxWriter.write();
String fileHash = StringHelper.toHexString(FileHelper.hashFileSha256(configFile)); String expected = """
assertEquals("dcb6b3ed7198e0a7c88bf5c61c0bd6f0d684415f2a2f29429879edc6bc795f06", fileHash); <?xml version="1.0" encoding="UTF-8"?>
<Privilege>
<Container>
<Parameters>
<Parameter name="autoPersistOnPasswordChange" value="true"/>
</Parameters>
<PrivilegeHandler class="li.strolch.privilege.handler.DefaultPrivilegeHandler"/>
<EncryptionHandler class="li.strolch.privilege.handler.DefaultEncryptionHandler">
<Parameters>
<Parameter name="hashAlgorithm" value="SHA-256"/>
</Parameters>
</EncryptionHandler>
<PersistenceHandler class="li.strolch.privilege.handler.XmlPersistenceHandler">
<Parameters>
<Parameter name="basePath" value="target/test/"/>
<Parameter name="modelXmlFile" value="PrivilegeModel.xml"/>
</Parameters>
</PersistenceHandler>
<UserChallengeHandler class="li.strolch.privilege.handler.MailUserChallengeHandler"/>
<SsoHandler class="li.strolch.privilege.test.model.DummySsoHandler"/>
</Container>
<Policies>
<Policy name="DefaultPrivilege" class="li.strolch.privilege.policy.DefaultPrivilege"/>
</Policies>
</Privilege>
""";
assertEquals(expected, Files.readString(configFile.toPath()));
} }
@Test @Test
@ -166,12 +199,13 @@ public class XmlTest {
assertEquals("1", admin.getUserId()); assertEquals("1", admin.getUserId());
assertEquals("admin", admin.getUsername()); assertEquals("admin", admin.getUsername());
assertEquals("cb69962946617da006a2f95776d78b49e5ec7941d2bdb2d25cdb05f957f64344", assertEquals("cb69962946617da006a2f95776d78b49e5ec7941d2bdb2d25cdb05f957f64344",
StringHelper.toHexString(admin.getPasswordCrypt().getPassword())); StringHelper.toHexString(admin.getPasswordCrypt().password()));
assertEquals("61646d696e", StringHelper.toHexString(admin.getPasswordCrypt().getSalt())); assertEquals("61646d696e", StringHelper.toHexString(admin.getPasswordCrypt().salt()));
assertEquals("Application", admin.getFirstname()); assertEquals("Application", admin.getFirstname());
assertEquals("Administrator", admin.getLastname()); assertEquals("Administrator", admin.getLastname());
assertEquals(UserState.ENABLED, admin.getUserState()); assertEquals(UserState.ENABLED, admin.getUserState());
assertEquals("en-GB", admin.getLocale().toLanguageTag()); assertEquals("en-GB", admin.getLocale().toLanguageTag());
assertEquals(Set.of("GroupA"), admin.getGroups());
MatcherAssert.assertThat(admin.getRoles(), containsInAnyOrder("PrivilegeAdmin", "AppUser")); MatcherAssert.assertThat(admin.getRoles(), containsInAnyOrder("PrivilegeAdmin", "AppUser"));
Map<String, String> properties = admin.getProperties(); Map<String, String> properties = admin.getProperties();
assertEquals(new HashSet<>(Arrays.asList("organization", "organizationalUnit")), properties.keySet()); assertEquals(new HashSet<>(Arrays.asList("organization", "organizationalUnit")), properties.keySet());
@ -187,8 +221,54 @@ public class XmlTest {
assertEquals("Administrator", systemAdmin.getLastname()); assertEquals("Administrator", systemAdmin.getLastname());
assertEquals(UserState.SYSTEM, systemAdmin.getUserState()); assertEquals(UserState.SYSTEM, systemAdmin.getUserState());
assertEquals("en-GB", systemAdmin.getLocale().toLanguageTag()); assertEquals("en-GB", systemAdmin.getLocale().toLanguageTag());
assertEquals(Set.of(), systemAdmin.getGroups());
MatcherAssert.assertThat(systemAdmin.getRoles(), containsInAnyOrder("system_admin_privileges")); MatcherAssert.assertThat(systemAdmin.getRoles(), containsInAnyOrder("system_admin_privileges"));
assertTrue(systemAdmin.getProperties().isEmpty()); assertTrue(systemAdmin.getProperties().isEmpty());
// admin2
User admin2 = findUser("admin2", users);
assertEquals("1", admin2.getUserId());
assertEquals("admin2", admin2.getUsername());
assertEquals("8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918",
StringHelper.toHexString(admin2.getPasswordCrypt().password()));
assertEquals("Application", admin2.getFirstname());
assertEquals("Administrator", admin2.getLastname());
assertEquals(UserState.ENABLED, admin2.getUserState());
assertEquals("en-GB", admin2.getLocale().toLanguageTag());
MatcherAssert.assertThat(admin2.getGroups(), containsInAnyOrder("AppUserLocationA"));
MatcherAssert.assertThat(admin2.getRoles(), containsInAnyOrder("PrivilegeAdmin"));
properties = admin2.getProperties();
assertEquals(new HashSet<>(Arrays.asList("organization", "organizationalUnit")), properties.keySet());
assertEquals("eitchnet.ch", properties.get("organization"));
assertEquals("Development", properties.get("organizationalUnit"));
}
@Test
public void canReadGroups() {
PrivilegeGroupsSaxReader xmlHandler = new PrivilegeGroupsSaxReader();
File xmlFile = new File(SRC_TEST + "PrivilegeGroups.xml");
XmlHelper.parseDocument(xmlFile, xmlHandler);
Map<String, Group> groups = xmlHandler.getGroups();
assertNotNull(groups);
assertEquals(2, groups.size());
// group AppUserLocationA
Group group = groups.get("AppUserLocationA");
assertEquals("AppUserLocationA", group.name());
MatcherAssert.assertThat(group.roles(), containsInAnyOrder("AppUser", "ModelAccessor", "UserPrivileges"));
Map<String, String> properties = group.getProperties();
assertEquals(new HashSet<>(List.of("location")), properties.keySet());
assertEquals("LocationA", properties.get("location"));
group = groups.get("GroupA");
assertEquals("GroupA", group.name());
properties = group.getProperties();
assertTrue(properties.isEmpty());
assertTrue(group.roles().isEmpty());
} }
@Test @Test
@ -212,23 +292,22 @@ public class XmlTest {
// PrivilegeAdmin // PrivilegeAdmin
Role privilegeAdmin = findRole("PrivilegeAdmin", roles); Role privilegeAdmin = findRole("PrivilegeAdmin", roles);
assertEquals("PrivilegeAdmin", privilegeAdmin.getName()); assertEquals("PrivilegeAdmin", privilegeAdmin.getName());
assertEquals(18, privilegeAdmin.getPrivilegeNames().size()); assertEquals(16, privilegeAdmin.getPrivilegeNames().size());
IPrivilege privilegeAction = privilegeAdmin.getPrivilege(PrivilegeHandler.PRIVILEGE_ACTION); Privilege privilegeAction = privilegeAdmin.getPrivilege(PrivilegeHandler.PRIVILEGE_ACTION);
assertFalse(privilegeAction.isAllAllowed()); assertFalse(privilegeAction.isAllAllowed());
assertEquals(5, privilegeAction.getAllowList().size()); assertEquals(5, privilegeAction.getAllowList().size());
assertEquals(0, privilegeAction.getDenyList().size()); assertEquals(0, privilegeAction.getDenyList().size());
assertEquals("DefaultPrivilege", privilegeAction.getPolicy()); assertEquals("DefaultPrivilege", privilegeAction.getPolicy());
IPrivilege privilegeAddRole = privilegeAdmin.getPrivilege(PrivilegeHandler.PRIVILEGE_ADD_ROLE); Privilege privilegeAddRole = privilegeAdmin.getPrivilege(PrivilegeHandler.PRIVILEGE_ADD_ROLE);
assertTrue(privilegeAddRole.isAllAllowed()); assertTrue(privilegeAddRole.isAllAllowed());
assertEquals(0, privilegeAddRole.getAllowList().size()); assertEquals(0, privilegeAddRole.getAllowList().size());
assertEquals(0, privilegeAddRole.getDenyList().size()); assertEquals(0, privilegeAddRole.getDenyList().size());
IPrivilege privilegeRemRoleFromUser = privilegeAdmin.getPrivilege( Privilege privilegeRemRole = privilegeAdmin.getPrivilege(PrivilegeHandler.PRIVILEGE_REMOVE_ROLE);
PrivilegeHandler.PRIVILEGE_REMOVE_ROLE_FROM_USER); assertTrue(privilegeRemRole.isAllAllowed());
assertTrue(privilegeRemRoleFromUser.isAllAllowed()); assertEquals(0, privilegeRemRole.getAllowList().size());
assertEquals(0, privilegeRemRoleFromUser.getAllowList().size()); assertEquals(0, privilegeRemRole.getDenyList().size());
assertEquals(0, privilegeRemRoleFromUser.getDenyList().size());
// AppUser // AppUser
Role appUser = findRole("AppUser", roles); Role appUser = findRole("AppUser", roles);
@ -236,7 +315,7 @@ public class XmlTest {
assertEquals(new HashSet<>(Collections.singletonList("li.strolch.privilege.test.model.TestRestrictable")), assertEquals(new HashSet<>(Collections.singletonList("li.strolch.privilege.test.model.TestRestrictable")),
appUser.getPrivilegeNames()); appUser.getPrivilegeNames());
IPrivilege testRestrictable = appUser.getPrivilege("li.strolch.privilege.test.model.TestRestrictable"); Privilege testRestrictable = appUser.getPrivilege("li.strolch.privilege.test.model.TestRestrictable");
assertEquals("li.strolch.privilege.test.model.TestRestrictable", testRestrictable.getName()); assertEquals("li.strolch.privilege.test.model.TestRestrictable", testRestrictable.getName());
assertEquals("DefaultPrivilege", testRestrictable.getPolicy()); assertEquals("DefaultPrivilege", testRestrictable.getPolicy());
assertTrue(testRestrictable.isAllAllowed()); assertTrue(testRestrictable.isAllAllowed());
@ -251,7 +330,7 @@ public class XmlTest {
containsInAnyOrder("li.strolch.privilege.handler.SystemAction", containsInAnyOrder("li.strolch.privilege.handler.SystemAction",
"li.strolch.privilege.test.model.TestSystemRestrictable")); "li.strolch.privilege.test.model.TestSystemRestrictable"));
IPrivilege testSystemUserAction = systemAdminPrivileges.getPrivilege( Privilege testSystemUserAction = systemAdminPrivileges.getPrivilege(
"li.strolch.privilege.handler.SystemAction"); "li.strolch.privilege.handler.SystemAction");
assertEquals("li.strolch.privilege.handler.SystemAction", testSystemUserAction.getName()); assertEquals("li.strolch.privilege.handler.SystemAction", testSystemUserAction.getName());
assertEquals("DefaultPrivilege", testSystemUserAction.getPolicy()); assertEquals("DefaultPrivilege", testSystemUserAction.getPolicy());
@ -259,7 +338,7 @@ public class XmlTest {
assertEquals(1, testSystemUserAction.getAllowList().size()); assertEquals(1, testSystemUserAction.getAllowList().size());
assertEquals(1, testSystemUserAction.getDenyList().size()); assertEquals(1, testSystemUserAction.getDenyList().size());
IPrivilege testSystemRestrictable = systemAdminPrivileges.getPrivilege( Privilege testSystemRestrictable = systemAdminPrivileges.getPrivilege(
"li.strolch.privilege.test.model.TestSystemRestrictable"); "li.strolch.privilege.test.model.TestSystemRestrictable");
assertEquals("li.strolch.privilege.test.model.TestSystemRestrictable", testSystemRestrictable.getName()); assertEquals("li.strolch.privilege.test.model.TestSystemRestrictable", testSystemRestrictable.getName());
assertEquals("DefaultPrivilege", testSystemRestrictable.getPolicy()); assertEquals("DefaultPrivilege", testSystemRestrictable.getPolicy());
@ -274,7 +353,7 @@ public class XmlTest {
MatcherAssert.assertThat(restrictedRole.getPrivilegeNames(), MatcherAssert.assertThat(restrictedRole.getPrivilegeNames(),
containsInAnyOrder("li.strolch.privilege.handler.SystemAction")); containsInAnyOrder("li.strolch.privilege.handler.SystemAction"));
IPrivilege testSystemUserAction2 = restrictedRole.getPrivilege("li.strolch.privilege.handler.SystemAction"); Privilege testSystemUserAction2 = restrictedRole.getPrivilege("li.strolch.privilege.handler.SystemAction");
assertEquals("li.strolch.privilege.handler.SystemAction", testSystemUserAction2.getName()); assertEquals("li.strolch.privilege.handler.SystemAction", testSystemUserAction2.getName());
assertEquals("DefaultPrivilege", testSystemUserAction2.getPolicy()); assertEquals("DefaultPrivilege", testSystemUserAction2.getPolicy());
assertFalse(testSystemUserAction2.isAllAllowed()); assertFalse(testSystemUserAction2.isAllAllowed());
@ -295,36 +374,42 @@ public class XmlTest {
} }
@Test @Test
public void canWriteUsers() { public void canWriteUsers() throws XMLStreamException, IOException {
Map<String, String> propertyMap; Map<String, String> propertyMap;
Set<String> groups;
Set<String> userRoles; Set<String> userRoles;
List<User> users = new ArrayList<>(); List<User> users = new ArrayList<>();
propertyMap = new HashMap<>(); propertyMap = new HashMap<>();
propertyMap.put("prop1", "value1"); propertyMap.put("prop1", "value1");
groups = new HashSet<>();
groups.add("group1");
userRoles = new HashSet<>(); userRoles = new HashSet<>();
userRoles.add("role1"); userRoles.add("role1");
UserHistory history = new UserHistory(); UserHistory history = UserHistory.EMPTY.withFirstLogin(
history.setFirstLogin(ZonedDateTime.of(LocalDateTime.of(2020, 1, 2, 2, 3, 4, 5), ZoneId.systemDefault())); ZonedDateTime.of(LocalDateTime.of(2020, 1, 2, 2, 3, 4, 5), ZoneId.systemDefault()));
User user1 = new User("1", "user1", User user1 = new User("1", "user1",
new PasswordCrypt("blabla".getBytes(), "blabla".getBytes(), "PBKDF2WithHmacSHA512", 10000, 256), "Bob", new PasswordCrypt("blabla".getBytes(), "blabla".getBytes(), "PBKDF2WithHmacSHA512", 10000, 256), "Bob",
"White", UserState.DISABLED, userRoles, Locale.ENGLISH, propertyMap, false, history); "White", UserState.DISABLED, groups, userRoles, Locale.ENGLISH, propertyMap, false, history);
users.add(user1); users.add(user1);
propertyMap = new HashMap<>(); propertyMap = new HashMap<>();
propertyMap.put("prop2", "value2"); propertyMap.put("prop2", "value2");
groups = new HashSet<>();
groups.add("group2");
userRoles = new HashSet<>(); userRoles = new HashSet<>();
userRoles.add("role2"); userRoles.add("role2");
history = new UserHistory(); history = UserHistory.EMPTY.withFirstLogin(
history.setFirstLogin(ZonedDateTime.of(LocalDateTime.of(2020, 1, 2, 2, 3, 4, 5), ZoneId.systemDefault())); ZonedDateTime.of(LocalDateTime.of(2020, 1, 2, 2, 3, 4, 5), ZoneId.systemDefault()))
history.setLastLogin(ZonedDateTime.of(LocalDateTime.of(2020, 1, 5, 2, 3, 4, 5), ZoneId.systemDefault())); .withLastLogin(ZonedDateTime.of(LocalDateTime.of(2020, 1, 5, 2, 3, 4, 5), ZoneId.systemDefault()));
User user2 = new User("2", "user2", new PasswordCrypt("haha".getBytes(), "haha".getBytes(), null, -1, -1), User user2 = new User("2", "user2", new PasswordCrypt("haha".getBytes(), "haha".getBytes(), null, -1, -1),
"Leonard", "Sheldon", UserState.ENABLED, userRoles, Locale.ENGLISH, propertyMap, false, history); "Leonard", "Sheldon", UserState.ENABLED, groups, userRoles, Locale.ENGLISH, propertyMap, false,
history);
users.add(user2); users.add(user2);
File modelFile = new File(TARGET_TEST + "PrivilegeUsersTest.xml"); File modelFile = new File(TARGET_TEST + "PrivilegeUsersTest.xml");
PrivilegeUsersDomWriter configSaxWriter = new PrivilegeUsersDomWriter(users, modelFile); PrivilegeUsersSaxWriter configSaxWriter = new PrivilegeUsersSaxWriter(users, modelFile);
configSaxWriter.write(); configSaxWriter.write();
PrivilegeUsersSaxReader xmlHandler = new PrivilegeUsersSaxReader(true); PrivilegeUsersSaxReader xmlHandler = new PrivilegeUsersSaxReader(true);
@ -342,8 +427,8 @@ public class XmlTest {
assertEquals(user1.getFirstname(), parsedUser1.getFirstname()); assertEquals(user1.getFirstname(), parsedUser1.getFirstname());
assertEquals(user1.getLastname(), parsedUser1.getLastname()); assertEquals(user1.getLastname(), parsedUser1.getLastname());
assertEquals(user1.getLocale(), parsedUser1.getLocale()); assertEquals(user1.getLocale(), parsedUser1.getLocale());
assertArrayEquals(user1.getPasswordCrypt().getPassword(), parsedUser1.getPasswordCrypt().getPassword()); assertArrayEquals(user1.getPasswordCrypt().password(), parsedUser1.getPasswordCrypt().password());
assertArrayEquals(user1.getPasswordCrypt().getSalt(), parsedUser1.getPasswordCrypt().getSalt()); assertArrayEquals(user1.getPasswordCrypt().salt(), parsedUser1.getPasswordCrypt().salt());
assertEquals(user1.getProperties(), parsedUser1.getProperties()); assertEquals(user1.getProperties(), parsedUser1.getProperties());
assertEquals(user1.getUserId(), parsedUser1.getUserId()); assertEquals(user1.getUserId(), parsedUser1.getUserId());
assertEquals(user1.getUserState(), parsedUser1.getUserState()); assertEquals(user1.getUserState(), parsedUser1.getUserState());
@ -352,8 +437,8 @@ public class XmlTest {
assertEquals(user2.getFirstname(), parsedUser2.getFirstname()); assertEquals(user2.getFirstname(), parsedUser2.getFirstname());
assertEquals(user2.getLastname(), parsedUser2.getLastname()); assertEquals(user2.getLastname(), parsedUser2.getLastname());
assertEquals(user2.getLocale(), parsedUser2.getLocale()); assertEquals(user2.getLocale(), parsedUser2.getLocale());
assertArrayEquals(user2.getPasswordCrypt().getPassword(), parsedUser2.getPasswordCrypt().getPassword()); assertArrayEquals(user2.getPasswordCrypt().password(), parsedUser2.getPasswordCrypt().password());
assertArrayEquals(user2.getPasswordCrypt().getSalt(), parsedUser2.getPasswordCrypt().getSalt()); assertArrayEquals(user2.getPasswordCrypt().salt(), parsedUser2.getPasswordCrypt().salt());
assertEquals(user2.getProperties(), parsedUser2.getProperties()); assertEquals(user2.getProperties(), parsedUser2.getProperties());
assertEquals(user2.getUserId(), parsedUser2.getUserId()); assertEquals(user2.getUserId(), parsedUser2.getUserId());
assertEquals(user2.getUserState(), parsedUser2.getUserState()); assertEquals(user2.getUserState(), parsedUser2.getUserState());
@ -361,13 +446,48 @@ public class XmlTest {
} }
@Test @Test
public void canWriteRoles() { public void canWriteGroups() throws XMLStreamException, IOException {
Map<String, IPrivilege> privilegeMap; Map<String, String> propertyMap;
Set<String> roles;
List<Group> groups = new ArrayList<>();
propertyMap = new HashMap<>();
propertyMap.put("prop1", "value1");
roles = new HashSet<>();
roles.add("role1");
Group newGroup = new Group("group1", roles, propertyMap);
groups.add(newGroup);
File modelFile = new File(TARGET_TEST + "PrivilegeGroupsTest.xml");
PrivilegeGroupsSaxWriter configSaxWriter = new PrivilegeGroupsSaxWriter(groups, modelFile);
configSaxWriter.write();
PrivilegeGroupsSaxReader xmlHandler = new PrivilegeGroupsSaxReader();
XmlHelper.parseDocument(modelFile, xmlHandler);
Map<String, Group> parsedGroups = xmlHandler.getGroups();
assertNotNull(parsedGroups);
assertEquals(1, parsedGroups.size());
// group group1
Group parsedGroup1 = parsedGroups.get("group1");
assertNotNull(parsedGroup1);
assertEquals("group1", parsedGroup1.name());
MatcherAssert.assertThat(parsedGroup1.roles(), containsInAnyOrder("role1"));
Map<String, String> properties = parsedGroup1.getProperties();
assertEquals(new HashSet<>(List.of("prop1")), properties.keySet());
assertEquals("value1", properties.get("prop1"));
}
@Test
public void canWriteRoles() throws XMLStreamException, IOException {
Map<String, Privilege> privilegeMap;
List<Role> roles = new ArrayList<>(); List<Role> roles = new ArrayList<>();
Set<String> list = Collections.emptySet(); Set<String> list = Collections.emptySet();
privilegeMap = new HashMap<>(); privilegeMap = new HashMap<>();
privilegeMap.put("priv1", new PrivilegeImpl("priv1", "DefaultPrivilege", true, list, list)); privilegeMap.put("priv1", new Privilege("priv1", "DefaultPrivilege", true, list, list));
Role role1 = new Role("role1", privilegeMap); Role role1 = new Role("role1", privilegeMap);
roles.add(role1); roles.add(role1);
@ -376,13 +496,13 @@ public class XmlTest {
denyList.add("myself"); denyList.add("myself");
Set<String> allowList = new HashSet<>(); Set<String> allowList = new HashSet<>();
allowList.add("other"); allowList.add("other");
privilegeMap.put("priv2", new PrivilegeImpl("priv2", "DefaultPrivilege", false, denyList, allowList)); privilegeMap.put("priv2", new Privilege("priv2", "DefaultPrivilege", false, denyList, allowList));
Role role2 = new Role("role2", privilegeMap); Role role2 = new Role("role2", privilegeMap);
roles.add(role2); roles.add(role2);
File modelFile = new File(TARGET_TEST + "PrivilegeRolesTest.xml"); File modelFile = new File(TARGET_TEST + "PrivilegeRolesTest.xml");
PrivilegeRolesDomWriter configSaxWriter = new PrivilegeRolesDomWriter(roles, modelFile); PrivilegeRolesSaxWriter writer = new PrivilegeRolesSaxWriter(roles, modelFile);
configSaxWriter.write(); writer.write();
PrivilegeRolesSaxReader xmlHandler = new PrivilegeRolesSaxReader(); PrivilegeRolesSaxReader xmlHandler = new PrivilegeRolesSaxReader();
XmlHelper.parseDocument(modelFile, xmlHandler); XmlHelper.parseDocument(modelFile, xmlHandler);
@ -400,8 +520,8 @@ public class XmlTest {
Set<String> privilegeNames = role1.getPrivilegeNames(); Set<String> privilegeNames = role1.getPrivilegeNames();
assertEquals(privilegeNames, parsedRole1.getPrivilegeNames()); assertEquals(privilegeNames, parsedRole1.getPrivilegeNames());
for (String privilegeName : privilegeNames) { for (String privilegeName : privilegeNames) {
IPrivilege privilege = role1.getPrivilege(privilegeName); Privilege privilege = role1.getPrivilege(privilegeName);
IPrivilege privilege2 = parsedRole1.getPrivilege(privilegeName); Privilege privilege2 = parsedRole1.getPrivilege(privilegeName);
assertNotNull(privilege); assertNotNull(privilege);
assertNotNull(privilege2); assertNotNull(privilege2);
@ -415,8 +535,8 @@ public class XmlTest {
assertEquals(role2.getPrivilegeNames(), parsedRole2.getPrivilegeNames()); assertEquals(role2.getPrivilegeNames(), parsedRole2.getPrivilegeNames());
privilegeNames = role2.getPrivilegeNames(); privilegeNames = role2.getPrivilegeNames();
for (String privilegeName : privilegeNames) { for (String privilegeName : privilegeNames) {
IPrivilege privilege = role2.getPrivilege(privilegeName); Privilege privilege = role2.getPrivilege(privilegeName);
IPrivilege privilege2 = parsedRole2.getPrivilege(privilegeName); Privilege privilege2 = parsedRole2.getPrivilege(privilegeName);
assertNotNull(privilege); assertNotNull(privilege);
assertNotNull(privilege2); assertNotNull(privilege2);

View File

@ -28,9 +28,10 @@ public class DummySsoHandler implements SingleSignOnHandler {
@SuppressWarnings("unchecked") Map<String, String> map = (Map<String, String>) data; @SuppressWarnings("unchecked") Map<String, String> map = (Map<String, String>) data;
Set<String> groups = Arrays.stream(map.get("groups").split(",")).map(String::trim).collect(Collectors.toSet());
Set<String> roles = Arrays.stream(map.get("roles").split(",")).map(String::trim).collect(Collectors.toSet()); Set<String> roles = Arrays.stream(map.get("roles").split(",")).map(String::trim).collect(Collectors.toSet());
Map<String, String> properties = new HashMap<>(); Map<String, String> properties = new HashMap<>();
return new User(map.get("userId"), map.get("username"), null, map.get("firstName"), map.get("lastName"), return new User(map.get("userId"), map.get("username"), null, map.get("firstName"), map.get("lastName"),
UserState.REMOTE, roles, Locale.ENGLISH, properties, false, new UserHistory()); UserState.REMOTE, groups, roles, Locale.ENGLISH, properties, false, UserHistory.EMPTY);
} }
} }

View File

@ -27,6 +27,7 @@
<Parameters> <Parameters>
<Parameter name="basePath" value="target/${target}"/> <Parameter name="basePath" value="target/${target}"/>
<Parameter name="usersXmlFile" value="PrivilegeUsersMerge.xml"/> <Parameter name="usersXmlFile" value="PrivilegeUsersMerge.xml"/>
<Parameter name="groupsXmlFile" value="PrivilegeGroupsMerge.xml"/>
<Parameter name="rolesXmlFile" value="PrivilegeRolesMerge.xml"/> <Parameter name="rolesXmlFile" value="PrivilegeRolesMerge.xml"/>
</Parameters> </Parameters>
</PersistenceHandler> </PersistenceHandler>

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<Groups>
<Group name="GroupA"/>
<Group name="AppUserLocationA">
<Roles>
<Role>AppUser</Role>
<Role>ModelAccessor</Role>
<Role>UserPrivileges</Role>
</Roles>
<Properties>
<Property name="location" value="LocationA"/>
</Properties>
</Group>
</Groups>

View File

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<Groups>
<Group name="GroupA1">
<Roles>
<Role>RoleA1</Role>
</Roles>
<Properties>
<Property name="location" value="LocationA1"/>
</Properties>
</Group>
<Group name="GroupA2">
<Roles>
<Role>RoleA2</Role>
</Roles>
<Properties>
<Property name="location" value="LocationA2"/>
</Properties>
</Group>
<Group name="GroupB1">
<Roles>
<Role>RoleB1</Role>
</Roles>
<Properties>
<Property name="location" value="LocationB1"/>
</Properties>
</Group>
<Group name="GroupB2">
<Roles>
<Role>RoleB2</Role>
</Roles>
<Properties>
<Property name="location" value="LocationB2"/>
</Properties>
</Group>
</Groups>

View File

@ -2,61 +2,32 @@
<Roles> <Roles>
<Role name="PrivilegeAdmin"> <Role name="PrivilegeAdmin">
<Privilege name="li.strolch.service.api.Service" policy="DefaultPrivilege"> <Privilege name="GetSession" policy="UserSessionAccessPrivilege">
<Allow>li.strolch.service.privilege.users.PrivilegeUpdateUserService</Allow>
<Allow>li.strolch.service.privilege.users.PrivilegeUpdateUserRolesService</Allow>
<Allow>li.strolch.service.privilege.users.PrivilegeSetUserPasswordService</Allow>
<Allow>li.strolch.service.privilege.users.PrivilegeSetUserLocaleService</Allow>
<Allow>li.strolch.service.privilege.users.PrivilegeRemoveUserService</Allow>
<Allow>li.strolch.service.privilege.users.PrivilegeRemoveRoleFromUserService</Allow>
<Allow>li.strolch.service.privilege.users.PrivilegeAddUserService</Allow>
<Allow>li.strolch.service.privilege.users.PrivilegeAddRoleToUserService</Allow>
<Allow>li.strolch.service.privilege.roles.PrivilegeUpdateRoleService</Allow>
<Allow>li.strolch.service.privilege.roles.PrivilegeRemoveRoleService</Allow>
<Allow>li.strolch.service.privilege.roles.PrivilegeRemovePrivilegeFromRoleService</Allow>
<Allow>li.strolch.service.privilege.roles.PrivilegeAddRoleService</Allow>
<Allow>li.strolch.service.privilege.roles.PrivilegeAddOrReplacePrivilegeOnRoleService</Allow>
</Privilege>
<Privilege name="PrivilegeAddUser" policy="UserAccessPrivilege">
<AllAllowed>true</AllAllowed>
</Privilege>
<Privilege name="PrivilegeRemoveUser" policy="UserAccessPrivilege">
<AllAllowed>true</AllAllowed> <AllAllowed>true</AllAllowed>
</Privilege> </Privilege>
<Privilege name="InvalidateSession" policy="UserSessionAccessPrivilege"> <Privilege name="InvalidateSession" policy="UserSessionAccessPrivilege">
<AllAllowed>true</AllAllowed> <AllAllowed>true</AllAllowed>
</Privilege> </Privilege>
<Privilege name="PrivilegeSetUserPassword" policy="UserAccessPrivilege">
<AllAllowed>true</AllAllowed>
</Privilege>
<Privilege name="RequirePasswordChange" policy="UserAccessPrivilege">
<AllAllowed>true</AllAllowed>
</Privilege>
<Privilege name="PrivilegeSetUserLocale" policy="UserAccessPrivilege">
<AllAllowed>true</AllAllowed>
</Privilege>
<Privilege name="PrivilegeAction" policy="DefaultPrivilege"> <Privilege name="PrivilegeAction" policy="DefaultPrivilege">
<Allow>Reload</Allow> <Allow>GetCertificates</Allow>
<Allow>GetPolicies</Allow> <Allow>GetPolicies</Allow>
<Allow>Persist</Allow> <Allow>Persist</Allow>
<Allow>GetCertificates</Allow>
<Allow>PersistSessions</Allow> <Allow>PersistSessions</Allow>
<Allow>Reload</Allow>
</Privilege> </Privilege>
<Privilege name="PrivilegeGetUser" policy="UserAccessPrivilege"> <Privilege name="PrivilegeAddRole" policy="RoleAccessPrivilege">
<AllAllowed>true</AllAllowed> <AllAllowed>true</AllAllowed>
</Privilege> </Privilege>
<Privilege name="PrivilegeSetUserState" policy="UserAccessPrivilege"> <Privilege name="PrivilegeAddUser" policy="UserAccessPrivilege">
<Deny>SYSTEM</Deny>
<Allow>DISABLED</Allow>
<Allow>ENABLED</Allow>
</Privilege>
<Privilege name="PrivilegeAddRoleToUser" policy="UserAccessPrivilege">
<AllAllowed>true</AllAllowed> <AllAllowed>true</AllAllowed>
</Privilege> </Privilege>
<Privilege name="PrivilegeGetRole" policy="RoleAccessPrivilege"> <Privilege name="PrivilegeGetRole" policy="RoleAccessPrivilege">
<AllAllowed>true</AllAllowed> <AllAllowed>true</AllAllowed>
</Privilege> </Privilege>
<Privilege name="GetSession" policy="UserSessionAccessPrivilege"> <Privilege name="PrivilegeGetUser" policy="UserAccessPrivilege">
<AllAllowed>true</AllAllowed>
</Privilege>
<Privilege name="PrivilegeModifyRole" policy="RoleAccessPrivilege">
<AllAllowed>true</AllAllowed> <AllAllowed>true</AllAllowed>
</Privilege> </Privilege>
<Privilege name="PrivilegeModifyUser" policy="UserAccessPrivilege"> <Privilege name="PrivilegeModifyUser" policy="UserAccessPrivilege">
@ -65,15 +36,35 @@
<Privilege name="PrivilegeRemoveRole" policy="RoleAccessPrivilege"> <Privilege name="PrivilegeRemoveRole" policy="RoleAccessPrivilege">
<AllAllowed>true</AllAllowed> <AllAllowed>true</AllAllowed>
</Privilege> </Privilege>
<Privilege name="PrivilegeRemoveRoleFromUser" policy="UserAccessPrivilege"> <Privilege name="PrivilegeRemoveUser" policy="UserAccessPrivilege">
<AllAllowed>true</AllAllowed> <AllAllowed>true</AllAllowed>
</Privilege> </Privilege>
<Privilege name="PrivilegeModifyRole" policy="RoleAccessPrivilege"> <Privilege name="PrivilegeSetUserLocale" policy="UserAccessPrivilege">
<AllAllowed>true</AllAllowed> <AllAllowed>true</AllAllowed>
</Privilege> </Privilege>
<Privilege name="PrivilegeAddRole" policy="RoleAccessPrivilege"> <Privilege name="PrivilegeSetUserPassword" policy="UserAccessPrivilege">
<AllAllowed>true</AllAllowed> <AllAllowed>true</AllAllowed>
</Privilege> </Privilege>
<Privilege name="PrivilegeSetUserState" policy="UserAccessPrivilege">
<Deny>SYSTEM</Deny>
<Allow>DISABLED</Allow>
<Allow>ENABLED</Allow>
</Privilege>
<Privilege name="RequirePasswordChange" policy="UserAccessPrivilege">
<AllAllowed>true</AllAllowed>
</Privilege>
<Privilege name="li.strolch.service.api.Service" policy="DefaultPrivilege">
<Allow>li.strolch.service.privilege.roles.PrivilegeAddRoleService</Allow>
<Allow>li.strolch.service.privilege.roles.PrivilegeRemoveRoleService</Allow>
<Allow>li.strolch.service.privilege.roles.PrivilegeUpdateRoleService</Allow>
<Allow>li.strolch.service.privilege.users.PrivilegeAddUserService</Allow>
<Allow>li.strolch.service.privilege.users.PrivilegeRemoveUserService</Allow>
<Allow>li.strolch.service.privilege.users.PrivilegeSetUserLocaleService</Allow>
<Allow>li.strolch.service.privilege.users.PrivilegeSetUserPasswordService</Allow>
<Allow>li.strolch.service.privilege.users.PrivilegeSetUserStateService</Allow>
<Allow>li.strolch.service.privilege.users.PrivilegeUpdateUserRolesService</Allow>
<Allow>li.strolch.service.privilege.users.PrivilegeUpdateUserService</Allow>
</Privilege>
</Role> </Role>
<Role name="AppUser"> <Role name="AppUser">

View File

@ -6,6 +6,9 @@
<Lastname>Administrator</Lastname> <Lastname>Administrator</Lastname>
<State>ENABLED</State> <State>ENABLED</State>
<Locale>en-GB</Locale> <Locale>en-GB</Locale>
<Groups>
<Group>GroupA</Group>
</Groups>
<Roles> <Roles>
<Role>PrivilegeAdmin</Role> <Role>PrivilegeAdmin</Role>
<Role>AppUser</Role> <Role>AppUser</Role>
@ -21,9 +24,11 @@
<Lastname>Administrator</Lastname> <Lastname>Administrator</Lastname>
<State>ENABLED</State> <State>ENABLED</State>
<Locale>en-GB</Locale> <Locale>en-GB</Locale>
<Groups>
<Group>AppUserLocationA</Group>
</Groups>
<Roles> <Roles>
<Role>PrivilegeAdmin</Role> <Role>PrivilegeAdmin</Role>
<Role>AppUser</Role>
</Roles> </Roles>
<Properties> <Properties>
<Property name="organization" value="eitchnet.ch"/> <Property name="organization" value="eitchnet.ch"/>

View File

@ -23,4 +23,26 @@
</Roles> </Roles>
</User> </User>
<User userId="3" username="userC" password="cb69962946617da006a2f95776d78b49e5ec7941d2bdb2d25cdb05f957f64344" salt="61646d696e">
<Firstname>System User</Firstname>
<Lastname>Administrator</Lastname>
<State>ENABLED</State>
<Locale>en-GB</Locale>
<Groups>
<Group>GroupA1</Group>
<Group>GroupA2</Group>
</Groups>
</User>
<User userId="4" username="userD" password="cb69962946617da006a2f95776d78b49e5ec7941d2bdb2d25cdb05f957f64344" salt="61646d696e">
<Firstname>System User</Firstname>
<Lastname>Administrator</Lastname>
<State>ENABLED</State>
<Locale>en-GB</Locale>
<Groups>
<Group>GroupB1</Group>
<Group>GroupB2</Group>
</Groups>
</User>
</Users> </Users>

View File

@ -1,15 +1,5 @@
package li.strolch.execution; package li.strolch.execution;
import static java.util.Collections.emptyMap;
import static java.util.Collections.emptySet;
import static li.strolch.model.StrolchModelConstants.*;
import static li.strolch.runtime.StrolchConstants.SYSTEM_USER_AGENT;
import static li.strolch.utils.collections.SynchronizedCollections.synchronizedMapOfMaps;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Stream;
import li.strolch.agent.api.ComponentContainer; import li.strolch.agent.api.ComponentContainer;
import li.strolch.agent.api.ObserverEvent; import li.strolch.agent.api.ObserverEvent;
import li.strolch.agent.api.StrolchRealm; import li.strolch.agent.api.StrolchRealm;
@ -28,6 +18,16 @@ import li.strolch.privilege.model.Certificate;
import li.strolch.privilege.model.PrivilegeContext; import li.strolch.privilege.model.PrivilegeContext;
import li.strolch.utils.collections.MapOfMaps; import li.strolch.utils.collections.MapOfMaps;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Stream;
import static java.util.Collections.emptyMap;
import static java.util.Collections.emptySet;
import static li.strolch.model.StrolchModelConstants.*;
import static li.strolch.runtime.StrolchConstants.SYSTEM_USER_AGENT;
import static li.strolch.utils.collections.SynchronizedCollections.synchronizedMapOfMaps;
/** /**
* The event based execution handler waits for events in that the {@link ExecutionPolicy} implementations must call the * The event based execution handler waits for events in that the {@link ExecutionPolicy} implementations must call the
* relevant methods when the work is complete. Afterwards the next {@link Action} in the procedure is executed * relevant methods when the work is complete. Afterwards the next {@link Action} in the procedure is executed
@ -51,12 +51,12 @@ public class EventBasedExecutionHandler extends ExecutionHandler {
@Override @Override
public boolean isControlling(Activity activity) { public boolean isControlling(Activity activity) {
return this.controllers.containsElement(getDefaultRealm(), activity.getLocator()); return this.controllers.containsElement(getDefaultRealm(), activity.getRootElement().getLocator());
} }
@Override @Override
public boolean isControlling(String realm, Activity activity) { public boolean isControlling(String realm, Activity activity) {
return this.controllers.containsElement(realm, activity.getLocator()); return this.controllers.containsElement(realm, activity.getRootElement().getLocator());
} }
@Override @Override
@ -83,12 +83,12 @@ public class EventBasedExecutionHandler extends ExecutionHandler {
@Override @Override
public Controller getController(Activity activity) { public Controller getController(Activity activity) {
return getController(getDefaultRealm(), activity.getLocator()); return getController(getDefaultRealm(), activity.getRootElement().getLocator());
} }
@Override @Override
public Controller getController(String realm, Activity activity) { public Controller getController(String realm, Activity activity) {
return this.controllers.getElement(realm, activity.getLocator()); return this.controllers.getElement(realm, activity.getRootElement().getLocator());
} }
@Override @Override
@ -157,7 +157,7 @@ public class EventBasedExecutionHandler extends ExecutionHandler {
synchronized (this.controllers) { synchronized (this.controllers) {
Map<Locator, Controller> map = this.controllers.getMap(realm); Map<Locator, Controller> map = this.controllers.getMap(realm);
if (map == null) { if (map == null) {
logger.error("No controllers for realm " + realm); logger.info("No controllers for realm " + realm);
return; return;
} }
@ -171,26 +171,28 @@ public class EventBasedExecutionHandler extends ExecutionHandler {
} }
@Override @Override
public void addForExecution(Activity activity) { public Controller addForExecution(Activity activity) {
addForExecution(getDefaultRealm(), activity); return addForExecution(getDefaultRealm(), activity);
} }
@Override @Override
public void addForExecution(String realm, Activity activity) { public Controller addForExecution(String realm, Activity activity) {
ExecutionHandlerState state = this.statesByRealm.getOrDefault(realm, ExecutionHandlerState.Running); ExecutionHandlerState state = this.statesByRealm.getOrDefault(realm, ExecutionHandlerState.Running);
if (state == ExecutionHandlerState.HaltNew) if (state == ExecutionHandlerState.HaltNew)
throw new IllegalStateException( throw new IllegalStateException(
"ExecutionHandler state is " + state + ", can not add activities for execution!"); "ExecutionHandler state is " + state + ", can not add activities for execution!");
if (this.controllers.containsElement(realm, activity.getLocator())) Locator locator = activity.getRootElement().getLocator();
throw new IllegalStateException(activity.getLocator() + " is already registered for execution!"); if (this.controllers.containsElement(realm, locator))
throw new IllegalStateException(locator + " is already registered for execution!");
logger.info("Added " + activity.getLocator() + " @ " + realm); logger.info("Added " + locator + " @ " + realm);
Controller controller = newController(realm, activity); Controller controller = newController(realm, activity);
this.controllers.addElement(realm, activity.getLocator(), controller); this.controllers.addElement(realm, locator, controller);
notifyObserverAdd(controller); notifyObserverAdd(controller);
triggerExecution(realm); triggerExecution(realm);
return controller;
} }
@Override @Override
@ -205,10 +207,11 @@ public class EventBasedExecutionHandler extends ExecutionHandler {
throw new IllegalStateException( throw new IllegalStateException(
"ExecutionHandler state is " + state + ", can not add activities for execution!"); "ExecutionHandler state is " + state + ", can not add activities for execution!");
Controller controller = this.controllers.getElement(realm, activity.getLocator()); Locator locator = activity.getRootElement().getLocator();
Controller controller = this.controllers.getElement(realm, locator);
if (controller == null) { if (controller == null) {
controller = newController(realm, activity); controller = newController(realm, activity);
this.controllers.addElement(realm, activity.getLocator(), controller); this.controllers.addElement(realm, locator, controller);
notifyObserverAdd(controller); notifyObserverAdd(controller);
} }
@ -296,7 +299,8 @@ public class EventBasedExecutionHandler extends ExecutionHandler {
if (activity.isReadOnly()) if (activity.isReadOnly())
activity = activity.getClone(true); activity = activity.getClone(true);
logger.info("Restarting Execution of " + activity.getLocator() + " on realm " + realmName); Locator locator = activity.getRootElement().getLocator();
logger.info("Restarting Execution of " + locator + " on realm " + realmName);
// in execution actions need to be in state STOPPED to restart // in execution actions need to be in state STOPPED to restart
activity.findActionsDeep(a -> a.getState().inExecutionPhase()).forEach(a -> { activity.findActionsDeep(a -> a.getState().inExecutionPhase()).forEach(a -> {
@ -309,7 +313,7 @@ public class EventBasedExecutionHandler extends ExecutionHandler {
// register for execution // register for execution
Controller controller = newController(realmName, activity); Controller controller = newController(realmName, activity);
this.controllers.addElement(realmName, activity.getLocator(), controller); this.controllers.addElement(realmName, locator, controller);
}); });
// commit changes to state // commit changes to state

View File

@ -219,7 +219,7 @@ public abstract class ExecutionHandler extends StrolchComponent {
* @throws IllegalStateException * @throws IllegalStateException
* if the default realm is not set! * if the default realm is not set!
*/ */
public abstract void addForExecution(Activity activity); public abstract Controller addForExecution(Activity activity);
/** /**
* Registers the given {@link Activity} for execution. Execution is started when the concrete implementation deems * Registers the given {@link Activity} for execution. Execution is started when the concrete implementation deems
@ -230,7 +230,7 @@ public abstract class ExecutionHandler extends StrolchComponent {
* @param activity * @param activity
* the {@link Activity} * the {@link Activity}
*/ */
public abstract void addForExecution(String realm, Activity activity); public abstract Controller addForExecution(String realm, Activity activity);
/** /**
* Registers the given {@link Activity} for execution on the default realm, and submits it for execution immediately * Registers the given {@link Activity} for execution on the default realm, and submits it for execution immediately

View File

@ -20,8 +20,12 @@ import li.strolch.privilege.model.PrivilegeContext;
import li.strolch.runtime.StrolchConstants; import li.strolch.runtime.StrolchConstants;
import li.strolch.runtime.privilege.PrivilegedRunnable; import li.strolch.runtime.privilege.PrivilegedRunnable;
import li.strolch.runtime.privilege.PrivilegedRunnableWithResult; import li.strolch.runtime.privilege.PrivilegedRunnableWithResult;
import li.strolch.utils.time.PeriodDuration;
import java.time.Duration; import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -53,6 +57,8 @@ public abstract class ExecutionPolicy extends StrolchPolicy {
protected Locator resourceLoc; protected Locator resourceLoc;
protected Locator actionLoc; protected Locator actionLoc;
protected List<ScheduledFuture<?>> futures;
/** /**
* The TX for this execution policy. The TX needs to be updated when this execution policy has a longer life time * The TX for this execution policy. The TX needs to be updated when this execution policy has a longer life time
* than the actual TX * than the actual TX
@ -63,6 +69,7 @@ public abstract class ExecutionPolicy extends StrolchPolicy {
super(tx); super(tx);
this.tx = tx; this.tx = tx;
this.realm = tx.getRealmName(); this.realm = tx.getRealmName();
this.futures = new ArrayList<>();
} }
/** /**
@ -225,6 +232,7 @@ public abstract class ExecutionPolicy extends StrolchPolicy {
public void stop() { public void stop() {
this.stopped = true; this.stopped = true;
try { try {
this.futures.forEach(future -> future.cancel(false));
handleStopped(); handleStopped();
} catch (Exception e) { } catch (Exception e) {
logger.error("Stopping failed for " + this.actionLoc, e); logger.error("Stopping failed for " + this.actionLoc, e);
@ -246,6 +254,9 @@ public abstract class ExecutionPolicy extends StrolchPolicy {
* @param state the new state to set * @param state the new state to set
*/ */
protected void setActionState(Action action, State state) { protected void setActionState(Action action, State state) {
if (action.getState().inClosedPhase())
throw new IllegalStateException("Action " + action.getLocator() + " has state " + action.getState() +
" and can not be changed to " + state);
action.setState(state); action.setState(state);
@ -270,6 +281,17 @@ public abstract class ExecutionPolicy extends StrolchPolicy {
return action.findObjectivesParam(PARAM_DURATION, true); return action.findObjectivesParam(PARAM_DURATION, true);
} }
/**
* Delays the given {@link Runnable} by the given {@link PeriodDuration}
*
* @param duration the duration to delay
* @param runnable the action to delay
*/
public void delay(PeriodDuration duration, Runnable runnable) {
long delayMs = duration.toMillis();
this.futures.add(getDelayedExecutionTimer().delay(delayMs, runnable));
}
/** /**
* Delays the given {@link Runnable} by the given {@link Duration} * Delays the given {@link Runnable} by the given {@link Duration}
* *
@ -278,7 +300,7 @@ public abstract class ExecutionPolicy extends StrolchPolicy {
*/ */
public void delay(Duration duration, Runnable runnable) { public void delay(Duration duration, Runnable runnable) {
long delayMs = duration.toMillis(); long delayMs = duration.toMillis();
getDelayedExecutionTimer().delay(delayMs, runnable); this.futures.add(getDelayedExecutionTimer().delay(delayMs, runnable));
} }
/** /**
@ -321,7 +343,7 @@ public abstract class ExecutionPolicy extends StrolchPolicy {
delayMs = 20; delayMs = 20;
} }
logger.info("Delaying runnable " + runnable + " by " + formatMillisecondsDuration(delayMs)); logger.info("Delaying runnable " + runnable + " by " + formatMillisecondsDuration(delayMs));
getDelayedExecutionTimer().delay(delayMs, runnable); this.futures.add(getDelayedExecutionTimer().delay(delayMs, runnable));
} }
/** /**

View File

@ -92,7 +92,7 @@ public class SimpleExecution extends ExecutionPolicy {
protected void toWarning(LogMessage message) { protected void toWarning(LogMessage message) {
cancelWarningTask(); cancelWarningTask();
addMessage(message); addMessage(message);
getExecutionHandler().toWarning(this.realm, message.getLocator()); getExecutionHandler().toWarning(this.realm, this.actionLoc);
} }
protected void toExecuted() throws Exception { protected void toExecuted() throws Exception {
@ -126,7 +126,7 @@ public class SimpleExecution extends ExecutionPolicy {
stop(); stop();
logger.error("Action " + message.getLocator() + " failed because of: " + message.formatMessage()); logger.error("Action " + message.getLocator() + " failed because of: " + message.formatMessage());
addMessage(message); addMessage(message);
getExecutionHandler().toError(this.realm, message.getLocator()); getExecutionHandler().toError(this.realm, this.actionLoc);
} }
protected void setActionStateWithValueChange(Action action, State execution, double value) { protected void setActionStateWithValueChange(Action action, State execution, double value) {

Some files were not shown because too many files have changed in this diff Show More