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;
import static li.strolch.model.StrolchModelConstants.PolicyConstants.PARAM_ORDER;
import li.strolch.agent.api.ComponentContainer;
import li.strolch.agent.api.StrolchAgent;
import li.strolch.agent.api.StrolchComponent;
@ -27,6 +25,8 @@ import li.strolch.service.api.Command;
import org.slf4j.Logger;
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}
*
@ -103,6 +103,14 @@ public abstract class StrolchPolicy {
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) {
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 li.strolch.privilege.handler.PrivilegeHandler.PARAM_PERSIST_SESSIONS;
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 java.io.File;
@ -126,7 +126,7 @@ public class DefaultStrolchPrivilegeHandler extends StrolchComponent implements
if (containerModel.getPersistenceHandlerClassName().equals(XmlPersistenceHandler.class.getName())) {
Map<String, String> xmlParams = containerModel.getPersistenceHandlerParameterMap();
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);

View File

@ -9,7 +9,7 @@ import li.strolch.model.StrolchRootElement;
import li.strolch.privilege.base.AccessDeniedException;
import li.strolch.privilege.base.PrivilegeException;
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.Restrictable;
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
*
* @see li.strolch.privilege.policy.PrivilegePolicy#validateAction(PrivilegeContext, IPrivilege, Restrictable)
* @see li.strolch.privilege.policy.PrivilegePolicy#validateAction(PrivilegeContext, Privilege, Restrictable)
*/
@Override
public void validateAction(PrivilegeContext ctx, IPrivilege privilege, Restrictable restrictable)
public void validateAction(PrivilegeContext ctx, Privilege privilege, Restrictable restrictable)
throws AccessDeniedException {
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
*
* @see li.strolch.privilege.policy.PrivilegePolicy#validateAction(PrivilegeContext, IPrivilege, Restrictable)
* @see li.strolch.privilege.policy.PrivilegePolicy#validateAction(PrivilegeContext, Privilege, Restrictable)
*/
@Override
public boolean hasPrivilege(PrivilegeContext ctx, IPrivilege privilege, Restrictable restrictable)
public boolean hasPrivilege(PrivilegeContext ctx, Privilege privilege, Restrictable restrictable)
throws PrivilegeException {
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 {
preValidate(privilege, restrictable);

View File

@ -13,21 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package li.strolch.rest;
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;
package li.strolch.runtime.sessions;
import li.strolch.agent.api.ComponentContainer;
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.SimpleRestrictable;
import li.strolch.privilege.model.Usage;
import li.strolch.rest.model.UserSession;
import li.strolch.runtime.configuration.ComponentConfiguration;
import li.strolch.runtime.privilege.PrivilegeHandler;
import li.strolch.utils.dbc.DBC;
import org.slf4j.Logger;
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>
*/
@ -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_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 PrivilegeHandler privilegeHandler;
private final Map<String, Certificate> certificateMap;
private boolean reloadSessions;
private int sessionTtlMinutes;
private int maxKeepAliveMinutes;
@ -74,6 +70,29 @@ public class DefaultStrolchSessionHandler extends StrolchComponent implements St
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
public int getSessionMaxKeepAliveMinutes() {
return this.maxKeepAliveMinutes;
@ -89,30 +108,14 @@ public class DefaultStrolchSessionHandler extends StrolchComponent implements St
this.sessionTtlMinutes = configuration.getInt(PARAM_SESSION_TTL_MINUTES, 30);
this.maxKeepAliveMinutes = configuration.getInt(PARAM_SESSION_MAX_KEEP_ALIVE_MINUTES,
Math.max(this.sessionTtlMinutes, 30));
this.reloadSessions = configuration.getBoolean(PARAM_SESSION_RELOAD_SESSIONS, false);
super.initialize(configuration);
}
@Override
public void start() throws Exception {
this.privilegeHandler = getContainer().getComponent(PrivilegeHandler.class);
this.certificateMap.clear();
if (this.reloadSessions) {
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.");
}
refreshSessions();
this.validateSessionsTask = getScheduledExecutor("SessionHandler").scheduleWithFixedDelay(
this::checkSessionsForTimeout, 5, 1, TimeUnit.MINUTES);
@ -126,27 +129,6 @@ public class DefaultStrolchSessionHandler extends StrolchComponent implements St
if (this.validateSessionsTask != null)
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;
super.stop();
}
@ -321,8 +303,8 @@ public class DefaultStrolchSessionHandler extends StrolchComponent implements St
}
private void checkSessionsForTimeout() {
ZonedDateTime maxKeepAliveTime = ZonedDateTime.now().minus(this.maxKeepAliveMinutes, ChronoUnit.MINUTES);
ZonedDateTime timeOutTime = ZonedDateTime.now().minus(this.sessionTtlMinutes, ChronoUnit.MINUTES);
ZonedDateTime maxKeepAliveTime = ZonedDateTime.now().minusMinutes(this.maxKeepAliveMinutes);
ZonedDateTime timeOutTime = ZonedDateTime.now().minusMinutes(this.sessionTtlMinutes);
Map<String, Certificate> certificateMap = getCertificateMapCopy();
for (Certificate certificate : certificateMap.values()) {

View File

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package li.strolch.rest;
package li.strolch.runtime.sessions;
import java.util.List;
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.PrivilegeContext;
import li.strolch.privilege.model.Usage;
import li.strolch.rest.model.UserSession;
/**
* 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 {
/**
* Refreshes the sessions from the {@link li.strolch.runtime.privilege.PrivilegeHandler}
*/
void refreshSessions();
/**
* 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
* limitations under the License.
*/
package li.strolch.rest.model;
package li.strolch.runtime.sessions;
import java.time.ZonedDateTime;
import java.util.Locale;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,7 +1,5 @@
package li.strolch.model.json;
import java.util.*;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
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.UserState;
import java.util.*;
public class PrivilegeElementFromJsonVisitor {
public UserRep userRepFromJson(String string) {
@ -32,11 +32,12 @@ public class PrivilegeElementFromJsonVisitor {
String name = nameE == null ? null : nameE.getAsString().trim();
List<PrivilegeRep> privileges = new ArrayList<>();
Map<String, PrivilegeRep> privileges = new HashMap<>();
if (privilegesE != null) {
JsonArray privilegesArr = privilegesE.getAsJsonArray();
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 userStateE = jsonObject.get("userState");
JsonElement localeE = jsonObject.get("locale");
JsonElement groupsE = jsonObject.get("groups");
JsonElement rolesE = jsonObject.get("roles");
JsonElement propertiesE = jsonObject.get("properties");
@ -90,16 +92,11 @@ public class PrivilegeElementFromJsonVisitor {
String firstname = firstNameE == null ? null : firstNameE.getAsString().trim();
String lastname = lastNameE == null ? null : lastNameE.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;
if (rolesE != null) {
roles = new HashSet<>();
JsonArray rolesArr = rolesE.getAsJsonArray();
for (JsonElement role : rolesArr) {
roles.add(role.getAsString().trim());
}
}
Set<String> groups = jsonArrayToSet(groupsE);
Set<String> roles = jsonArrayToSet(rolesE);
Map<String, String> properties = 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());
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);
return jsonObject;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,8 +1,18 @@
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.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.PartialResultException;
import javax.naming.directory.*;
import java.util.Hashtable;
import java.util.Locale;
@ -10,21 +20,20 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.ScheduledExecutorService;
import li.strolch.privilege.base.AccessDeniedException;
import li.strolch.privilege.model.UserState;
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;
import static li.strolch.utils.helper.StringHelper.trimOrEmpty;
public abstract class BaseLdapPrivilegeHandler extends DefaultPrivilegeHandler {
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 searchBase;
private String additionalFilter;
private String domain;
private String domainPrefix;
@Override
public void initialize(ScheduledExecutorService executorService, Map<String, String> parameterMap,
@ -36,9 +45,24 @@ public abstract class BaseLdapPrivilegeHandler extends DefaultPrivilegeHandler {
userChallengeHandler, ssoHandler, policyMap);
this.providerUrl = parameterMap.get("providerUrl");
logger.info("providerUrl: " + this.providerUrl);
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
@ -49,52 +73,24 @@ public abstract class BaseLdapPrivilegeHandler extends DefaultPrivilegeHandler {
if (internalUser != null && internalUser.getUserState() != UserState.REMOTE)
return super.checkCredentialsAndUserState(username, password);
// Set up the environment for creating the initial context
Hashtable<String, String> env = new Hashtable<>();
String userPrincipalName;
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");
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);
logger.info("User {} tries to login on ldap {}", username, this.providerUrl);
// Create the initial context
DirContext ctx = null;
try {
ctx = new InitialDirContext(env);
//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");
ctx = new InitialDirContext(buildLdapEnv(password, userPrincipalName));
SearchResult searchResult = searchLdap(username, ctx, userPrincipalName);
User user = buildUserFromSearchResult(username, searchResult);
// persist this user
@ -104,13 +100,15 @@ public abstract class BaseLdapPrivilegeHandler extends DefaultPrivilegeHandler {
this.persistenceHandler.replaceUser(user);
if (this.autoPersistOnUserChangesData)
this.persistenceHandler.persist();
persistModelAsync();
return user;
} catch (AccessDeniedException e) {
throw e;
} catch (Exception e) {
logger.error("Could not login with user: " + username + this.domain + " on Ldap", e);
throw new AccessDeniedException("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 + " on Ldap", e);
} finally {
if (ctx != null) {
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 {
Attributes attrs = sr.getAttributes();
@ -135,19 +192,20 @@ public abstract class BaseLdapPrivilegeHandler extends DefaultPrivilegeHandler {
Set<String> ldapGroups = getLdapGroups(username, attrs);
logger.info("User " + username + " is member of the following LDAP groups: ");
ldapGroups.forEach(s -> logger.info("- " + s));
Set<String> strolchGroups = mapToStrolchGroups(username, ldapGroups);
Set<String> strolchRoles = mapToStrolchRoles(username, ldapGroups);
Map<String, String> properties = buildProperties(username, attrs, ldapGroups, strolchRoles);
return new User(username, username, null, firstName, lastName, UserState.REMOTE, strolchRoles, locale,
properties, false, new UserHistory());
return new User(username, username, null, firstName, lastName, UserState.REMOTE, strolchGroups, strolchRoles,
locale, properties, false, UserHistory.EMPTY);
}
protected abstract Map<String, String> buildProperties(String username, Attributes attrs, Set<String> ldapGroups,
Set<String> strolchRoles) throws Exception;
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()))
throw new AccessDeniedException(
"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> mapToStrolchGroups(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;
if (this.maxLength < 100)
description = MessageFormat
.format(getString(locale, "Privilege.passwordLengthBetween"), this.minLength, this.maxLength);
description = MessageFormat.format(getString(locale, "Privilege.passwordLengthBetween"), this.minLength,
this.maxLength);
else
description = MessageFormat.format(getString(locale, "Privilege.passwordLengthAtLeast"), this.minLength);

View File

@ -44,7 +44,7 @@ import static li.strolch.privilege.helper.XmlConstants.*;
* <p>
* Required parameters:
* <ul>
* <li>{@link XmlConstants#XML_PARAM_HASH_ALGORITHM}</li>
* <li>{@link XmlConstants#PARAM_HASH_ALGORITHM}</li>
* </ul>
*
* @author Robert von Burg <eitch@eitchnet.ch>
@ -121,7 +121,7 @@ public class DefaultEncryptionHandler implements EncryptionHandler {
try {
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) {
throw new PrivilegeException(MessageFormat.format("Algorithm {0} was not found!", nonSaltAlgorithm),
@ -151,9 +151,8 @@ public class DefaultEncryptionHandler implements EncryptionHandler {
@Override
public boolean isPasswordCryptOutdated(PasswordCrypt passwordCrypt) {
return passwordCrypt.getSalt() == null || passwordCrypt.getHashAlgorithm() == null ||
passwordCrypt.getHashIterations() != this.iterations ||
passwordCrypt.getHashKeyLength() != this.keyLength;
return passwordCrypt.salt() == null || passwordCrypt.hashAlgorithm() == null ||
passwordCrypt.hashIterations() != this.iterations || passwordCrypt.hashKeyLength() != this.keyLength;
}
@Override
@ -163,13 +162,13 @@ public class DefaultEncryptionHandler implements EncryptionHandler {
this.secureRandom = new SecureRandom();
// get hash algorithm parameters
this.algorithm = parameterMap.getOrDefault(XML_PARAM_HASH_ALGORITHM, DEFAULT_ALGORITHM);
this.nonSaltAlgorithm = parameterMap.getOrDefault(XML_PARAM_HASH_ALGORITHM_NON_SALT,
this.algorithm = parameterMap.getOrDefault(PARAM_HASH_ALGORITHM, DEFAULT_ALGORITHM);
this.nonSaltAlgorithm = parameterMap.getOrDefault(PARAM_HASH_ALGORITHM_NON_SALT,
DEFAULT_ALGORITHM_NON_SALT);
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(
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
try {
@ -178,7 +177,7 @@ public class DefaultEncryptionHandler implements EncryptionHandler {
MessageFormat.format("Using non-salt hashing algorithm {0}", this.nonSaltAlgorithm));
} catch (Exception e) {
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());
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));
} catch (Exception e) {
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());
throw new PrivilegeException(msg, e);
}

View File

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

View File

@ -16,9 +16,9 @@ public class MailUserChallengeHandler extends UserChallengeHandler {
String subject = "Mail TAN";
String text = "Hello " + user.getFirstname() + " " + user.getLastname() + "\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;
String text = "Hello " + user.getFirstname() + " " + user.getLastname() + "\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;
String recipient = user.getEmail();
if (StringHelper.isEmpty(recipient)) {
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
* concrete {@link PasswordStrengthHandler} might need
*
* @param parameterMap
* a map containing configuration properties
* @param parameterMap a map containing configuration properties
*/
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
* implementation
*
* @param locale the locale in which to return the description
*
* @return a description of a strong password
* @param locale
*/
String getDescription(Locale locale);
/**
* Performs the validation of the given password
*
* @param password
* the password to validate
* @param password the password to validate
*
* @return true if the password meets the criteria for a strong password
*/

View File

@ -15,15 +15,18 @@
*/
package li.strolch.privilege.handler;
import java.util.List;
import java.util.Map;
import li.strolch.privilege.model.IPrivilege;
import li.strolch.privilege.model.Privilege;
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.User;
import li.strolch.privilege.policy.PrivilegePolicy;
import javax.xml.stream.XMLStreamException;
import java.io.IOException;
import java.util.List;
import java.util.Map;
/**
* <p>
* 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>
* 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}
* and {@link IPrivilege}
* and {@link Privilege}
* </p>
*
* @author Robert von Burg <eitch@eitchnet.ch>
@ -47,6 +50,13 @@ public interface PersistenceHandler {
*/
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
*
@ -57,18 +67,25 @@ public interface PersistenceHandler {
/**
* Returns a {@link User} object from the underlying database
*
* @param username
* the name/id of the {@link User} object to return
* @param username the name/id of the {@link User} object to return
*
* @return the {@link User} object, or null if it was not found
*/
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
*
* @param roleName
* the name/id of the {@link Role} object to return
* @param roleName the name/id of the {@link Role} object to return
*
* @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
*
* @param username
* the name of the {@link User} to remove
* @param username the name of the {@link User} to remove
*
* @return the {@link User} removed, or null if it did not exist
*/
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
*
* @param roleName
* the name of the {@link Role} to remove
* @param roleName the name of the {@link Role} to remove
*
* @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
*
* @param user
* the {@link User} object to add
* @param user the {@link User} object to add
*/
void addUser(User user);
/**
* Replaces the existing {@link User} object in the underlying database
*
* @param user
* the {@link User} object to add
* @param user the {@link User} object to add
*/
void replaceUser(User user);
/**
* Adds a {@link Role} object to the underlying database
*
* @param role
* the {@link User} object to add
* @param role the {@link Role} object to add
*/
void addRole(Role role);
/**
* Replaces the {@link Role} object in the underlying database
*
* @param role
* the {@link User} object to add
* @param role the {@link User} object to add
*/
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
*
* @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
@ -144,8 +178,7 @@ public interface PersistenceHandler {
* Initialize the concrete {@link PersistenceHandler}. The passed parameter map contains any configuration the
* concrete {@link PersistenceHandler} might need
*
* @param parameterMap
* a map containing configuration properties
* @param parameterMap a map containing configuration properties
*/
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;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import li.strolch.privilege.base.*;
import li.strolch.privilege.model.*;
import li.strolch.privilege.model.internal.Role;
import li.strolch.privilege.model.internal.User;
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
* 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
*
* @param certificate
* 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 certificate the {@link Certificate} of the user which has the privilege to perform this action
* @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
*/
@ -224,10 +222,8 @@ public interface PrivilegeHandler {
/**
* Returns a {@link RoleRep} for the given roleName
*
* @param certificate
* 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 certificate the {@link Certificate} of the user which has the privilege to perform this action
* @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
*/
@ -236,8 +232,7 @@ public interface PrivilegeHandler {
/**
* Returns the map of {@link PrivilegePolicy} definitions
*
* @param certificate
* the {@link Certificate} of the user which has the privilege to perform this action
* @param certificate the {@link Certificate} of the user which has the privilege to perform this action
*
* @return the map of {@link PrivilegePolicy} definitions
*/
@ -246,8 +241,7 @@ public interface PrivilegeHandler {
/**
* Returns the list of {@link Certificate Certificates}
*
* @param certificate
* the {@link Certificate} of the user which has the privilege to perform this action
* @param certificate the {@link Certificate} of the user which has the privilege to perform this action
*
* @return the list of {@link Certificate Certificates}
*/
@ -256,8 +250,7 @@ public interface PrivilegeHandler {
/**
* Returns all {@link RoleRep RoleReps}
*
* @param certificate
* the {@link Certificate} of the user which has the privilege to perform this action
* @param certificate the {@link Certificate} of the user which has the privilege to perform this action
*
* @return the list of {@link RoleRep RoleReps}
*/
@ -266,8 +259,7 @@ public interface PrivilegeHandler {
/**
* Returns all {@link UserRep UserReps}
*
* @param certificate
* the {@link Certificate} of the user which has the privilege to perform this action
* @param certificate the {@link Certificate} of the user which has the privilege to perform this action
*
* @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
* fields are irrelevant.
*
* @param certificate
* 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 certificate the {@link Certificate} of the user which has the privilege to perform this action
* @param selectorRep the {@link UserRep} to use as criteria selection
*
* @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
*
* @param certificate
* the {@link Certificate} of the user which has the privilege to perform this action
* @param username
* the username of the user to remove
* @param certificate the {@link Certificate} of the user which has the privilege to perform this action
* @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
*
* @throws AccessDeniedException
* if the user for this certificate may not perform the action
* @throws PrivilegeException
* if there is anything wrong with this certificate
* @throws AccessDeniedException if the user for this certificate may not perform the action
* @throws PrivilegeException if there is anything wrong with this certificate
*/
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
*
* @param certificate
* the {@link Certificate} of the user which has the privilege to perform this action
* @param roleName
* the roleName of the role to remove
* @param certificate the {@link Certificate} of the user which has the privilege to perform this action
* @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
*
* @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 is still in use by a 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 the role is still in use by a
* user
*/
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>
* 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[])}
* </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 create the new {@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[])}
* @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 create the new {@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 the user already exists
* @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 user already exists
*/
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,
* otherwise the user is created without a password
*
* @param certificate
* 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 certificate the {@link Certificate} of the user which has the privilege to perform this action
* @param userReps the list of users to add or update
*/
void addOrUpdateUsers(Certificate certificate, List<UserRep> userReps) throws PrivilegeException;
@ -410,108 +352,39 @@ public interface PrivilegeHandler {
* Any other fields will be ignored
* </p>
*
* @param certificate
* 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 certificate 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 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
* @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 updateUser(Certificate certificate, UserRep userRep) 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;
UserRep updateUser(Certificate certificate, UserRep userRep, char[] password) throws PrivilegeException;
/**
* Adds a new role with the information from this {@link RoleRep}
*
* @param certificate
* 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 certificate 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}
*
* @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 already exists
* @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 already exists
*/
RoleRep addRole(Certificate certificate, RoleRep roleRep) throws PrivilegeException;
/**
* Replaces the existing role with the information from this {@link RoleRep}
*
* @param certificate
* 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 certificate 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}
*
* @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
* @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
*/
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>
* 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
* </p>
*
* @param certificate
* 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 password
* 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[])}
* @param certificate 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 password 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
* if the user for this certificate may not perform the action
* @throws PrivilegeException
* if there is anything wrong with this certificate
* @throws AccessDeniedException if the user for this certificate may not perform the action
* @throws PrivilegeException if there is anything wrong with this certificate
*/
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
* </p>
*
* @param certificate
* 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 certificate 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
*
* @throws AccessDeniedException
* if the user for this certificate may not perform the action
* @throws PrivilegeException
* if there is anything wrong with this certificate
* @throws AccessDeniedException if the user for this certificate may not perform the action
* @throws PrivilegeException if there is anything wrong with this certificate
*/
void requirePasswordChange(Certificate certificate, String username) throws PrivilegeException;
/**
* Changes the {@link UserState} of the user
*
* @param certificate
* 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 state
* the new state for the user
* @param certificate 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 state the new state for 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
* @throws AccessDeniedException if the user for this certificate may not perform the action
* @throws PrivilegeException if there is anything wrong with this certificate
*/
UserRep setUserState(Certificate certificate, String username, UserState state) throws PrivilegeException;
/**
* Changes the {@link Locale} of the user
*
* @param certificate
* 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 locale
* the new {@link Locale} for the user
* @param certificate 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 locale the new {@link Locale} for 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
* @throws AccessDeniedException if the user for this certificate may not perform the action
* @throws PrivilegeException if there is anything wrong with this certificate
*/
UserRep setUserLocale(Certificate certificate, String username, Locale locale) throws PrivilegeException;
/**
* Initiate a password reset challenge for the given username
*
* @param usage
* the usage for which the challenge is requested
* @param username
* the username of the user to initiate the challenge for
* @param usage the usage for which the challenge is requested
* @param username the username of the user to initiate the challenge for
*/
void initiateChallengeFor(Usage usage, String username);
/**
* Initiate a password reset challenge for the given username
*
* @param usage
* the usage for which the challenge is requested
* @param username
* the username of the user to initiate the challenge for
* @param source
* the source of the challenge
* @param usage the usage for which the challenge is requested
* @param username 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);
/**
* Validate the response of a challenge for the given username
*
* @param username
* the username of the user for which the challenge is to be validated
* @param challenge
* the challenge from the user
* @param username the username of the user for which the challenge is to be validated
* @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
* initiated challenge
*
* @throws PrivilegeException
* if anything goes wrong
* @throws PrivilegeException if anything goes wrong
*/
Certificate validateChallenge(String username, String challenge) throws PrivilegeException;
/**
* Validate the response of a challenge for the given username
*
* @param username
* the username of the user for which the challenge is to be validated
* @param challenge
* the challenge from the user
* @param source
* the source of the challenge validation
* @param username the username of the user for which the challenge is to be validated
* @param challenge 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
* initiated challenge
*
* @throws PrivilegeException
* if anything goes wrong
* @throws PrivilegeException if anything goes wrong
*/
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
* a {@link Certificate} with which this user may then perform actions
*
* @param username
* 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 they must meet
* the requirements of the {@link #validatePassword(Locale, char[])}-method
* @param keepAlive
* should this session be kept alive
* @param username 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
* they must meet 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
*
* @throws AccessDeniedException
* if the user credentials are not valid
* @throws AccessDeniedException if the user credentials are not valid
*/
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
* a {@link Certificate} with which this user may then perform actions
*
* @param username
* 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 they must meet
* the requirements of the {@link #validatePassword(Locale, char[])}-method
* @param source
* 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
* @param username 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
* they must meet the requirements of the {@link #validatePassword(Locale, char[])}-method
* @param source 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
*
* @throws AccessDeniedException
* if the user credentials are not valid
* @throws AccessDeniedException if the user credentials are not valid
*/
Certificate authenticate(String username, char[] password, String source, Usage usage, boolean keepAlive)
throws AccessDeniedException;
@ -692,47 +524,37 @@ public interface PrivilegeHandler {
/**
* Authenticates a user on a remote Single Sign On service. This is implemented by the
*
* @param data
* the data to perform the SSO
* @param keepAlive
* should this session be kept alive
* @param data the data to perform the SSO
* @param keepAlive should this session be kept alive
*
* @return the {@link Certificate} for the user
*
* @throws PrivilegeException
* if something goes wrong with the SSO
* @throws PrivilegeException if something goes wrong with the SSO
*/
Certificate authenticateSingleSignOn(Object data, boolean keepAlive) throws PrivilegeException;
/**
* Authenticates a user on a remote Single Sign On service. This is implemented by the
*
* @param data
* the data to perform the SSO
* @param source
* the source of the SSO authentication
* @param keepAlive
* may the certificate be kept alive
* @param data the data to perform the SSO
* @param source the source of the SSO authentication
* @param keepAlive may the certificate be kept alive
*
* @return the {@link Certificate} for the user
*
* @throws PrivilegeException
* if something goes wrong with the SSO
* @throws PrivilegeException if something goes wrong with the SSO
*/
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
*
* @param certificate
* the certificate for which to perform a refresh
* @param source
* the source of the refresh request
* @param certificate the certificate for which to perform a refresh
* @param source the source of the refresh request
*
* @return a {@link Certificate} with which this user may then perform actions
*
* @throws AccessDeniedException
* if the certificate is now valid, or refreshing is not allowed
* @throws AccessDeniedException if the certificate is now valid, or refreshing is not allowed
*/
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
* with the credentials associated to the given {@link Certificate}
*
* @param certificate
* the {@link Certificate} for which the session is to be invalidated
* @param certificate 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
*/
@ -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
* actions and do not need to access the {@link PrivilegeHandler} anymore
*
* @param certificate
* the {@link Certificate} to check
* @param certificate the {@link Certificate} to check
*
* @return the {@link PrivilegeContext} for the given {@link Certificate}
*
* @throws PrivilegeException
* if there is anything wrong with this certificate
* @throws NotAuthenticatedException
* if the certificate has expired
* @throws PrivilegeException if there is anything wrong with this certificate
* @throws NotAuthenticatedException if the certificate has expired
*/
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}
* has been tampered with
*
* @param ctx
* the {@link PrivilegeContext} to check
* @param ctx the {@link PrivilegeContext} to check
*
* @throws PrivilegeException
* if there is anything wrong with this privilege context
* @throws NotAuthenticatedException
* if the privilege context has expired
* @throws PrivilegeException if there is anything wrong with this privilege context
* @throws NotAuthenticatedException if the privilege context has expired
*/
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
* actions and do not need to access the {@link PrivilegeHandler} anymore
*
* @param certificate
* the {@link Certificate} to check
* @param source
* the source, e.g. remote IP for this validation request
* @param certificate the {@link Certificate} to check
* @param source the source, e.g. remote IP for this validation request
*
* @return the {@link PrivilegeContext} for the given {@link Certificate}
*
* @throws PrivilegeException
* if there is anything wrong with this certificate
* @throws NotAuthenticatedException
* if the certificate has expired
* @throws PrivilegeException if there is anything wrong with this certificate
* @throws NotAuthenticatedException if the certificate has expired
*/
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
*
* @param certificate
* the {@link Certificate} of the user which has the privilege to perform this action
* @param source
* the source of the request
* @param certificate the {@link Certificate} of the user which has the privilege to perform this action
* @param source the source of the request
*
* @return true if the reload was successful, false if something went wrong
*
* @throws AccessDeniedException
* if the users of the given certificate does not have the privilege to perform this action
* @throws AccessDeniedException if the users of the given certificate does not have the privilege to perform this
* action
*/
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
* actively performed
*
* @param certificate
* the {@link Certificate} of the user which has the privilege to perform this action
* @param certificate 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
*
* @throws AccessDeniedException
* if the users of the given certificate does not have the privilege to perform this action
* @throws AccessDeniedException if the users of the given certificate does not have the privilege to perform this
* action
*/
boolean persist(Certificate certificate) throws AccessDeniedException;
/**
* Persists all currently active sessions
*
* @param certificate
* the {@link Certificate} of the user which has the privilege to perform this action
* @param source
* the source of the request
* @param certificate the {@link Certificate} of the user which has the privilege to perform this action
* @param source the source of the request
*
* @return true if changes were persisted, false if not (i.e. not enabled)
*
* @throws AccessDeniedException
* if the users of the given certificate does not have the privilege to perform this action
* @throws AccessDeniedException if the users of the given certificate does not have the privilege to perform this
* action
*/
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
* of the given {@link SystemAction} instance
*
* @param systemUsername
* the username of the system user to perform the action as
* @param action
* the action to be performed as the system user
* @param systemUsername the username of the system user to perform the action as
* @param action the action to be performed as the system user
*
* @throws PrivilegeException
* if the user does not exist, or the system action is not allowed
* @throws Exception
* if anything else goes wrong during execution
* @throws PrivilegeException if the user does not exist, or the system action is not allowed
* @throws Exception if anything else goes wrong during execution
*/
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
* of the given {@link SystemAction} instance
*
* @param systemUsername
* the username of the system user to perform the action as
* @param action
* the action to be performed as the system user
* @param systemUsername the username of the system user to perform the action as
* @param action the action to be performed as the system user
*
* @return the action
*
* @throws PrivilegeException
* if the user does not exist, or the system action is not allowed
* @throws Exception
* if anything else goes wrong during execution
* @throws PrivilegeException if the user does not exist, or the system action is not allowed
* @throws Exception if anything else goes wrong during execution
*/
<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} is resource intensive e.g. on low power devices.
*
* @param systemUsername
* the username of the system user to perform the action as
* @param systemUsername the username of the system user to perform the action as
*
* @return the action
*
* @throws PrivilegeException
* if the user does not exist, or the system action is not allowed
* @throws PrivilegeException if the user does not exist, or the system action is not allowed
*/
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;
import static java.text.MessageFormat.format;
import static li.strolch.privilege.handler.PrivilegeHandler.PARAM_CASE_INSENSITIVE_USERNAME;
import static li.strolch.privilege.helper.XmlConstants.*;
import static li.strolch.utils.helper.StringHelper.formatNanoDuration;
import li.strolch.privilege.base.PrivilegeException;
import li.strolch.privilege.helper.XmlConstants;
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.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.IOException;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import li.strolch.privilege.base.PrivilegeException;
import li.strolch.privilege.helper.XmlConstants;
import li.strolch.privilege.model.internal.Role;
import li.strolch.privilege.model.internal.User;
import li.strolch.privilege.xml.PrivilegeRolesDomWriter;
import li.strolch.privilege.xml.PrivilegeRolesSaxReader;
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;
import static java.lang.Boolean.parseBoolean;
import static java.text.MessageFormat.format;
import static li.strolch.privilege.handler.PrivilegeHandler.PARAM_CASE_INSENSITIVE_USERNAME;
import static li.strolch.privilege.helper.XmlConstants.*;
import static li.strolch.utils.helper.StringHelper.formatNanoDuration;
import static li.strolch.utils.helper.StringHelper.isEmpty;
/**
* {@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);
private final Map<String, User> userMap;
private final Map<String, Group> groupMap;
private final Map<String, Role> roleMap;
private boolean userMapDirty;
private boolean groupMapDirty;
private boolean roleMapDirty;
private Map<String, String> parameterMap;
private File usersPath;
private File groupsPath;
private File rolesPath;
private boolean caseInsensitiveUsername;
public XmlPersistenceHandler() {
this.roleMap = new ConcurrentHashMap<>();
this.groupMap = 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
public List<Role> getAllRoles() {
synchronized (this.roleMap) {
@ -91,6 +104,11 @@ public class XmlPersistenceHandler implements PersistenceHandler {
return this.userMap.get(this.caseInsensitiveUsername ? username.toLowerCase() : username);
}
@Override
public Group getGroup(String groupName) {
return this.groupMap.get(groupName);
}
@Override
public Role getRole(String roleName) {
return this.roleMap.get(roleName);
@ -103,6 +121,13 @@ public class XmlPersistenceHandler implements PersistenceHandler {
return user;
}
@Override
public Group removeGroup(String groupName) {
Group group = this.groupMap.remove(groupName);
this.groupMapDirty = group != null;
return group;
}
@Override
public Role removeRole(String roleName) {
Role role = this.roleMap.remove(roleName);
@ -129,6 +154,23 @@ public class XmlPersistenceHandler implements PersistenceHandler {
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
public void addRole(Role role) {
if (this.roleMap.containsKey(role.getName()))
@ -149,9 +191,10 @@ public class XmlPersistenceHandler implements PersistenceHandler {
/**
* Initializes this {@link XmlPersistenceHandler} by reading the following parameters:
* <ul>
* <li>{@link XmlConstants#XML_PARAM_BASE_PATH}</li>
* <li>{@link XmlConstants#XML_PARAM_USERS_FILE}</li>
* <li>{@link XmlConstants#XML_PARAM_ROLES_FILE}</li>
* <li>{@link XmlConstants#PARAM_BASE_PATH}</li>
* <li>{@link XmlConstants#PARAM_USERS_FILE}</li>
* <li>{@link XmlConstants#PARAM_GROUPS_FILE}</li>
* <li>{@link XmlConstants#PARAM_ROLES_FILE}</li>
* </ul>
*/
@Override
@ -159,58 +202,50 @@ public class XmlPersistenceHandler implements PersistenceHandler {
this.parameterMap = Map.copyOf(paramsMap);
// 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);
if (!basePathF.exists() && !basePathF.isDirectory()) {
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);
}
// get users file name
String usersFileName = this.parameterMap.get(XML_PARAM_USERS_FILE);
if (StringHelper.isEmpty(usersFileName)) {
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);
}
File usersPath = getFile(basePath, PARAM_USERS_FILE, PARAM_USERS_FILE_DEF, true);
File groupsPath = getFile(basePath, PARAM_GROUPS_FILE, PARAM_GROUPS_FILE_DEF, false);
File rolesPath = getFile(basePath, PARAM_ROLES_FILE, PARAM_ROLES_FILE_DEF, true);
// save path to model
this.usersPath = usersPath;
this.groupsPath = groupsPath;
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())
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
* the while calling {@link #initialize(Map)}
@ -224,6 +259,10 @@ public class XmlPersistenceHandler implements PersistenceHandler {
PrivilegeUsersSaxReader usersXmlHandler = new PrivilegeUsersSaxReader(this.caseInsensitiveUsername);
XmlHelper.parseDocument(this.usersPath, usersXmlHandler);
PrivilegeGroupsSaxReader groupsXmlHandler = new PrivilegeGroupsSaxReader();
if (this.groupsPath.exists())
XmlHelper.parseDocument(this.groupsPath, groupsXmlHandler);
PrivilegeRolesSaxReader rolesXmlHandler = new PrivilegeRolesSaxReader();
XmlHelper.parseDocument(this.rolesPath, rolesXmlHandler);
@ -233,6 +272,12 @@ public class XmlPersistenceHandler implements PersistenceHandler {
this.roleMap.putAll(rolesXmlHandler.getRoles());
}
// GROUPS
synchronized (this.groupMap) {
this.groupMap.clear();
this.groupMap.putAll(groupsXmlHandler.getGroups());
}
// USERS
synchronized (this.userMap) {
this.userMap.clear();
@ -240,21 +285,40 @@ public class XmlPersistenceHandler implements PersistenceHandler {
}
this.userMapDirty = false;
this.groupMapDirty = false;
this.roleMapDirty = false;
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()));
// validate referenced roles exist
// validate referenced elements exist
for (User user : this.userMap.values()) {
for (String roleName : user.getRoles()) {
// validate that role exists
if (getRole(roleName) == null) {
logger.error(
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;
@ -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)}
*/
@Override
public boolean persist() {
public boolean persist() throws XMLStreamException, IOException {
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;
// get users file
// write users file
if (this.userMapDirty) {
// delegate writing
PrivilegeUsersDomWriter modelWriter = new PrivilegeUsersDomWriter(getAllUsers(), this.usersPath);
modelWriter.write();
new PrivilegeUsersSaxWriter(getAllUsers(), this.usersPath).write();
this.userMapDirty = false;
saved = true;
}
// get roles file
if (this.roleMapDirty) {
// delegate writing
PrivilegeRolesDomWriter modelWriter = new PrivilegeRolesDomWriter(getAllRoles(), this.rolesPath);
modelWriter.write();
// write groups file
if (this.groupMapDirty) {
new PrivilegeGroupsSaxWriter(getAllGroups(), this.groupsPath).write();
this.groupMapDirty = false;
saved = true;
}
// write roles file
if (this.roleMapDirty) {
new PrivilegeRolesSaxWriter(getAllRoles(), this.rolesPath).write();
this.roleMapDirty = false;
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;
}
}

View File

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

View File

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

View File

@ -23,298 +23,69 @@ package li.strolch.privilege.helper;
@SuppressWarnings("nls")
public class XmlConstants {
/**
* XML_ROOT_PRIVILEGE_CONTAINER = "PrivilegeContainer" :
*/
public static final String XML_ROOT_PRIVILEGE = "Privilege";
/**
* XML_CONTAINER = "Container" :
*/
public static final String XML_CONTAINER = "Container";
/**
* XML_POLICIES = "Policies" :
*/
public static final String XML_POLICIES = "Policies";
/**
* XML_PRIVILEGES = "Privileges" :
*/
public static final String XML_PRIVILEGES = "Privileges";
/**
* XML_ROOT_PRIVILEGE_USERS_AND_ROLES = "UsersAndRoles" :
*/
public static final String XML_ROOT_PRIVILEGE_USERS_AND_ROLES = "UsersAndRoles";
/**
* XML_ROOT_CERTIFICATES = "Certificates" :
*/
public static final String XML_ROOT_CERTIFICATES = "Certificates";
/**
* XML_HANDLER_USER_CHALLENGE = "UserChallengeHandler" :
*/
public static final String XML_HANDLER_USER_CHALLENGE = "UserChallengeHandler";
/**
* XML_HANDLER_PERSISTENCE = "PersistenceHandler" :
*/
public static final String XML_HANDLER_PERSISTENCE = "PersistenceHandler";
/**
* XML_HANDLER_ENCRYPTION = "EncryptionHandler" :
*/
public static final String XML_HANDLER_ENCRYPTION = "EncryptionHandler";
/**
* XML_HANDLER_ENCRYPTION = "PasswordStrengthHandler" :
*/
public static final String XML_HANDLER_PASSWORD_STRENGTH = "PasswordStrengthHandler";
/**
* XML_HANDLER_ENCRYPTION = "SsoHandler" :
*/
public static final String XML_HANDLER_SSO = "SsoHandler";
/**
* XML_HANDLER_PRIVILEGE = "PrivilegeHandler" :
*/
public static final String XML_HANDLER_PRIVILEGE = "PrivilegeHandler";
/**
* XML_ROLES = "Roles" :
*/
public static final String XML_ROLES = "Roles";
/**
* 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";
public static final String ROOT_PRIVILEGE = "Privilege";
public static final String CONTAINER = "Container";
public static final String POLICIES = "Policies";
public static final String PRIVILEGES = "Privileges";
public static final String ROOT_PRIVILEGE_USERS_AND_ROLES = "UsersAndRoles";
public static final String ROOT_CERTIFICATES = "Certificates";
public static final String HANDLER_USER_CHALLENGE = "UserChallengeHandler";
public static final String HANDLER_PERSISTENCE = "PersistenceHandler";
public static final String HANDLER_ENCRYPTION = "EncryptionHandler";
public static final String HANDLER_PASSWORD_STRENGTH = "PasswordStrengthHandler";
public static final String HANDLER_SSO = "SsoHandler";
public static final String HANDLER_PRIVILEGE = "PrivilegeHandler";
public static final String ROLES = "Roles";
public static final String ROLE = "Role";
public static final String USERS = "Users";
public static final String GROUPS = "Groups";
public static final String GROUP = "Group";
public static final String CERTIFICATE = "Certificate";
public static final String SESSION_DATA = "SessionData";
public static final String USER = "User";
public static final String HISTORY = "History";
public static final String FIRST_LOGIN = "FirstLogin";
public static final String LAST_LOGIN = "LastLogin";
public static final String LAST_PASSWORD_CHANGE = "LastPasswordChange";
public static final String PASSWORD_CHANGE_REQUESTED = "PasswordChangeRequested";
public static final String PRIVILEGE = "Privilege";
public static final String POLICY = "Policy";
public static final String PARAMETERS = "Parameters";
public static final String PARAMETER = "Parameter";
public static final String PROPERTIES = "Properties";
public static final String PROPERTY = "Property";
public static final String ALL_ALLOWED = "AllAllowed";
public static final String DENY = "Deny";
public static final String ALLOW = "Allow";
public static final String FIRSTNAME = "Firstname";
public static final String LASTNAME = "Lastname";
public static final String STATE = "State";
public static final String LOCALE = "Locale";
public static final String ATTR_CLASS = "class";
public static final String ATTR_LOGIN_TIME = "loginTime";
public static final String ATTR_KEEP_ALIVE = "keepAlive";
public static final String ATTR_LAST_ACCESS = "lastAccess";
public static final String ATTR_NAME = "name";
public static final String ATTR_VALUE = "value";
public static final String ATTR_POLICY = "policy";
public static final String ATTR_USER_ID = "userId";
public static final String ATTR_SESSION_ID = "sessionId";
public static final String ATTR_USAGE = "usage";
public static final String ATTR_USERNAME = "username";
public static final String ATTR_AUTH_TOKEN = "authToken";
public static final String ATTR_SOURCE = "source";
public static final String ATTR_LOCALE = "locale";
public static final String ATTR_PASSWORD = "password";
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";
public static final String PARAM_HASH_ITERATIONS = "hashIterations";
public static final String PARAM_HASH_KEY_LENGTH = "hashKeyLength";
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";
public static final String PARAM_GROUPS_FILE_DEF = "PrivilegeGroups.xml";
public static final String PARAM_ROLES_FILE = "rolesXmlFile";
public static final String PARAM_ROLES_FILE_DEF = "PrivilegeRoles.xml";
public static final String 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;
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.util.Collections;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import li.strolch.privilege.base.PrivilegeConstants;
import li.strolch.privilege.base.PrivilegeException;
import li.strolch.privilege.handler.PrivilegeHandler;
import li.strolch.privilege.model.internal.User;
import li.strolch.utils.helper.StringHelper;
import static li.strolch.privilege.base.PrivilegeConstants.*;
import static li.strolch.utils.helper.StringHelper.isNotEmpty;
/**
* 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>
*/
public final class Certificate implements Serializable {
public final class Certificate {
private final Usage usage;
private final String sessionId;
@ -50,6 +48,7 @@ public final class Certificate implements Serializable {
private final ZonedDateTime loginTime;
private final boolean keepAlive;
private final Set<String> userGroups;
private final Set<String> userRoles;
private final Map<String, String> propertyMap;
@ -64,49 +63,28 @@ public final class Certificate implements Serializable {
* by the {@link PrivilegeHandler}
* </p>
*
* @param usage
* the usage allowed for this certificate
* @param sessionId
* the users session id
* @param username
* the users login name
* @param firstName
* the users first name
* @param lastName
* the users last name
* @param authToken
* 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
* @param usage the usage allowed for this certificate
* @param sessionId the users session id
* @param username the users login name
* @param firstName the users first name
* @param lastName the users last name
* @param authToken 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,
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
if (StringHelper.isEmpty(sessionId)) {
throw new PrivilegeException("sessionId is null!");
}
if (StringHelper.isEmpty(username)) {
throw new PrivilegeException("username is null!");
}
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!");
}
DBC.PRE.assertNotEmpty("sessionId must not be empty", sessionId);
DBC.PRE.assertNotEmpty("username must not be empty", username);
DBC.PRE.assertNotEmpty("authToken must not be empty", authToken);
DBC.PRE.assertNotNull("userState must not be empty", userState);
DBC.PRE.assertNotNull("usage must not be empty", usage);
DBC.PRE.assertNotNull("source must not be null", source);
this.usage = usage;
this.sessionId = sessionId;
@ -126,11 +104,12 @@ public final class Certificate implements Serializable {
this.locale = locale;
if (propertyMap == null)
this.propertyMap = Collections.emptyMap();
this.propertyMap = Map.of();
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();
}
@ -150,15 +129,29 @@ public final class Certificate implements Serializable {
return this.usage;
}
public Set<String> getUserGroups() {
return this.userGroups;
}
public Set<String> getUserRoles() {
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
*
* @param role
* the role to check for
* @param role the role to check for
*
* @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
*
* @param key
* the key for which the property is to be returned
* @param key 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
*/
@ -283,12 +275,12 @@ public final class Certificate implements Serializable {
builder.append(", username=");
builder.append(this.username);
if (StringHelper.isNotEmpty(this.firstname)) {
if (isNotEmpty(this.firstname)) {
builder.append(", firstname=");
builder.append(this.firstname);
}
if (StringHelper.isNotEmpty(this.lastname)) {
if (isNotEmpty(this.lastname)) {
builder.append(", 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;
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.Set;
import li.strolch.privilege.base.AccessDeniedException;
import li.strolch.privilege.base.PrivilegeException;
import li.strolch.privilege.i18n.PrivilegeMessages;
import li.strolch.privilege.policy.PrivilegePolicy;
import static java.text.MessageFormat.format;
import static li.strolch.privilege.i18n.PrivilegeMessages.getString;
/**
* <p>
@ -36,19 +38,15 @@ import li.strolch.privilege.policy.PrivilegePolicy;
*
* @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) {
//
// 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,
public PrivilegeContext(UserRep userRep, Certificate certificate, Map<String, Privilege> privileges,
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.certificate = certificate;
this.privileges = Map.copyOf(privileges);
@ -85,35 +83,64 @@ public class PrivilegeContext {
public void assertHasPrivilege(String privilegeName) throws AccessDeniedException {
if (!this.privileges.containsKey(privilegeName)) {
String msg = MessageFormat.format(PrivilegeMessages.getString("Privilege.noprivilege.user"),
userRep.getUsername(), privilegeName);
String msg = format(getString("Privilege.noprivilege.user"), userRep.getUsername(), privilegeName);
throw new AccessDeniedException(msg);
}
}
public boolean hasGroup(String groupName) {
return this.userRep.hasGroup(groupName);
}
public boolean hasRole(String roleName) {
return this.userRep.hasRole(roleName);
}
public void assertHasRole(String roleName) throws AccessDeniedException {
if (!this.userRep.hasRole(roleName)) {
String msg = MessageFormat.format(PrivilegeMessages.getString("Privilege.noprivilege.role"),
userRep.getUsername(), roleName);
public void assertHasGroup(String groupName) throws AccessDeniedException {
if (!this.userRep.hasGroup(groupName)) {
String msg = format(getString("Privilege.noprivilege.group"), userRep.getUsername(), groupName);
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 {
for (String roleName : roleNames) {
if (this.userRep.hasRole(roleName))
return;
}
String msg = MessageFormat.format(PrivilegeMessages.getString("Privilege.noprivilege.role"),
userRep.getUsername(), String.join(", ", roleNames));
String msg = format(getString("Privilege.noprivilege.role"), userRep.getUsername(),
String.join(", ", roleNames));
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 {
for (String roleName : roleNames) {
if (this.userRep.hasRole(roleName))
@ -123,7 +150,7 @@ public class PrivilegeContext {
return false;
}
public IPrivilege getPrivilege(String privilegeName) throws AccessDeniedException {
public Privilege getPrivilege(String privilegeName) throws AccessDeniedException {
assertHasPrivilege(privilegeName);
return this.privileges.get(privilegeName);
}
@ -132,7 +159,7 @@ public class PrivilegeContext {
PrivilegePolicy policy = this.policies.get(policyName);
if (policy == null) {
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;
}
@ -148,15 +175,12 @@ public class PrivilegeContext {
*
* <p>This method uses the {@link SimpleRestrictable} to verify access</p>
*
* @param privilegeName
* the name of the privilege to verify
* @param privilegeValue
* the value
* @param privilegeName the name of the privilege to verify
* @param privilegeValue the value
*
* @throws AccessDeniedException
* if the user does not have access
* @throws PrivilegeException
* if there is an internal error due to wrongly configured privileges or programming errors
* @throws AccessDeniedException if the user does not have access
* @throws PrivilegeException if there is an internal error due to wrongly configured privileges or programming
* errors
*/
public void validateAction(String privilegeName, String privilegeValue)
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,
* then a {@link AccessDeniedException} is thrown.
*
* @param restrictable
* the {@link Restrictable} which the user wants to access
* @param restrictable the {@link Restrictable} which the user wants to access
*
* @throws AccessDeniedException
* if the user does not have access
* @throws PrivilegeException
* if there is an internal error due to wrongly configured privileges or programming errors
* @throws AccessDeniedException if the user does not have access
* @throws PrivilegeException if there is an internal error due to wrongly configured privileges or programming
* errors
*/
public void validateAction(Restrictable restrictable) throws PrivilegeException, AccessDeniedException {
// the privilege for the restrictable
String privilegeName = restrictable.getPrivilegeName();
IPrivilege privilege = this.privileges.get(privilegeName);
Privilege privilege = this.privileges.get(privilegeName);
if (privilege == null) {
String msg = MessageFormat.format(PrivilegeMessages.getString("Privilege.accessdenied.noprivilege"),
getUsername(), privilegeName, restrictable.getClass().getName(), restrictable.getPrivilegeValue());
String msg = format(getString("Privilege.accessdenied.noprivilege"), getUsername(), privilegeName,
restrictable.getClass().getName(), restrictable.getPrivilegeValue());
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
* true if the user has the privilege, and false if not
*
* @param restrictable
* the {@link Restrictable} which the user wants to access
* @param restrictable the {@link Restrictable} which the user wants to access
*
* @return returns true if the user has the privilege, and false if not
*
* @throws PrivilegeException
* if there is an internal error due to wrongly configured privileges or programming errors
* @throws PrivilegeException if there is an internal error due to wrongly configured privileges or programming
* errors
*/
public boolean hasPrivilege(Restrictable restrictable) throws PrivilegeException {
// the privilege for the restrictable
String privilegeName = restrictable.getPrivilegeName();
IPrivilege privilege = this.privileges.get(privilegeName);
Privilege privilege = this.privileges.get(privilegeName);
if (privilege == null)
return false;
@ -229,15 +250,13 @@ public class PrivilegeContext {
*
* <p>This method uses the {@link SimpleRestrictable} to verify access</p>
*
* @param privilegeName
* the name of the privilege to verify
* @param privilegeValue
* the value
* @param privilegeName the name of the privilege to verify
* @param privilegeValue the value
*
* @return returns true if the user has the privilege, and false if not
*
* @throws PrivilegeException
* if there is an internal error due to wrongly configured privileges or programming errors
* @throws PrivilegeException if there is an internal error due to wrongly configured privileges or programming
* errors
*/
public boolean hasPrivilege(String privilegeName, String privilegeValue) throws PrivilegeException {
return hasPrivilege(new SimpleRestrictable(privilegeName, privilegeValue));

View File

@ -15,27 +15,25 @@
*/
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.handler.PrivilegeHandler;
import li.strolch.privilege.model.internal.Role;
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
* 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>
*/
public class PrivilegeRep implements Serializable {
public class PrivilegeRep {
private String name;
private String policy;
@ -43,48 +41,57 @@ public class PrivilegeRep implements Serializable {
private Set<String> denyList;
private Set<String> allowList;
private boolean readOnly;
/**
* 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
* @param allAllowed
* a boolean defining if a {@link Role} with this {@link IPrivilege} has unrestricted access to a {@link
* Restrictable}
* @param denyList
* a list of deny rules for this {@link IPrivilege}
* @param allowList
* a list of allow rules for this {@link IPrivilege}
* @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
* @param allAllowed a boolean defining if a {@link Role} with this {@link Privilege} has unrestricted access to a
* {@link Restrictable}
* @param denyList a list of deny rules for this {@link Privilege}
* @param allowList a list of allow rules for this {@link Privilege}
*/
public PrivilegeRep(String name, String policy, boolean allAllowed, Set<String> denyList, Set<String> allowList) {
this.name = trimOrEmpty(name);
this.policy = trimOrEmpty(policy);
this.allAllowed = allAllowed;
this.denyList = denyList;
this.allowList = allowList;
setDenyList(denyList == null ? Set.of() : denyList);
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
*/
public void validate() {
if (StringHelper.isEmpty(this.name)) {
if (isEmpty(this.name))
throw new PrivilegeException("No name defined!");
}
if (StringHelper.isEmpty(this.policy)) {
throw new PrivilegeException("policy is null!");
}
if (this.denyList == null) {
if (isEmpty(this.policy))
throw new PrivilegeException("No policy defined!");
if (this.denyList == null)
throw new PrivilegeException("denyList is null");
}
if (this.allowList == null) {
if (this.allowList == null)
throw new PrivilegeException("allowList is null");
}
}
/**
@ -95,10 +102,10 @@ public class PrivilegeRep implements Serializable {
}
/**
* @param name
* the name to set
* @param name the name to set
*/
public void setName(String name) {
assertNotReadonly();
this.name = trimOrEmpty(name);
}
@ -110,10 +117,10 @@ public class PrivilegeRep implements Serializable {
}
/**
* @param policy
* the policy to set
* @param policy the policy to set
*/
public void setPolicy(String policy) {
assertNotReadonly();
this.policy = trimOrEmpty(policy);
}
@ -125,10 +132,10 @@ public class PrivilegeRep implements Serializable {
}
/**
* @param allAllowed
* the allAllowed to set
* @param allAllowed the allAllowed to set
*/
public void setAllAllowed(boolean allAllowed) {
assertNotReadonly();
this.allAllowed = allAllowed;
}
@ -136,30 +143,30 @@ public class PrivilegeRep implements Serializable {
* @return the denyList
*/
public Set<String> getDenyList() {
return this.denyList == null ? new HashSet<>() : this.denyList;
return this.denyList;
}
/**
* @param denyList
* the denyList to set
* @param denyList the denyList to set
*/
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
*/
public Set<String> getAllowList() {
return this.allowList == null ? new HashSet<>() : this.allowList;
return this.allowList;
}
/**
* @param allowList
* the allowList to set
* @param allowList the allowList to set
*/
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
public String toString() {
return "PrivilegeRep [name=" + this.name + ", policy=" + this.policy + ", allAllowed=" + this.allAllowed
+ ", denyList=" + (this.denyList == null ? "null" : this.denyList.size()) + ", allowList=" + (
this.allowList == null ?
"null" :
this.allowList.size()) + "]";
return "PrivilegeRep [name=" + this.name + ", policy=" + this.policy + ", allAllowed=" + this.allAllowed +
", denyList=" + this.denyList.size() + ", allowList=" + this.allowList.size() + "]";
}
@Override
@ -193,10 +197,13 @@ public class PrivilegeRep implements Serializable {
if (getClass() != obj.getClass())
return false;
PrivilegeRep other = (PrivilegeRep) obj;
if (this.name == null) {
if (this.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) {

View File

@ -21,7 +21,7 @@ import li.strolch.privilege.policy.PrivilegePolicy;
* <p>
* 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
* #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
* </p>
*
@ -30,9 +30,9 @@ import li.strolch.privilege.policy.PrivilegePolicy;
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();

View File

@ -15,16 +15,16 @@
*/
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.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
@ -33,40 +33,55 @@ import li.strolch.utils.helper.StringHelper;
*
* @author Robert von Burg <eitch@eitchnet.ch>
*/
public class RoleRep implements Serializable {
public class RoleRep {
private String name;
private List<PrivilegeRep> privileges;
private Map<String, PrivilegeRep> privileges;
private boolean readOnly;
/**
* Default constructor
*
* @param name
* the name of this role
* @param privileges
* the list of privileges granted to this role
* @param name the name of 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.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
*/
public void validate() {
if (StringHelper.isEmpty(this.name))
if (isEmpty(this.name))
throw new PrivilegeException("name is null");
if (this.privileges != null && !this.privileges.isEmpty()) {
for (PrivilegeRep privilege : this.privileges) {
try {
privilege.validate();
} catch (Exception e) {
String msg = "Privilege {0} is invalid on role {1}";
msg = MessageFormat.format(msg, privilege.getName(), this.name);
throw new PrivilegeException(msg, e);
}
for (PrivilegeRep privilege : this.privileges.values()) {
try {
privilege.validate();
} catch (Exception e) {
String msg = "Privilege {0} is invalid on role {1}";
msg = MessageFormat.format(msg, privilege.getName(), this.name);
throw new PrivilegeException(msg, e);
}
}
}
@ -79,30 +94,29 @@ public class RoleRep implements Serializable {
}
/**
* @param name
* the name to set
* @param name the name to set
*/
public void setName(String name) {
assertNotReadonly();
this.name = trimOrEmpty(name);
}
/**
* Returns the privileges assigned to this Role as a list
*
* @return the privileges assigned to this Role as a list
*/
public List<PrivilegeRep> getPrivileges() {
return this.privileges == null ? new ArrayList<>() : this.privileges;
public Map<String, PrivilegeRep> getPrivileges() {
if (this.privileges == null)
return null;
return this.privileges;
}
/**
* Sets the privileges on this from a list
*
* @param privileges
* the list of privileges to assign to this role
*/
public void setPrivileges(List<PrivilegeRep> privileges) {
this.privileges = privileges;
public void setPrivileges(Map<String, PrivilegeRep> privileges) {
assertNotReadonly();
DBC.PRE.assertNotNull("privileges must not be null!", privileges);
this.privileges = new HashMap<>(privileges);
}
public void addPrivilege(PrivilegeRep privilegeRep) {
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
public String toString() {
return "RoleRep [name=" + this.name + ", privilegeMap=" + (this.privileges == null ? "null" : this.privileges)
+ "]";
return "RoleRep [name=" + this.name + ", privilegeMap=" + this.privileges + "]";
}
@Override
@ -133,10 +146,13 @@ public class RoleRep implements Serializable {
if (getClass() != obj.getClass())
return false;
RoleRep other = (RoleRep) obj;
if (this.name == null) {
if (this.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) {

View File

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

View File

@ -15,20 +15,19 @@
*/
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.PrivilegeException;
import li.strolch.privilege.model.internal.Role;
import li.strolch.privilege.model.internal.User;
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
@ -37,7 +36,7 @@ import li.strolch.utils.helper.StringHelper;
*
* @author Robert von Burg <eitch@eitchnet.ch>
*/
public class UserRep implements Serializable {
public class UserRep {
private String userId;
private String username;
@ -45,47 +44,40 @@ public class UserRep implements Serializable {
private String lastname;
private UserState userState;
private Locale locale;
private Set<String> groups;
private Set<String> roles;
private Map<String, String> properties;
private UserHistory history;
private boolean readOnly;
/**
* Default constructor
*
* @param userId
* the user's id
* @param username
* the user's login name
* @param firstname
* the user's first name
* @param lastname
* the user's last name
* @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
* @param userId the user's id
* @param username the user's login name
* @param firstname the user's first name
* @param lastname the user's last name
* @param userState the user's {@link UserState}
* @param groups the set of {@link li.strolch.privilege.model.internal.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
*/
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.username = trimOrEmpty(username);
this.firstname = trimOrEmpty(firstname);
this.lastname = trimOrEmpty(lastname);
this.userState = userState;
this.roles = roles == null ? null : roles.stream().map(String::trim).collect(Collectors.toSet());
this.locale = locale;
if (propertyMap != null) {
this.properties = new HashMap<>();
propertyMap.forEach((key, value) -> this.properties.put(key.trim(), value.trim()));
}
this.history = history;
setGroups(groups == null ? Set.of() : groups);
setRoles(roles == null ? Set.of() : roles);
setProperties(properties == null ? Map.of() : properties);
this.history = history == null ? UserHistory.EMPTY : history;
}
@SuppressWarnings("unused")
@ -97,30 +89,53 @@ public class UserRep implements Serializable {
* Validates that all required fields are set
*/
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))
throw new PrivilegeException("userId is null or empty");
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);
// username must be at least 3 characters in length
if (this.username.length() < 3) {
String msg = MessageFormat.format("The given username ''{0}'' is shorter than 3 characters", this.username);
throw new PrivilegeException(msg);
}
if (this.userState == null)
throw new PrivilegeException("userState is null");
throw new PrivilegeException("userState may not be null");
if (StringHelper.isEmpty(this.firstname))
throw new PrivilegeException("firstname is null or empty");
if (this.userState != UserState.SYSTEM) {
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))
throw new PrivilegeException("lastname is null or empty");
if (this.groups == null)
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())
throw new PrivilegeException("roles is null or empty");
if (this.groups.isEmpty() && this.roles.isEmpty())
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() {
@ -145,10 +160,10 @@ public class UserRep implements Serializable {
/**
* Set the userId
*
* @param userId
* to set
* @param userId to set
*/
public void setUserId(String userId) {
assertNotReadonly();
this.userId = trimOrEmpty(userId);
}
@ -160,10 +175,10 @@ public class UserRep implements Serializable {
}
/**
* @param username
* the username to set
* @param username the username to set
*/
public void setUsername(String username) {
assertNotReadonly();
this.username = trimOrEmpty(username);
}
@ -175,10 +190,10 @@ public class UserRep implements Serializable {
}
/**
* @param firstname
* the firstname to set
* @param firstname the firstname to set
*/
public void setFirstname(String firstname) {
assertNotReadonly();
this.firstname = trimOrEmpty(firstname);
}
@ -190,10 +205,10 @@ public class UserRep implements Serializable {
}
/**
* @param lastname
* the lastname to set
* @param lastname the lastname to set
*/
public void setLastname(String lastname) {
assertNotReadonly();
this.lastname = trimOrEmpty(lastname);
}
@ -205,13 +220,27 @@ public class UserRep implements Serializable {
}
/**
* @param userState
* the userState to set
* @param userState the userState to set
*/
public void setUserState(UserState userState) {
assertNotReadonly();
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
*/
@ -220,21 +249,19 @@ public class UserRep implements Serializable {
}
/**
* @param roles
* the roles to set
* @param roles the roles to set
*/
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) {
return this.roles.contains(role);
}
@ -247,10 +274,10 @@ public class UserRep implements Serializable {
}
/**
* @param locale
* the locale to set
* @param locale the locale to set
*/
public void setLocale(Locale locale) {
assertNotReadonly();
this.locale = locale;
}
@ -260,18 +287,15 @@ public class UserRep implements Serializable {
* @return the user history
*/
public UserHistory getHistory() {
if (this.history == null)
return new UserHistory();
return this.history;
}
/**
* Returns true if the the given property exists
* Returns true if the given property exists
*
* @param key
* the property key to check
* @param key 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) {
return this.properties.containsKey(key);
@ -280,40 +304,41 @@ public class UserRep implements Serializable {
/**
* Returns the property with the given key
*
* @param key
* the key for which the property is to be returned
* @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) {
if (this.properties == null)
return null;
return this.properties.get(key);
}
/**
* Set the property with the key to the value
*
* @param key
* the key of the property to set
* @param value
* the value of the property to set
* @param key the key of the property to set
* @param value the value of the property to set
*/
public void setProperty(String key, String value) {
if (this.properties == null)
this.properties = new HashMap<>(1);
DBC.PRE.assertNotEmpty("key must not be empty!", key);
DBC.PRE.assertNotEmpty("value must not be empty!", value);
assertNotReadonly();
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
*
* @return the {@link Set} of keys of all properties
*/
public Set<String> getPropertyKeySet() {
if (this.properties == null)
return new HashSet<>();
return new HashSet<>(this.properties.keySet());
return this.properties.keySet();
}
/**
@ -322,9 +347,7 @@ public class UserRep implements Serializable {
* @return the map of properties
*/
public Map<String, String> getProperties() {
if (this.properties == null)
return new HashMap<>();
return new HashMap<>(this.properties);
return this.properties;
}
/**
@ -361,9 +384,9 @@ public class UserRep implements Serializable {
*/
@Override
public String toString() {
return "UserRep [userId=" + this.userId + ", username=" + this.username + ", firstname=" + this.firstname
+ ", lastname=" + this.lastname + ", userState=" + this.userState + ", locale=" + this.locale
+ ", roles=" + this.roles + "]";
return "UserRep [userId=" + this.userId + ", username=" + this.username + ", firstname=" + this.firstname +
", lastname=" + this.lastname + ", userState=" + this.userState + ", locale=" + this.locale +
", roles=" + this.roles + "]";
}
@Override
@ -390,12 +413,8 @@ public class UserRep implements Serializable {
}
public UserRep getCopy() {
Set<String> roles = new HashSet<>(this.roles);
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());
return new UserRep(this.userId, this.username, this.firstname, this.lastname, this.userState, this.groups,
this.roles, this.locale, this.properties, this.history);
}
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.*;
public class PasswordCrypt {
public record PasswordCrypt(byte[] password, byte[] salt, String hashAlgorithm, int hashIterations, int hashKeyLength) {
private final byte[] password;
private final byte[] salt;
private final String hashAlgorithm;
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;
@Override
public String toString() {
return buildPasswordString();
}
public PasswordCrypt(byte[] password, byte[] salt, String hashAlgorithm, int hashIterations, int hashKeyLength) {
this.password = password;
this.salt = salt;
this.hashAlgorithm = hashAlgorithm;
this.hashIterations = hashIterations;
this.hashKeyLength = hashKeyLength;
public String buildPasswordString() {
if (this.password == null || this.salt == null || this.hashAlgorithm == null || this.hashIterations == -1 ||
this.hashKeyLength == -1) {
return null;
}
return buildPasswordString(this.hashAlgorithm, this.hashIterations, this.hashKeyLength, this.salt,
this.password);
}
public byte[] getPassword() {
return password;
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;
}
public byte[] getSalt() {
return salt;
}
public String getHashAlgorithm() {
return hashAlgorithm;
}
public int getHashIterations() {
return hashIterations;
}
public int getHashKeyLength() {
return hashKeyLength;
public static PasswordCrypt of(byte[] password, byte[] salt) {
return new PasswordCrypt(password, salt, null, -1, -1);
}
public static PasswordCrypt parse(String passwordS, String saltS) {
@ -55,14 +40,14 @@ public class PasswordCrypt {
salt = fromHexString(saltS.trim());
if (isEmpty(passwordS))
return new PasswordCrypt(null, salt);
return PasswordCrypt.of(null, salt);
passwordS = passwordS.trim();
byte[] password;
if (!passwordS.startsWith("$")) {
password = fromHexString(passwordS);
return new PasswordCrypt(password, salt);
return PasswordCrypt.of(password, salt);
}
String[] parts = passwordS.split("\\$");
@ -86,28 +71,4 @@ public class PasswordCrypt {
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;
import java.util.*;
import java.util.Map.Entry;
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.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>
@ -37,57 +40,32 @@ import li.strolch.utils.helper.StringHelper;
*
* @author Robert von Burg <eitch@eitchnet.ch>
*/
public final class Role {
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 record Role(String name, Map<String, Privilege> privilegeMap) {
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.privilegeMap = Collections.unmodifiableMap(privilegeMap);
this.privilegeMap = Map.copyOf(privilegeMap);
}
/**
* Construct {@link Role} from its representation {@link RoleRep}
*
* @param roleRep
* the representation from which to create the {@link Role}
* @param roleRep the representation from which to create the {@link Role}
*/
public Role(RoleRep roleRep) {
public static Role of(RoleRep roleRep) {
String name = roleRep.getName();
if (StringHelper.isEmpty(name)) {
if (isEmpty(name))
throw new PrivilegeException("No name defined!");
}
if (roleRep.getPrivileges() == null) {
if (roleRep.getPrivileges() == null)
throw new PrivilegeException("Privileges may not be null!");
}
// build privileges from rep
Map<String, IPrivilege> privilegeMap = new HashMap<>(roleRep.getPrivileges().size());
for (PrivilegeRep privilege : roleRep.getPrivileges()) {
privilegeMap.put(privilege.getName(), new PrivilegeImpl(privilege));
}
Map<String, Privilege> privilegeMap = new HashMap<>(roleRep.getPrivileges().size());
roleRep.getPrivileges().values().forEach(p -> privilegeMap.put(p.getName(), Privilege.of(p)));
this.name = name;
this.privilegeMap = Collections.unmodifiableMap(privilegeMap);
return new Role(name, 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() {
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);
}
/**
* 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
* the name of the {@link IPrivilege}
* @param name the name of the {@link Privilege}
*
* @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) {
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
*/
public RoleRep asRoleRep() {
List<PrivilegeRep> privileges = new ArrayList<>();
for (Entry<String, IPrivilege> entry : this.privilegeMap.entrySet()) {
privileges.add(entry.getValue().asPrivilegeRep());
}
Map<String, PrivilegeRep> privileges = new HashMap<>();
this.privilegeMap.values().forEach(p -> privileges.put(p.getName(), p.asPrivilegeRep()));
return new RoleRep(this.name, privileges);
}

View File

@ -16,12 +16,14 @@
package li.strolch.privilege.model.internal;
import li.strolch.privilege.base.PrivilegeConstants;
import li.strolch.privilege.base.PrivilegeException;
import li.strolch.privilege.model.UserRep;
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.*;
@ -34,59 +36,36 @@ import static li.strolch.privilege.base.PrivilegeConstants.*;
* that
* </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>
*/
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,
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) {
if (StringHelper.isEmpty(userId))
throw new PrivilegeException("No UserId defined!");
if (userState == null)
throw new PrivilegeException("No userState defined!");
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!");
}
DBC.PRE.assertNotEmpty("userId must not be empty", userId);
DBC.PRE.assertNotEmpty("username must not be empty", username);
DBC.PRE.assertNotNull("userState must not be null", userState);
DBC.PRE.assertNotNull("history must not be null", history);
if (history == null)
throw new PrivilegeException("History must not be null!");
if (userState != UserState.SYSTEM) {
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
// 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.lastname = lastname;
if (roles == null)
this.roles = Collections.emptySet();
else
this.roles = Set.copyOf(roles);
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.groups = groups == null ? Set.of() : Set.copyOf(groups);
this.roles = roles == null ? Set.of() : Set.copyOf(roles);
this.locale = locale == null ? Locale.getDefault() : locale;
this.propertyMap = propertyMap == null ? Map.of() : Map.copyOf(propertyMap);
this.passwordChangeRequested = passwordChangeRequested;
this.history = history;
}
/**
* @return the userId
*/
public String getUserId() {
return this.userId;
}
/**
* @return the username
*/
public String getUsername() {
return this.username;
}
@ -145,48 +108,34 @@ public final class User {
return this.passwordCrypt;
}
/**
* @return the first name
*/
public String getFirstname() {
return this.firstname;
}
/**
* @return the last name
*/
public String getLastname() {
return this.lastname;
}
/**
* @return the userState
*/
public UserState getUserState() {
return this.userState;
}
/**
* @return the roles
*/
public Set<String> getGroups() {
return this.groups;
}
public Set<String> getRoles() {
return this.roles;
}
/**
* Returns true if this user has the specified role
*
* @param role the name of the {@link Role} to check for
*
* @return true if the this user has the specified role
*/
public boolean hasGroup(String group) {
return this.groups.contains(group);
}
public boolean hasRole(String role) {
return this.roles.contains(role);
}
/**
* @return the locale
*/
public Locale getLocale() {
return this.locale;
}
@ -195,11 +144,6 @@ public final class User {
return this.passwordChangeRequested;
}
/**
* Returns the History object
*
* @return the History object
*/
public UserHistory getHistory() {
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
*/
public UserRep asUserRep() {
return new UserRep(this.userId, this.username, this.firstname, this.lastname, this.userState,
new HashSet<>(this.roles), this.locale, new HashMap<>(this.propertyMap), this.history.getClone());
return new UserRep(this.userId, this.username, this.firstname, this.lastname, this.userState, this.groups,
this.roles, this.locale, new HashMap<>(this.propertyMap), this.history);
}
/**
@ -315,9 +259,13 @@ public final class User {
if (getClass() != obj.getClass())
return false;
User other = (User) obj;
if (this.userId == null) {
if (this.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;
import li.strolch.privilege.model.Usage;
import li.strolch.utils.dbc.DBC;
import java.time.LocalDateTime;
import li.strolch.privilege.model.Usage;
public class UserChallenge {
public final class UserChallenge {
private final User user;
private final String challenge;
private final String source;
@ -13,6 +14,10 @@ public class UserChallenge {
private boolean fulfilled;
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.user = user;
this.challenge = challenge;

View File

@ -1,32 +1,20 @@
package li.strolch.privilege.model.internal;
import java.io.Serializable;
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;
private ZonedDateTime lastLogin;
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 static final UserHistory EMPTY = new UserHistory(EMPTY_VALUE_ZONED_DATE, EMPTY_VALUE_ZONED_DATE,
EMPTY_VALUE_ZONED_DATE);
public ZonedDateTime getFirstLogin() {
return this.firstLogin;
}
public boolean isFirstLoginEmpty() {
return this.firstLogin.equals(ISO8601.EMPTY_VALUE_ZONED_DATE);
}
public void setFirstLogin(ZonedDateTime firstLogin) {
this.firstLogin = firstLogin;
return this.firstLogin.equals(EMPTY_VALUE_ZONED_DATE);
}
public ZonedDateTime getLastLogin() {
@ -34,11 +22,7 @@ public class UserHistory implements Serializable {
}
public boolean isLastLoginEmpty() {
return this.lastLogin.equals(ISO8601.EMPTY_VALUE_ZONED_DATE);
}
public void setLastLogin(ZonedDateTime lastLogin) {
this.lastLogin = lastLogin;
return this.lastLogin.equals(EMPTY_VALUE_ZONED_DATE);
}
public ZonedDateTime getLastPasswordChange() {
@ -46,22 +30,26 @@ public class UserHistory implements Serializable {
}
public boolean isLastPasswordChangeEmpty() {
return this.lastPasswordChange.equals(ISO8601.EMPTY_VALUE_ZONED_DATE);
}
public void setLastPasswordChange(ZonedDateTime lastPasswordChange) {
this.lastPasswordChange = lastPasswordChange;
return this.lastPasswordChange.equals(EMPTY_VALUE_ZONED_DATE);
}
public boolean isEmpty() {
return isFirstLoginEmpty() && isLastLoginEmpty() && isLastPasswordChangeEmpty();
}
public UserHistory getClone() {
UserHistory clone = new UserHistory();
clone.firstLogin = this.firstLogin;
clone.lastLogin = this.lastLogin;
clone.lastPasswordChange = this.lastPasswordChange;
return clone;
public UserHistory withFirstLogin(ZonedDateTime firstLogin) {
return new UserHistory(firstLogin, this.lastLogin, lastPasswordChange);
}
public UserHistory withLastLogin(ZonedDateTime lastLogin) {
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.PrivilegeException;
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.Restrictable;
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
*
* @see li.strolch.privilege.policy.PrivilegePolicy#validateAction(PrivilegeContext, IPrivilege, Restrictable)
* @see li.strolch.privilege.policy.PrivilegePolicy#validateAction(PrivilegeContext, Privilege, Restrictable)
*/
@Override
public void validateAction(PrivilegeContext ctx, IPrivilege privilege, Restrictable restrictable)
public void validateAction(PrivilegeContext ctx, Privilege privilege, Restrictable restrictable)
throws AccessDeniedException {
String privilegeValue = validatePrivilegeValue(privilege, restrictable);
@ -54,7 +54,7 @@ public class DefaultPrivilege implements PrivilegePolicy {
}
@Override
public boolean hasPrivilege(PrivilegeContext ctx, IPrivilege privilege, Restrictable restrictable) {
public boolean hasPrivilege(PrivilegeContext ctx, Privilege privilege, Restrictable restrictable) {
String privilegeValue = validatePrivilegeValue(privilege, restrictable);
@ -65,7 +65,7 @@ public class DefaultPrivilege implements PrivilegePolicy {
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);
// 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.PrivilegeException;
import li.strolch.privilege.model.IPrivilege;
import li.strolch.privilege.model.Privilege;
import li.strolch.privilege.model.PrivilegeContext;
import li.strolch.privilege.model.Restrictable;
import li.strolch.privilege.model.internal.Role;
@ -26,7 +26,7 @@ import li.strolch.privilege.model.internal.User;
/**
* <p>
* {@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>
@ -38,29 +38,29 @@ import li.strolch.privilege.model.internal.User;
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
* the privilege context
* @param privilege
* the {@link IPrivilege} containing the permissions
* the {@link Privilege} containing the permissions
* @param restrictable
* the {@link Restrictable} to which the user wants access
*
* @throws AccessDeniedException
* if action not allowed
*/
void validateAction(PrivilegeContext context, IPrivilege privilege, Restrictable restrictable)
void validateAction(PrivilegeContext context, Privilege privilege, Restrictable restrictable)
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}
*
* @param context
* the privilege context
* @param privilege
* the {@link IPrivilege} containing the permissions
* the {@link Privilege} containing the permissions
* @param restrictable
* the {@link Restrictable} to which the user wants access
*
@ -69,6 +69,6 @@ public interface PrivilegePolicy {
* @throws AccessDeniedException
* 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;
}

View File

@ -20,7 +20,7 @@ import java.text.MessageFormat;
import li.strolch.privilege.base.AccessDeniedException;
import li.strolch.privilege.base.PrivilegeException;
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.Restrictable;
import li.strolch.utils.helper.StringHelper;
@ -34,7 +34,7 @@ public class PrivilegePolicyHelper {
* Validates the given values and returns the privilege name
*
* @param privilege
* the {@link IPrivilege}
* the {@link Privilege}
* @param restrictable
* the {@link Restrictable}
*
@ -43,7 +43,7 @@ public class PrivilegePolicyHelper {
* @throws PrivilegeException
* 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)
throw new PrivilegeException(PrivilegeMessages.getString("Privilege.privilegeNull"));
if (restrictable == null)
@ -88,7 +88,7 @@ public class PrivilegePolicyHelper {
* @throws AccessDeniedException
* 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 {
// first check values not allowed
@ -102,7 +102,7 @@ public class PrivilegePolicyHelper {
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) {
if (assertHasPrivilege) {

View File

@ -24,7 +24,7 @@ import li.strolch.privilege.base.AccessDeniedException;
import li.strolch.privilege.base.PrivilegeException;
import li.strolch.privilege.handler.PrivilegeHandler;
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.Restrictable;
import li.strolch.privilege.model.internal.Role;
@ -42,18 +42,18 @@ import li.strolch.utils.dbc.DBC;
public class RoleAccessPrivilege implements PrivilegePolicy {
@Override
public void validateAction(PrivilegeContext ctx, IPrivilege privilege, Restrictable restrictable)
public void validateAction(PrivilegeContext ctx, Privilege privilege, Restrictable restrictable)
throws AccessDeniedException {
validateAction(ctx, privilege, restrictable, true);
}
@Override
public boolean hasPrivilege(PrivilegeContext ctx, IPrivilege privilege, Restrictable restrictable)
public boolean hasPrivilege(PrivilegeContext ctx, Privilege privilege, Restrictable restrictable)
throws PrivilegeException {
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 {
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.handler.PrivilegeHandler;
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.Restrictable;
import li.strolch.privilege.model.internal.User;
@ -41,18 +41,18 @@ import li.strolch.utils.dbc.DBC;
public class UserAccessPrivilege implements PrivilegePolicy {
@Override
public void validateAction(PrivilegeContext ctx, IPrivilege privilege, Restrictable restrictable)
public void validateAction(PrivilegeContext ctx, Privilege privilege, Restrictable restrictable)
throws AccessDeniedException {
validateAction(ctx, privilege, restrictable, true);
}
@Override
public boolean hasPrivilege(PrivilegeContext ctx, IPrivilege privilege, Restrictable restrictable)
public boolean hasPrivilege(PrivilegeContext ctx, Privilege privilege, Restrictable restrictable)
throws PrivilegeException {
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 {
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.handler.PrivilegeHandler;
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.Restrictable;
import li.strolch.privilege.model.internal.User;
@ -43,13 +43,13 @@ import li.strolch.utils.dbc.DBC;
public class UserAccessWithSameOrganisationPrivilege extends UserAccessPrivilege {
@Override
public void validateAction(PrivilegeContext ctx, IPrivilege privilege, Restrictable restrictable)
public void validateAction(PrivilegeContext ctx, Privilege privilege, Restrictable restrictable)
throws AccessDeniedException {
validateAction(ctx, privilege, restrictable, true);
}
@Override
public boolean hasPrivilege(PrivilegeContext ctx, IPrivilege privilege, Restrictable restrictable)
public boolean hasPrivilege(PrivilegeContext ctx, Privilege privilege, Restrictable restrictable)
throws PrivilegeException {
return validateAction(ctx, privilege, restrictable, false);
}
@ -59,7 +59,7 @@ public class UserAccessWithSameOrganisationPrivilege extends UserAccessPrivilege
}
@Override
protected boolean validateAction(PrivilegeContext ctx, IPrivilege privilege, Restrictable restrictable,
protected boolean validateAction(PrivilegeContext ctx, Privilege privilege, Restrictable restrictable,
boolean assertHasPrivilege) throws AccessDeniedException {
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.i18n.PrivilegeMessages;
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.Restrictable;
@ -44,18 +44,18 @@ import li.strolch.privilege.model.Restrictable;
public class UsernameFromCertificatePrivilege implements PrivilegePolicy {
@Override
public void validateAction(PrivilegeContext ctx, IPrivilege privilege, Restrictable restrictable)
public void validateAction(PrivilegeContext ctx, Privilege privilege, Restrictable restrictable)
throws AccessDeniedException {
validateAction(ctx, privilege, restrictable, true);
}
@Override
public boolean hasPrivilege(PrivilegeContext ctx, IPrivilege privilege, Restrictable restrictable)
public boolean hasPrivilege(PrivilegeContext ctx, Privilege privilege, Restrictable restrictable)
throws PrivilegeException {
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 {
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.i18n.PrivilegeMessages;
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.Restrictable;
@ -48,19 +48,19 @@ import li.strolch.privilege.model.Restrictable;
public class UsernameFromCertificateWithSameOrganisationPrivilege extends UsernameFromCertificatePrivilege {
@Override
public void validateAction(PrivilegeContext ctx, IPrivilege privilege, Restrictable restrictable)
public void validateAction(PrivilegeContext ctx, Privilege privilege, Restrictable restrictable)
throws AccessDeniedException {
validateAction(ctx, privilege, restrictable, true);
}
@Override
public boolean hasPrivilege(PrivilegeContext ctx, IPrivilege privilege, Restrictable restrictable)
public boolean hasPrivilege(PrivilegeContext ctx, Privilege privilege, Restrictable restrictable)
throws PrivilegeException {
return validateAction(ctx, privilege, restrictable, false);
}
@Override
protected boolean validateAction(PrivilegeContext ctx, IPrivilege privilege, Restrictable restrictable,
protected boolean validateAction(PrivilegeContext ctx, Privilege privilege, Restrictable restrictable,
boolean assertHasPrivilege) throws AccessDeniedException {
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;
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.model.Usage;
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.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>
*/
@ -55,33 +55,27 @@ public class CertificateStubsSaxReader extends DefaultHandler {
public void startElement(String uri, String localName, String qName, Attributes attributes) {
switch (qName) {
case XML_ROOT_CERTIFICATES:
break;
case XML_CERTIFICATE:
CertificateStub stub = new CertificateStub();
stub.usage = Usage.valueOf(attributes.getValue(XML_ATTR_USAGE).trim());
stub.sessionId = attributes.getValue(XML_ATTR_SESSION_ID).trim();
stub.username = attributes.getValue(XML_ATTR_USERNAME).trim();
stub.authToken = attributes.getValue(XML_ATTR_AUTH_TOKEN).trim();
stub.source = attributes.getValue(XML_ATTR_SOURCE).trim();
stub.locale = Locale.forLanguageTag(attributes.getValue(XML_ATTR_LOCALE).trim());
stub.loginTime = ISO8601.parseToZdt(attributes.getValue(XML_ATTR_LOGIN_TIME).trim());
stub.lastAccess = ISO8601.parseToZdt(attributes.getValue(XML_ATTR_LAST_ACCESS).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("authToken missing on sessions data!", stub.authToken);
if (isEmpty(stub.source))
stub.source = SOURCE_UNKNOWN;
this.stubs.add(stub);
break;
default:
throw new PrivilegeException("Unhandled tag " + qName);
case ROOT_CERTIFICATES -> {
}
case CERTIFICATE -> {
CertificateStub stub = new CertificateStub();
stub.usage = Usage.valueOf(attributes.getValue(ATTR_USAGE).trim());
stub.sessionId = attributes.getValue(ATTR_SESSION_ID).trim();
stub.username = attributes.getValue(ATTR_USERNAME).trim();
stub.authToken = attributes.getValue(ATTR_AUTH_TOKEN).trim();
stub.source = attributes.getValue(ATTR_SOURCE).trim();
stub.locale = Locale.forLanguageTag(attributes.getValue(ATTR_LOCALE).trim());
stub.loginTime = ISO8601.parseToZdt(attributes.getValue(ATTR_LOGIN_TIME).trim());
stub.lastAccess = ISO8601.parseToZdt(attributes.getValue(ATTR_LAST_ACCESS).trim());
stub.keepAlive = Boolean.parseBoolean(attributes.getValue(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("authToken missing on sessions data!", stub.authToken);
if (isEmpty(stub.source))
stub.source = SOURCE_UNKNOWN;
this.stubs.add(stub);
}
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;
// TODO write JavaDoc...
public interface ElementParser {
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;
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.Deque;
import java.util.HashMap;
import java.util.Map;
import 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 static li.strolch.privilege.helper.XmlConstants.*;
/**
* @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 {
switch (qName) {
case XML_CONTAINER -> this.buildersStack.push(new ContainerParser());
case XML_PARAMETERS -> this.buildersStack.push(new ParametersParser());
case XML_POLICIES -> this.buildersStack.push(new PoliciesParser());
default -> {
// nothing to do, probably handle on stack
}
case CONTAINER -> this.buildersStack.push(new ContainerParser());
case PARAMETERS -> this.buildersStack.push(new ParametersParser());
case POLICIES -> this.buildersStack.push(new PoliciesParser());
default -> {
// nothing to do, probably handle on stack
}
}
if (!this.buildersStack.isEmpty())
@ -73,7 +72,7 @@ public class PrivilegeConfigSaxReader extends DefaultHandler {
this.buildersStack.peek().endElement(uri, localName, qName);
ElementParser elementParser = switch (qName) {
case XML_CONTAINER, XML_PARAMETERS, XML_POLICIES -> this.buildersStack.pop();
case CONTAINER, PARAMETERS, POLICIES -> this.buildersStack.pop();
default -> null;
};
@ -89,38 +88,38 @@ public class PrivilegeConfigSaxReader extends DefaultHandler {
public void startElement(String uri, String localName, String qName, Attributes attributes) {
switch (qName) {
case XML_CONTAINER -> this.currentElement = qName;
case XML_HANDLER_ENCRYPTION -> {
this.currentElement = qName;
String className = attributes.getValue(XML_ATTR_CLASS).trim();
getContainerModel().setEncryptionHandlerClassName(className);
}
case XML_HANDLER_PASSWORD_STRENGTH -> {
this.currentElement = qName;
String className = attributes.getValue(XML_ATTR_CLASS).trim();
getContainerModel().setPasswordStrengthHandlerClassName(className);
}
case XML_HANDLER_PERSISTENCE -> {
this.currentElement = qName;
String className = attributes.getValue(XML_ATTR_CLASS).trim();
getContainerModel().setPersistenceHandlerClassName(className);
}
case XML_HANDLER_USER_CHALLENGE -> {
this.currentElement = qName;
String className = attributes.getValue(XML_ATTR_CLASS).trim();
getContainerModel().setUserChallengeHandlerClassName(className);
}
case XML_HANDLER_SSO -> {
this.currentElement = qName;
String className = attributes.getValue(XML_ATTR_CLASS).trim();
getContainerModel().setSsoHandlerClassName(className);
}
case XML_HANDLER_PRIVILEGE -> {
this.currentElement = qName;
String className = attributes.getValue(XML_ATTR_CLASS).trim();
getContainerModel().setPrivilegeHandlerClassName(className);
}
default -> throw new IllegalStateException("Unexpected value: " + qName);
case CONTAINER -> this.currentElement = qName;
case HANDLER_PRIVILEGE -> {
this.currentElement = qName;
String className = attributes.getValue(ATTR_CLASS).trim();
getContainerModel().setPrivilegeHandlerClassName(className);
}
case HANDLER_ENCRYPTION -> {
this.currentElement = qName;
String className = attributes.getValue(ATTR_CLASS).trim();
getContainerModel().setEncryptionHandlerClassName(className);
}
case HANDLER_PASSWORD_STRENGTH -> {
this.currentElement = qName;
String className = attributes.getValue(ATTR_CLASS).trim();
getContainerModel().setPasswordStrengthHandlerClassName(className);
}
case HANDLER_PERSISTENCE -> {
this.currentElement = qName;
String className = attributes.getValue(ATTR_CLASS).trim();
getContainerModel().setPersistenceHandlerClassName(className);
}
case HANDLER_USER_CHALLENGE -> {
this.currentElement = qName;
String className = attributes.getValue(ATTR_CLASS).trim();
getContainerModel().setUserChallengeHandlerClassName(className);
}
case HANDLER_SSO -> {
this.currentElement = qName;
String className = attributes.getValue(ATTR_CLASS).trim();
getContainerModel().setSsoHandlerClassName(className);
}
default -> throw new IllegalStateException("Unexpected value: " + qName);
}
}
@ -129,20 +128,17 @@ public class PrivilegeConfigSaxReader extends DefaultHandler {
if (!(child instanceof ParametersParser parametersChild))
return;
final Map<String, String> params = parametersChild.getParameterMap();
switch (this.currentElement) {
case XML_CONTAINER -> getContainerModel().setParameterMap(parametersChild.getParameterMap());
case XML_HANDLER_ENCRYPTION ->
getContainerModel().setEncryptionHandlerParameterMap(parametersChild.getParameterMap());
case XML_HANDLER_PASSWORD_STRENGTH ->
getContainerModel().setPasswordStrengthHandlerParameterMap(parametersChild.getParameterMap());
case XML_HANDLER_PERSISTENCE ->
getContainerModel().setPersistenceHandlerParameterMap(parametersChild.getParameterMap());
case XML_HANDLER_USER_CHALLENGE ->
getContainerModel().setUserChallengeHandlerParameterMap(parametersChild.getParameterMap());
case XML_HANDLER_SSO -> getContainerModel().setSsoHandlerParameterMap(parametersChild.getParameterMap());
case XML_HANDLER_PRIVILEGE ->
getContainerModel().setPrivilegeHandlerParameterMap(parametersChild.getParameterMap());
default -> throw new IllegalStateException("Unexpected value: " + this.currentElement);
case CONTAINER -> getContainerModel().setParameterMap(params);
case HANDLER_PRIVILEGE -> getContainerModel().setPrivilegeHandlerParameterMap(params);
case HANDLER_ENCRYPTION -> getContainerModel().setEncryptionHandlerParameterMap(params);
case HANDLER_PASSWORD_STRENGTH ->
getContainerModel().setPasswordStrengthHandlerParameterMap(params);
case HANDLER_PERSISTENCE -> getContainerModel().setPersistenceHandlerParameterMap(params);
case HANDLER_USER_CHALLENGE -> getContainerModel().setUserChallengeHandlerParameterMap(params);
case HANDLER_SSO -> getContainerModel().setSsoHandlerParameterMap(params);
default -> throw new IllegalStateException("Unexpected value: " + this.currentElement);
}
}
}
@ -155,9 +151,9 @@ public class PrivilegeConfigSaxReader extends DefaultHandler {
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) {
if (qName.equals(XML_PARAMETER)) {
String key = attributes.getValue(XML_ATTR_NAME).trim();
String value = attributes.getValue(XML_ATTR_VALUE).trim();
if (qName.equals(PARAMETER)) {
String key = attributes.getValue(ATTR_NAME).trim();
String value = attributes.getValue(ATTR_VALUE).trim();
this.parameterMap.put(key, value);
}
}
@ -176,9 +172,9 @@ public class PrivilegeConfigSaxReader extends DefaultHandler {
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) {
if (qName.equals(XML_POLICY)) {
String policyName = attributes.getValue(XML_ATTR_NAME).trim();
String policyClassName = attributes.getValue(XML_ATTR_CLASS).trim();
if (qName.equals(POLICY)) {
String policyName = attributes.getValue(ATTR_NAME).trim();
String policyClassName = attributes.getValue(ATTR_CLASS).trim();
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;
import java.text.MessageFormat;
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.Privilege;
import li.strolch.privilege.model.internal.Role;
import li.strolch.utils.helper.StringHelper;
import org.slf4j.Logger;
@ -29,6 +24,11 @@ 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>
*/
@ -51,9 +51,13 @@ public class PrivilegeRolesSaxReader extends DefaultHandler {
@Override
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());
} 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());
}
@ -74,9 +78,9 @@ public class PrivilegeRolesSaxReader extends DefaultHandler {
this.buildersStack.peek().endElement(uri, localName, qName);
ElementParser elementParser = null;
if (qName.equals(XmlConstants.XML_ROLE)) {
if (qName.equals(ROLE)) {
elementParser = this.buildersStack.pop();
} else if (qName.equals(XmlConstants.XML_PROPERTIES)) {
} else if (qName.equals(PROPERTIES)) {
elementParser = this.buildersStack.pop();
}
@ -109,7 +113,7 @@ public class PrivilegeRolesSaxReader extends DefaultHandler {
private Set<String> denyList;
private Set<String> allowList;
private Map<String, IPrivilege> privileges;
private Map<String, Privilege> privileges;
public RoleParser() {
init();
@ -134,20 +138,15 @@ public class PrivilegeRolesSaxReader extends DefaultHandler {
this.text = new StringBuilder();
switch (qName) {
case XmlConstants.XML_ROLE:
this.roleName = attributes.getValue(XmlConstants.XML_ATTR_NAME).trim();
break;
case XmlConstants.XML_PRIVILEGE:
this.privilegeName = attributes.getValue(XmlConstants.XML_ATTR_NAME).trim();
this.privilegePolicy = attributes.getValue(XmlConstants.XML_ATTR_POLICY).trim();
break;
case XmlConstants.XML_ALLOW:
case XmlConstants.XML_DENY:
case XmlConstants.XML_ALL_ALLOWED:
case ROLE -> this.roleName = attributes.getValue(ATTR_NAME).trim();
case PRIVILEGE -> {
this.privilegeName = attributes.getValue(ATTR_NAME).trim();
this.privilegePolicy = attributes.getValue(ATTR_POLICY).trim();
}
case ALLOW, DENY, ALL_ALLOWED -> {
}
// 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
public void endElement(String uri, String localName, String qName) {
switch (qName) {
case XmlConstants.XML_ALL_ALLOWED ->
this.allAllowed = StringHelper.parseBoolean(this.text.toString().trim());
case XmlConstants.XML_ALLOW -> this.allowList.add(this.text.toString().trim());
case XmlConstants.XML_DENY -> this.denyList.add(this.text.toString().trim());
case XmlConstants.XML_PRIVILEGE -> {
IPrivilege privilege = new PrivilegeImpl(this.privilegeName, this.privilegePolicy, this.allAllowed,
this.denyList, this.allowList);
this.privileges.put(this.privilegeName, privilege);
this.privilegeName = null;
this.privilegePolicy = null;
this.allAllowed = false;
this.denyList = new HashSet<>();
this.allowList = new HashSet<>();
}
case XmlConstants.XML_ROLE -> {
Role role = new Role(this.roleName, this.privileges);
roles.put(role.getName(), role);
logger.info(MessageFormat.format("New Role: {0}", role));
init();
}
default -> throw new IllegalStateException("Unexpected value: " + qName);
case ALL_ALLOWED -> this.allAllowed = StringHelper.parseBoolean(getText());
case ALLOW -> this.allowList.add(getText());
case DENY -> this.denyList.add(getText());
case PRIVILEGE -> {
Privilege privilege = new Privilege(this.privilegeName, this.privilegePolicy, this.allAllowed,
this.denyList, this.allowList);
this.privileges.put(this.privilegeName, privilege);
this.privilegeName = null;
this.privilegePolicy = null;
this.allAllowed = false;
this.denyList = new HashSet<>();
this.allowList = new HashSet<>();
}
case ROLE -> {
Role role = new Role(this.roleName, this.privileges);
roles.put(role.getName(), role);
logger.info(MessageFormat.format("New Role: {0}", role));
init();
}
default -> throw new IllegalStateException("Unexpected value: " + qName);
}
}
private String getText() {
return this.text.toString().trim();
}
}
static class PropertyParser extends ElementParserAdapter {
@ -194,11 +195,11 @@ public class PrivilegeRolesSaxReader extends DefaultHandler {
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) {
if (qName.equals(XmlConstants.XML_PROPERTY)) {
String key = attributes.getValue(XmlConstants.XML_ATTR_NAME).trim();
String value = attributes.getValue(XmlConstants.XML_ATTR_VALUE).trim();
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(XmlConstants.XML_PROPERTIES)) {
} else if (!qName.equals(PROPERTIES)) {
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;
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.internal.Group;
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.iso8601.ISO8601;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -32,6 +27,11 @@ 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>
*/
@ -58,9 +58,13 @@ public class PrivilegeUsersSaxReader extends DefaultHandler {
@Override
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());
} 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());
}
@ -81,9 +85,9 @@ public class PrivilegeUsersSaxReader extends DefaultHandler {
this.buildersStack.peek().endElement(uri, localName, qName);
ElementParser elementParser = null;
if (qName.equals(XML_USER)) {
if (qName.equals(USER)) {
elementParser = this.buildersStack.pop();
} else if (qName.equals(XML_PROPERTIES)) {
} else if (qName.equals(PROPERTIES)) {
elementParser = this.buildersStack.pop();
}
@ -122,12 +126,14 @@ public class PrivilegeUsersSaxReader extends DefaultHandler {
String lastname;
UserState userState;
Locale locale;
final Set<String> groups;
final Set<String> userRoles;
Map<String, String> parameters;
UserHistory history;
boolean passwordChangeRequested;
public UserParser() {
this.groups = new HashSet<>();
this.userRoles = new HashSet<>();
}
@ -136,15 +142,15 @@ public class PrivilegeUsersSaxReader extends DefaultHandler {
this.text = new StringBuilder();
if (qName.equals(XML_USER)) {
this.userId = attributes.getValue(XML_ATTR_USER_ID).trim();
this.username = attributes.getValue(XML_ATTR_USERNAME).trim();
if (qName.equals(USER)) {
this.userId = attributes.getValue(ATTR_USER_ID).trim();
this.username = attributes.getValue(ATTR_USERNAME).trim();
String password = attributes.getValue(XML_ATTR_PASSWORD);
String salt = attributes.getValue(XML_ATTR_SALT);
String password = attributes.getValue(ATTR_PASSWORD);
String salt = attributes.getValue(ATTR_SALT);
this.passwordCrypt = PasswordCrypt.parse(password, salt);
} else if (qName.equals(XML_HISTORY)) {
this.history = new UserHistory();
} else if (qName.equals(HISTORY)) {
this.history = UserHistory.EMPTY;
}
}
@ -157,40 +163,45 @@ public class PrivilegeUsersSaxReader extends DefaultHandler {
public void endElement(String uri, String localName, String qName) {
switch (qName) {
case XML_FIRSTNAME -> this.firstName = this.text.toString().trim();
case XML_LASTNAME -> this.lastname = this.text.toString().trim();
case XML_STATE -> this.userState = UserState.valueOf(this.text.toString().trim());
case XML_LOCALE -> this.locale = Locale.forLanguageTag(this.text.toString().trim());
case XML_PASSWORD_CHANGE_REQUESTED ->
this.passwordChangeRequested = Boolean.parseBoolean(this.text.toString().trim());
case XML_FIRST_LOGIN -> this.history.setFirstLogin(ISO8601.parseToZdt(this.text.toString().trim()));
case XML_LAST_LOGIN -> this.history.setLastLogin(ISO8601.parseToZdt(this.text.toString().trim()));
case XML_LAST_PASSWORD_CHANGE ->
this.history.setLastPasswordChange(ISO8601.parseToZdt(this.text.toString().trim()));
case XML_ROLE -> this.userRoles.add(this.text.toString().trim());
case XML_USER -> {
case FIRSTNAME -> this.firstName = getText();
case LASTNAME -> this.lastname = getText();
case STATE -> this.userState = UserState.valueOf(getText());
case LOCALE -> this.locale = Locale.forLanguageTag(getText());
case PASSWORD_CHANGE_REQUESTED -> this.passwordChangeRequested = Boolean.parseBoolean(getText());
case FIRST_LOGIN -> this.history = this.history.withFirstLogin(ISO8601.parseToZdt(getText()));
case LAST_LOGIN -> this.history = this.history.withLastLogin(ISO8601.parseToZdt(getText()));
case LAST_PASSWORD_CHANGE ->
this.history = this.history.withLastPasswordChange(ISO8601.parseToZdt(getText()));
case GROUP -> this.groups.add(getText());
case ROLE -> this.userRoles.add(getText());
case USER -> {
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,
this.userState, this.userRoles, this.locale, this.parameters, this.passwordChangeRequested,
this.history);
this.userState, this.groups, this.userRoles, this.locale, this.parameters,
this.passwordChangeRequested, this.history);
logger.info(MessageFormat.format("New User: {0}", user));
String username = caseInsensitiveUsername ? user.getUsername().toLowerCase() : user.getUsername();
users.put(username, user);
}
default -> {
if (!(qName.equals(XML_ROLES) //
|| qName.equals(XML_PARAMETER) //
|| qName.equals(XML_HISTORY) //
|| qName.equals(XML_PARAMETERS))) {
if (!(qName.equals(ROLES) //
|| qName.equals(GROUPS) //
|| qName.equals(PARAMETER) //
|| qName.equals(HISTORY) //
|| 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) {
@ -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.restrictableNull=Restrictable may not be null\!
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.user=User {0} does not have the privilege {1}
Privilege.roleAccessPrivilege.unknownPrivilege=Unhandled privilege {0} for policy {1}

View File

@ -1,12 +1,5 @@
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.handler.PrivilegeHandler;
import li.strolch.privilege.helper.PrivilegeInitializer;
@ -18,6 +11,13 @@ import org.junit.BeforeClass;
import org.slf4j.Logger;
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 {
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) {
try {
File configPath = new File("src/test/resources/config");
File privilegeConfigFile = new File(configPath, configFilename);
File privilegeUsersFile = new File(configPath, usersFilename);
File privilegeGroupsFile = new File(configPath, groupsFilename);
File privilegeRolesFile = new File(configPath, rolesFilename);
File targetPath = new File("target/" + dst);
@ -72,6 +73,7 @@ public class AbstractPrivilegeTest {
File dstConfig = new File(targetPath, configFilename);
File dstUsers = new File(targetPath, usersFilename);
File dstGroups = new File(targetPath, groupsFilename);
File dstRoles = new File(targetPath, rolesFilename);
// write config
@ -81,6 +83,7 @@ public class AbstractPrivilegeTest {
// copy model
Files.copy(privilegeUsersFile.toPath(), dstUsers.toPath());
Files.copy(privilegeGroupsFile.toPath(), dstGroups.toPath());
Files.copy(privilegeRolesFile.toPath(), dstRoles.toPath());
} catch (Exception e) {

View File

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

View File

@ -15,14 +15,16 @@
*/
package li.strolch.privilege.test;
import static org.junit.Assert.*;
import li.strolch.privilege.model.IPrivilege;
import li.strolch.privilege.model.Privilege;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import java.util.Set;
import static org.junit.Assert.*;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*/
@ -32,7 +34,7 @@ public class PrivilegeConflictMergeTest extends AbstractPrivilegeTest {
public static void init() {
removeConfigs(PrivilegeConflictMergeTest.class.getSimpleName());
prepareConfigs(PrivilegeConflictMergeTest.class.getSimpleName(), "PrivilegeConfigMerge.xml",
"PrivilegeUsersMerge.xml", "PrivilegeRolesMerge.xml");
"PrivilegeUsersMerge.xml", "PrivilegeGroupsMerge.xml", "PrivilegeRolesMerge.xml");
}
@AfterClass
@ -49,7 +51,16 @@ public class PrivilegeConflictMergeTest extends AbstractPrivilegeTest {
public void shouldMergePrivileges1() {
try {
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.getAllowList().isEmpty());
assertTrue(privilege.getDenyList().isEmpty());
@ -63,7 +74,16 @@ public class PrivilegeConflictMergeTest extends AbstractPrivilegeTest {
public void shouldMergePrivileges2() {
try {
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());
assertEquals(2, privilege.getAllowList().size());
assertEquals(2, privilege.getDenyList().size());
@ -71,4 +91,53 @@ public class PrivilegeConflictMergeTest extends AbstractPrivilegeTest {
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;
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.InvalidCredentialsException;
import li.strolch.privilege.base.PrivilegeException;
@ -41,9 +35,15 @@ import org.junit.Test;
import org.slf4j.Logger;
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 />
*
* <p>
* TODO add more tests, especially with deny and allow lists
*
* @author Robert von Burg <eitch@eitchnet.ch>
@ -77,12 +77,12 @@ public class PrivilegeTest extends AbstractPrivilegeTest {
public static void init() {
removeConfigs(PrivilegeTest.class.getSimpleName());
prepareConfigs(PrivilegeTest.class.getSimpleName(), "PrivilegeConfig.xml", "PrivilegeUsers.xml",
"PrivilegeRoles.xml");
"PrivilegeGroups.xml", "PrivilegeRoles.xml");
}
@AfterClass
public static void destroy() {
removeConfigs(PrivilegeTest.class.getSimpleName());
//removeConfigs(PrivilegeTest.class.getSimpleName());
}
@Before
@ -137,7 +137,7 @@ public class PrivilegeTest extends AbstractPrivilegeTest {
try {
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();
this.privilegeHandler.addRole(certificate, roleRep);
@ -209,7 +209,9 @@ public class PrivilegeTest extends AbstractPrivilegeTest {
}
});
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("Admin", user.getLastname());
// let's add a new user bob
UserRep userRep = new UserRep(null, ADMIN, "The", "Admin", null, null, null, null, null);
this.privilegeHandler.updateUser(certificate, userRep);
// set new name
user.setFirstname("The");
user.setLastname("Admin");
// update user
this.privilegeHandler.updateUser(certificate, user, null);
user = this.privilegeHandler.getUser(certificate, ADMIN);
assertEquals("The", user.getFirstname());
@ -273,8 +278,9 @@ public class PrivilegeTest extends AbstractPrivilegeTest {
Certificate certificate = this.ctx.getCertificate();
// let's add a new user bob
UserRep userRep = new UserRep(null, BOB, null, null, null, null, null, null, null);
this.privilegeHandler.updateUser(certificate, userRep);
UserRep userRep = new UserRep(BOB, BOB, "Bob", "Anderson", UserState.ENABLED,
Set.of("AppUserLocationA"), null, null, null, null);
this.privilegeHandler.updateUser(certificate, userRep, null);
} finally {
logout();
}
@ -282,25 +288,6 @@ public class PrivilegeTest extends AbstractPrivilegeTest {
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
public void shouldQueryUsers() {
try {
@ -308,7 +295,24 @@ public class PrivilegeTest extends AbstractPrivilegeTest {
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);
assertEquals(1, users.size());
assertEquals(ADMIN, users.get(0).getUsername());
@ -325,8 +329,8 @@ public class PrivilegeTest extends AbstractPrivilegeTest {
Certificate certificate = this.ctx.getCertificate();
UserRep selectorRep = new UserRep(null, null, null, null, null,
new HashSet<>(Collections.singletonList("PrivilegeAdmin")), null, null, null);
UserRep selectorRep = new UserRep(null, null, null, null, null, null, Set.of("PrivilegeAdmin"), null, null,
null);
List<UserRep> users = this.privilegeHandler.queryUsers(certificate, selectorRep);
assertEquals(2, users.size());
assertEquals(ADMIN, users.get(0).getUsername());
@ -344,7 +348,7 @@ public class PrivilegeTest extends AbstractPrivilegeTest {
Certificate certificate = this.ctx.getCertificate();
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);
assertEquals(0, users.size());
@ -360,8 +364,10 @@ public class PrivilegeTest extends AbstractPrivilegeTest {
login(ADMIN, ArraysHelper.copyOf(PASS_ADMIN));
Certificate certificate = this.ctx.getCertificate();
PrivilegeRep privilegeRep = new PrivilegeRep(PrivilegeHandler.PRIVILEGE_ACTION, "DefaultPrivilege",
true, Collections.emptySet(), Collections.emptySet());
this.privilegeHandler.addOrReplacePrivilegeOnRole(certificate, ROLE_APP_USER, privilegeRep);
true, Set.of(), Set.of());
RoleRep role = this.privilegeHandler.getRole(certificate, ROLE_APP_USER);
role.addPrivilege(privilegeRep);
this.privilegeHandler.replaceRole(certificate, role);
} finally {
logout();
}
@ -375,8 +381,10 @@ public class PrivilegeTest extends AbstractPrivilegeTest {
try {
login(ADMIN, ArraysHelper.copyOf(PASS_ADMIN));
Certificate certificate = this.ctx.getCertificate();
this.privilegeHandler.addRoleToUser(certificate, ADMIN, ROLE_MY);
this.privilegeHandler.addRoleToUser(certificate, ADMIN, ROLE_MY2);
UserRep user = this.privilegeHandler.getUser(certificate, ADMIN);
user.addRole(ROLE_MY);
user.addRole(ROLE_MY2);
this.privilegeHandler.updateUser(certificate, user, null);
} finally {
logout();
}
@ -540,15 +548,21 @@ public class PrivilegeTest extends AbstractPrivilegeTest {
login(ADMIN, ArraysHelper.copyOf(PASS_ADMIN));
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,
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();
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);
this.privilegeHandler.persist(certificate);
} finally {
@ -574,7 +588,9 @@ public class PrivilegeTest extends AbstractPrivilegeTest {
// testAddAppRoleToBob
login(ADMIN, ArraysHelper.copyOf(PASS_ADMIN));
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);
this.privilegeHandler.persist(certificate);
} finally {
@ -593,7 +609,10 @@ public class PrivilegeTest extends AbstractPrivilegeTest {
this.ctx.validateAction(restrictable);
fail("Should fail as bob does not have role app");
} 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());
} finally {
logout();
@ -656,7 +675,8 @@ public class PrivilegeTest extends AbstractPrivilegeTest {
// let's add a new user ted
HashSet<String> roles = new HashSet<>();
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();
this.privilegeHandler.addUser(certificate, userRep, null);
logger.info("Added user " + TED);
@ -671,7 +691,9 @@ public class PrivilegeTest extends AbstractPrivilegeTest {
// testAddAdminRoleToBob
login(ADMIN, ArraysHelper.copyOf(PASS_ADMIN));
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);
this.privilegeHandler.persist(certificate);
} finally {
@ -688,14 +710,14 @@ public class PrivilegeTest extends AbstractPrivilegeTest {
// auth as Bob
login(BOB, ArraysHelper.copyOf(PASS_BOB));
// 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);
certificate = this.ctx.getCertificate();
this.privilegeHandler.addUser(certificate, userRep, null);
fail("User bob may not add a user as bob does not have admin rights!");
} catch (PrivilegeException e) {
String msg = MessageFormat.format(PrivilegeMessages.getString("Privilege.noprivilege.user"),
BOB, PrivilegeHandler.PRIVILEGE_ADD_USER);
String msg = MessageFormat.format(PrivilegeMessages.getString("Privilege.noprivilege.user"), BOB,
PrivilegeHandler.PRIVILEGE_ADD_USER);
assertEquals(msg, e.getMessage());
} finally {
logout();
@ -716,7 +738,9 @@ public class PrivilegeTest extends AbstractPrivilegeTest {
// testAddRoleUserToBob
login(ADMIN, ArraysHelper.copyOf(PASS_ADMIN));
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);
logout();
} finally {
@ -728,7 +752,7 @@ public class PrivilegeTest extends AbstractPrivilegeTest {
try {
// add role user
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();
this.privilegeHandler.addRole(certificate, roleRep);
this.privilegeHandler.persist(certificate);
@ -768,8 +792,8 @@ public class PrivilegeTest extends AbstractPrivilegeTest {
login(ADMIN, ArraysHelper.copyOf(PASS_ADMIN));
// let's add a new user bob
UserRep userRep = new UserRep(null, BOB, "Bob", "Newman", UserState.NEW,
new HashSet<>(Collections.singletonList(ROLE_MY)), null, new HashMap<>(), null);
UserRep userRep = new UserRep(null, BOB, "Bob", "Newman", UserState.NEW, Set.of(), Set.of(ROLE_MY), null,
new HashMap<>(), null);
Certificate certificate = this.ctx.getCertificate();
this.privilegeHandler.addUser(certificate, userRep, null);
logger.info("Added user " + BOB);

View File

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

View File

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

View File

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

View File

@ -28,9 +28,10 @@ public class DummySsoHandler implements SingleSignOnHandler {
@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());
Map<String, String> properties = new HashMap<>();
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>
<Parameter name="basePath" value="target/${target}"/>
<Parameter name="usersXmlFile" value="PrivilegeUsersMerge.xml"/>
<Parameter name="groupsXmlFile" value="PrivilegeGroupsMerge.xml"/>
<Parameter name="rolesXmlFile" value="PrivilegeRolesMerge.xml"/>
</Parameters>
</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>
<Role name="PrivilegeAdmin">
<Privilege name="li.strolch.service.api.Service" policy="DefaultPrivilege">
<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">
<Privilege name="GetSession" policy="UserSessionAccessPrivilege">
<AllAllowed>true</AllAllowed>
</Privilege>
<Privilege name="InvalidateSession" policy="UserSessionAccessPrivilege">
<AllAllowed>true</AllAllowed>
</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">
<Allow>Reload</Allow>
<Allow>GetCertificates</Allow>
<Allow>GetPolicies</Allow>
<Allow>Persist</Allow>
<Allow>GetCertificates</Allow>
<Allow>PersistSessions</Allow>
<Allow>Reload</Allow>
</Privilege>
<Privilege name="PrivilegeGetUser" policy="UserAccessPrivilege">
<Privilege name="PrivilegeAddRole" policy="RoleAccessPrivilege">
<AllAllowed>true</AllAllowed>
</Privilege>
<Privilege name="PrivilegeSetUserState" policy="UserAccessPrivilege">
<Deny>SYSTEM</Deny>
<Allow>DISABLED</Allow>
<Allow>ENABLED</Allow>
</Privilege>
<Privilege name="PrivilegeAddRoleToUser" policy="UserAccessPrivilege">
<Privilege name="PrivilegeAddUser" policy="UserAccessPrivilege">
<AllAllowed>true</AllAllowed>
</Privilege>
<Privilege name="PrivilegeGetRole" policy="RoleAccessPrivilege">
<AllAllowed>true</AllAllowed>
</Privilege>
<Privilege name="GetSession" policy="UserSessionAccessPrivilege">
<Privilege name="PrivilegeGetUser" policy="UserAccessPrivilege">
<AllAllowed>true</AllAllowed>
</Privilege>
<Privilege name="PrivilegeModifyRole" policy="RoleAccessPrivilege">
<AllAllowed>true</AllAllowed>
</Privilege>
<Privilege name="PrivilegeModifyUser" policy="UserAccessPrivilege">
@ -65,15 +36,35 @@
<Privilege name="PrivilegeRemoveRole" policy="RoleAccessPrivilege">
<AllAllowed>true</AllAllowed>
</Privilege>
<Privilege name="PrivilegeRemoveRoleFromUser" policy="UserAccessPrivilege">
<Privilege name="PrivilegeRemoveUser" policy="UserAccessPrivilege">
<AllAllowed>true</AllAllowed>
</Privilege>
<Privilege name="PrivilegeModifyRole" policy="RoleAccessPrivilege">
<Privilege name="PrivilegeSetUserLocale" policy="UserAccessPrivilege">
<AllAllowed>true</AllAllowed>
</Privilege>
<Privilege name="PrivilegeAddRole" policy="RoleAccessPrivilege">
<Privilege name="PrivilegeSetUserPassword" policy="UserAccessPrivilege">
<AllAllowed>true</AllAllowed>
</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 name="AppUser">

View File

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

View File

@ -23,4 +23,26 @@
</Roles>
</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>

View File

@ -1,15 +1,5 @@
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.ObserverEvent;
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.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
* 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
public boolean isControlling(Activity activity) {
return this.controllers.containsElement(getDefaultRealm(), activity.getLocator());
return this.controllers.containsElement(getDefaultRealm(), activity.getRootElement().getLocator());
}
@Override
public boolean isControlling(String realm, Activity activity) {
return this.controllers.containsElement(realm, activity.getLocator());
return this.controllers.containsElement(realm, activity.getRootElement().getLocator());
}
@Override
@ -83,12 +83,12 @@ public class EventBasedExecutionHandler extends ExecutionHandler {
@Override
public Controller getController(Activity activity) {
return getController(getDefaultRealm(), activity.getLocator());
return getController(getDefaultRealm(), activity.getRootElement().getLocator());
}
@Override
public Controller getController(String realm, Activity activity) {
return this.controllers.getElement(realm, activity.getLocator());
return this.controllers.getElement(realm, activity.getRootElement().getLocator());
}
@Override
@ -157,7 +157,7 @@ public class EventBasedExecutionHandler extends ExecutionHandler {
synchronized (this.controllers) {
Map<Locator, Controller> map = this.controllers.getMap(realm);
if (map == null) {
logger.error("No controllers for realm " + realm);
logger.info("No controllers for realm " + realm);
return;
}
@ -171,26 +171,28 @@ public class EventBasedExecutionHandler extends ExecutionHandler {
}
@Override
public void addForExecution(Activity activity) {
addForExecution(getDefaultRealm(), activity);
public Controller addForExecution(Activity activity) {
return addForExecution(getDefaultRealm(), activity);
}
@Override
public void addForExecution(String realm, Activity activity) {
public Controller addForExecution(String realm, Activity activity) {
ExecutionHandlerState state = this.statesByRealm.getOrDefault(realm, ExecutionHandlerState.Running);
if (state == ExecutionHandlerState.HaltNew)
throw new IllegalStateException(
"ExecutionHandler state is " + state + ", can not add activities for execution!");
if (this.controllers.containsElement(realm, activity.getLocator()))
throw new IllegalStateException(activity.getLocator() + " is already registered for execution!");
Locator locator = activity.getRootElement().getLocator();
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);
this.controllers.addElement(realm, activity.getLocator(), controller);
this.controllers.addElement(realm, locator, controller);
notifyObserverAdd(controller);
triggerExecution(realm);
return controller;
}
@Override
@ -205,10 +207,11 @@ public class EventBasedExecutionHandler extends ExecutionHandler {
throw new IllegalStateException(
"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) {
controller = newController(realm, activity);
this.controllers.addElement(realm, activity.getLocator(), controller);
this.controllers.addElement(realm, locator, controller);
notifyObserverAdd(controller);
}
@ -296,7 +299,8 @@ public class EventBasedExecutionHandler extends ExecutionHandler {
if (activity.isReadOnly())
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
activity.findActionsDeep(a -> a.getState().inExecutionPhase()).forEach(a -> {
@ -309,7 +313,7 @@ public class EventBasedExecutionHandler extends ExecutionHandler {
// register for execution
Controller controller = newController(realmName, activity);
this.controllers.addElement(realmName, activity.getLocator(), controller);
this.controllers.addElement(realmName, locator, controller);
});
// commit changes to state

View File

@ -219,7 +219,7 @@ public abstract class ExecutionHandler extends StrolchComponent {
* @throws IllegalStateException
* 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
@ -230,7 +230,7 @@ public abstract class ExecutionHandler extends StrolchComponent {
* @param 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

View File

@ -20,8 +20,12 @@ import li.strolch.privilege.model.PrivilegeContext;
import li.strolch.runtime.StrolchConstants;
import li.strolch.runtime.privilege.PrivilegedRunnable;
import li.strolch.runtime.privilege.PrivilegedRunnableWithResult;
import li.strolch.utils.time.PeriodDuration;
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.TimeUnit;
@ -53,6 +57,8 @@ public abstract class ExecutionPolicy extends StrolchPolicy {
protected Locator resourceLoc;
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
* than the actual TX
@ -63,6 +69,7 @@ public abstract class ExecutionPolicy extends StrolchPolicy {
super(tx);
this.tx = tx;
this.realm = tx.getRealmName();
this.futures = new ArrayList<>();
}
/**
@ -225,6 +232,7 @@ public abstract class ExecutionPolicy extends StrolchPolicy {
public void stop() {
this.stopped = true;
try {
this.futures.forEach(future -> future.cancel(false));
handleStopped();
} catch (Exception 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
*/
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);
@ -270,6 +281,17 @@ public abstract class ExecutionPolicy extends StrolchPolicy {
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}
*
@ -278,7 +300,7 @@ public abstract class ExecutionPolicy extends StrolchPolicy {
*/
public void delay(Duration duration, Runnable runnable) {
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;
}
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) {
cancelWarningTask();
addMessage(message);
getExecutionHandler().toWarning(this.realm, message.getLocator());
getExecutionHandler().toWarning(this.realm, this.actionLoc);
}
protected void toExecuted() throws Exception {
@ -126,7 +126,7 @@ public class SimpleExecution extends ExecutionPolicy {
stop();
logger.error("Action " + message.getLocator() + " failed because of: " + message.formatMessage());
addMessage(message);
getExecutionHandler().toError(this.realm, message.getLocator());
getExecutionHandler().toError(this.realm, this.actionLoc);
}
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