[New] Implemented keepAlive of strolch sessions

This commit is contained in:
Robert von Burg 2020-05-11 17:48:38 +02:00
parent 8f645d1af7
commit 5e5289cbc8
19 changed files with 489 additions and 175 deletions

View File

@ -141,15 +141,15 @@ public class DefaultStrolchPrivilegeHandler extends StrolchComponent implements
@Override @Override
public Certificate authenticate(String username, char[] password) { public Certificate authenticate(String username, char[] password) {
assertContainerStarted(); assertContainerStarted();
Certificate certificate = this.privilegeHandler.authenticate(username, password); Certificate certificate = this.privilegeHandler.authenticate(username, password, false);
writeAudit(certificate, LOGIN, AccessType.CREATE, username); writeAudit(certificate, LOGIN, AccessType.CREATE, username);
return certificate; return certificate;
} }
@Override @Override
public Certificate authenticate(String username, char[] password, String source, Usage usage) { public Certificate authenticate(String username, char[] password, String source, Usage usage, boolean keepAlive) {
assertContainerStarted(); assertContainerStarted();
Certificate certificate = this.privilegeHandler.authenticate(username, password, source, usage); Certificate certificate = this.privilegeHandler.authenticate(username, password, source, usage, keepAlive);
writeAudit(certificate, LOGIN, AccessType.CREATE, username); writeAudit(certificate, LOGIN, AccessType.CREATE, username);
return certificate; return certificate;
} }
@ -157,7 +157,7 @@ public class DefaultStrolchPrivilegeHandler extends StrolchComponent implements
@Override @Override
public Certificate authenticateSingleSignOn(Object data) { public Certificate authenticateSingleSignOn(Object data) {
assertContainerStarted(); assertContainerStarted();
Certificate certificate = this.privilegeHandler.authenticateSingleSignOn(data); Certificate certificate = this.privilegeHandler.authenticateSingleSignOn(data, false);
writeAudit(certificate, LOGIN, AccessType.CREATE, certificate.getUsername()); writeAudit(certificate, LOGIN, AccessType.CREATE, certificate.getUsername());
return certificate; return certificate;
} }
@ -165,11 +165,24 @@ public class DefaultStrolchPrivilegeHandler extends StrolchComponent implements
@Override @Override
public Certificate authenticateSingleSignOn(Object data, String source) { public Certificate authenticateSingleSignOn(Object data, String source) {
assertContainerStarted(); assertContainerStarted();
Certificate certificate = this.privilegeHandler.authenticateSingleSignOn(data, source); Certificate certificate = this.privilegeHandler.authenticateSingleSignOn(data, source, false);
writeAudit(certificate, LOGIN, AccessType.CREATE, certificate.getUsername()); writeAudit(certificate, LOGIN, AccessType.CREATE, certificate.getUsername());
return certificate; return certificate;
} }
@Override
public Certificate refreshSession(Certificate certificate, String source) {
assertContainerStarted();
Certificate refreshedCert = this.privilegeHandler.refresh(certificate, source);
writeAudit(refreshedCert, LOGIN, AccessType.CREATE, refreshedCert.getUsername());
return refreshedCert;
}
@Override
public boolean isRefreshAllowed() {
return this.privilegeHandler.isRefreshAllowed();
}
private void writeAudit(Certificate certificate, String login, AccessType accessType, String username) { private void writeAudit(Certificate certificate, String login, AccessType accessType, String username) {
StrolchRealm realm = getContainer().getRealm(certificate); StrolchRealm realm = getContainer().getRealm(certificate);
try (StrolchTransaction tx = realm.openTx(certificate, login, false).silentThreshold(1, NANOSECONDS)) { try (StrolchTransaction tx = realm.openTx(certificate, login, false).silentThreshold(1, NANOSECONDS)) {

View File

@ -45,7 +45,7 @@ public interface PrivilegeHandler {
* *
* @return the certificate * @return the certificate
* *
* @see li.strolch.privilege.handler.PrivilegeHandler#authenticate(String, char[]) * @see li.strolch.privilege.handler.PrivilegeHandler#authenticate(String, char[], boolean)
*/ */
Certificate authenticate(String username, char[] password); Certificate authenticate(String username, char[] password);
@ -60,12 +60,14 @@ public interface PrivilegeHandler {
* the source of the request * the source of the request
* @param usage * @param usage
* the usage for this authentication * the usage for this authentication
* @param keepAlive
* should this session be kept alive
* *
* @return the certificate * @return the certificate
* *
* @see li.strolch.privilege.handler.PrivilegeHandler#authenticate(String, char[]) * @see li.strolch.privilege.handler.PrivilegeHandler#authenticate(String, char[], boolean)
*/ */
Certificate authenticate(String username, char[] password, String source, Usage usage); Certificate authenticate(String username, char[] password, String source, Usage usage, boolean keepAlive);
/** /**
* Authenticates a user on a remote Single Sign On service. This is implemented by the * Authenticates a user on a remote Single Sign On service. This is implemented by the
@ -95,6 +97,25 @@ public interface PrivilegeHandler {
*/ */
Certificate authenticateSingleSignOn(Object data, String source) throws PrivilegeException; Certificate authenticateSingleSignOn(Object data, String source) throws PrivilegeException;
/**
* Performs a refresh of the given certificate's session by returning a new certificate
*
* @param certificate
* the certificate to refresh
* @param source
* the source of the request
*
* @return certificate a new certificate
*/
Certificate refreshSession(Certificate certificate, String source);
/**
* Return true if refreshing sessions is allowed
*
* @return true if refreshing sessions is allowed
*/
boolean isRefreshAllowed();
/** /**
* Returns the {@link PrivilegeContext} for the given certificate * Returns the {@link PrivilegeContext} for the given certificate
* *

View File

@ -22,7 +22,6 @@ import java.io.OutputStream;
import java.nio.file.Files; import java.nio.file.Files;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*; import java.util.*;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -124,9 +123,19 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
*/ */
protected SecretKey secretKey; protected SecretKey secretKey;
/**
* flag if session refreshing is allowed
*/
protected boolean allowSessionRefresh;
protected PrivilegeConflictResolution privilegeConflictResolution; protected PrivilegeConflictResolution privilegeConflictResolution;
private String identifier; private String identifier;
@Override
public boolean isRefreshAllowed() {
return this.allowSessionRefresh;
}
@Override @Override
public EncryptionHandler getEncryptionHandler() throws PrivilegeException { public EncryptionHandler getEncryptionHandler() throws PrivilegeException {
return this.encryptionHandler; return this.encryptionHandler;
@ -1038,7 +1047,7 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
if (ctx.getUserRep().getUsername().equals(newUser.getUsername())) { if (ctx.getUserRep().getUsername().equals(newUser.getUsername())) {
Certificate cert = ctx.getCertificate(); Certificate cert = ctx.getCertificate();
cert = buildCertificate(cert.getUsage(), newUser, cert.getAuthToken(), cert.getSessionId(), cert = buildCertificate(cert.getUsage(), newUser, cert.getAuthToken(), cert.getSessionId(),
cert.getSource(), cert.getLoginTime()); cert.getSource(), cert.getLoginTime(), cert.isKeepAlive());
PrivilegeContext privilegeContext = buildPrivilegeContext(cert, newUser); PrivilegeContext privilegeContext = buildPrivilegeContext(cert, newUser);
this.privilegeContextMap.put(cert.getSessionId(), privilegeContext); this.privilegeContextMap.put(cert.getSessionId(), privilegeContext);
} }
@ -1063,7 +1072,7 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
Certificate cert = ctx.getCertificate(); Certificate cert = ctx.getCertificate();
cert = buildCertificate(cert.getUsage(), user, cert.getAuthToken(), cert.getSessionId(), cert = buildCertificate(cert.getUsage(), user, cert.getAuthToken(), cert.getSessionId(),
cert.getSource(), cert.getLoginTime()); cert.getSource(), cert.getLoginTime(), cert.isKeepAlive());
PrivilegeContext privilegeContext = buildPrivilegeContext(cert, user); PrivilegeContext privilegeContext = buildPrivilegeContext(cert, user);
this.privilegeContextMap.put(cert.getSessionId(), privilegeContext); this.privilegeContextMap.put(cert.getSessionId(), privilegeContext);
} }
@ -1128,7 +1137,7 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
// create a new certificate, with details of the user // create a new certificate, with details of the user
Usage usage = userChallenge.getUsage(); Usage usage = userChallenge.getUsage();
Certificate certificate = buildCertificate(usage, user, authToken, sessionId, userChallenge.getSource(), Certificate certificate = buildCertificate(usage, user, authToken, sessionId, userChallenge.getSource(),
new Date()); LocalDateTime.now(), false);
PrivilegeContext privilegeContext = buildPrivilegeContext(certificate, user); PrivilegeContext privilegeContext = buildPrivilegeContext(certificate, user);
this.privilegeContextMap.put(sessionId, privilegeContext); this.privilegeContextMap.put(sessionId, privilegeContext);
@ -1145,12 +1154,12 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
} }
@Override @Override
public Certificate authenticate(String username, char[] password) { public Certificate authenticate(String username, char[] password, boolean keepAlive) {
return authenticate(username, password, "unknown", Usage.ANY); return authenticate(username, password, "unknown", Usage.ANY, keepAlive);
} }
@Override @Override
public Certificate authenticate(String username, char[] password, String source, Usage usage) { public Certificate authenticate(String username, char[] password, String source, Usage usage, boolean keepAlive) {
DBC.PRE.assertNotEmpty("source must not be empty!", source); DBC.PRE.assertNotEmpty("source must not be empty!", source);
try { try {
@ -1178,7 +1187,8 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
String sessionId = UUID.randomUUID().toString(); String sessionId = UUID.randomUUID().toString();
// create a new certificate, with details of the user // create a new certificate, with details of the user
Certificate certificate = buildCertificate(usage, user, authToken, sessionId, source, new Date()); Certificate certificate = buildCertificate(usage, user, authToken, sessionId, source, LocalDateTime.now(),
keepAlive);
PrivilegeContext privilegeContext = buildPrivilegeContext(certificate, user); PrivilegeContext privilegeContext = buildPrivilegeContext(certificate, user);
this.privilegeContextMap.put(sessionId, privilegeContext); this.privilegeContextMap.put(sessionId, privilegeContext);
@ -1204,12 +1214,13 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
} }
@Override @Override
public Certificate authenticateSingleSignOn(Object data) throws PrivilegeException { public Certificate authenticateSingleSignOn(Object data, boolean keepAlive) throws PrivilegeException {
return authenticateSingleSignOn(data, "unknown"); return authenticateSingleSignOn(data, "unknown", keepAlive);
} }
@Override @Override
public Certificate authenticateSingleSignOn(Object data, String source) throws PrivilegeException { public Certificate authenticateSingleSignOn(Object data, String source, boolean keepAlive)
throws PrivilegeException {
DBC.PRE.assertNotEmpty("source must not be empty!", source); DBC.PRE.assertNotEmpty("source must not be empty!", source);
if (this.ssoHandler == null) if (this.ssoHandler == null)
throw new IllegalStateException("The SSO Handler is not configured!"); throw new IllegalStateException("The SSO Handler is not configured!");
@ -1234,7 +1245,8 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
String sessionId = UUID.randomUUID().toString(); String sessionId = UUID.randomUUID().toString();
// create a new certificate, with details of the user // create a new certificate, with details of the user
Certificate certificate = buildCertificate(Usage.ANY, user, authToken, sessionId, source, new Date()); Certificate certificate = buildCertificate(Usage.ANY, user, authToken, sessionId, source, LocalDateTime.now(),
keepAlive);
PrivilegeContext privilegeContext = buildPrivilegeContext(certificate, user); PrivilegeContext privilegeContext = buildPrivilegeContext(certificate, user);
this.privilegeContextMap.put(sessionId, privilegeContext); this.privilegeContextMap.put(sessionId, privilegeContext);
@ -1247,12 +1259,69 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
return certificate; return certificate;
} }
@Override
public Certificate refresh(Certificate certificate, String source) throws AccessDeniedException {
DBC.PRE.assertNotNull("certificate must not be null!", certificate);
try {
// username must be at least 2 characters in length
if (!this.allowSessionRefresh)
throw new AccessDeniedException("Refreshing of sessions not allowed!");
validate(certificate);
if (!certificate.isKeepAlive())
throw new AccessDeniedException("Refreshing of session not allowed!");
if (!certificate.getSource().equals(source)) {
logger.error("Source of existing session {} is not the same as the refresh request's source {}",
certificate.getSource(), source);
}
// check the password
User user = this.persistenceHandler.getUser(certificate.getUsername());
// get 2 auth tokens
String authToken = this.encryptionHandler.nextToken();
// get next session id
String sessionId = UUID.randomUUID().toString();
// create a new certificate, with details of the user
Certificate refreshedCert = buildCertificate(certificate.getUsage(), user, authToken, sessionId, source,
LocalDateTime.now(), true);
PrivilegeContext privilegeContext = buildPrivilegeContext(refreshedCert, user);
this.privilegeContextMap.put(sessionId, privilegeContext);
// invalidate the previous session
invalidate(certificate);
persistSessions();
// log
logger.info(MessageFormat
.format("User {0} refreshed session: {1}", user.getUsername(), refreshedCert)); //$NON-NLS-1$
// return the certificate
return refreshedCert;
} catch (PrivilegeException e) {
throw e;
} catch (RuntimeException e) {
logger.error(e.getMessage(), e);
String msg = "User {0} failed to refresh session: {1}"; //$NON-NLS-1$
msg = MessageFormat.format(msg, certificate.getUsername(), e.getMessage());
throw new PrivilegeException(msg, e);
}
}
private Certificate buildCertificate(Usage usage, User user, String authToken, String sessionId, String source, private Certificate buildCertificate(Usage usage, User user, String authToken, String sessionId, String source,
Date loginTime) { LocalDateTime loginTime, boolean keepAlive) {
DBC.PRE.assertNotEmpty("source must not be empty!", source); DBC.PRE.assertNotEmpty("source must not be empty!", source);
Set<String> userRoles = user.getRoles(); Set<String> userRoles = user.getRoles();
return new Certificate(usage, sessionId, user.getUsername(), user.getFirstname(), user.getLastname(), return new Certificate(usage, sessionId, user.getUsername(), user.getFirstname(), user.getLastname(),
user.getUserState(), authToken, source, loginTime, user.getLocale(), userRoles, user.getUserState(), authToken, source, loginTime, keepAlive, user.getLocale(), userRoles,
new HashMap<>(user.getProperties())); new HashMap<>(user.getProperties()));
} }
@ -1341,7 +1410,8 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
} }
// create a new certificate, with details of the user // create a new certificate, with details of the user
Certificate certificate = buildCertificate(usage, user, authToken, sessionId, source, stub.getLoginTime()); Certificate certificate = buildCertificate(usage, user, authToken, sessionId, source, stub.getLoginTime(),
stub.isKeepAlive());
certificate.setLocale(stub.getLocale()); certificate.setLocale(stub.getLocale());
certificate.setLastAccess(stub.getLastAccess()); certificate.setLastAccess(stub.getLastAccess());
@ -1538,7 +1608,8 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
PrivilegeContext privilegeContext = this.privilegeContextMap.remove(certificate.getSessionId()); PrivilegeContext privilegeContext = this.privilegeContextMap.remove(certificate.getSessionId());
// persist sessions // persist sessions
persistSessions(); if (privilegeContext != null)
persistSessions();
// return true if object was really removed // return true if object was really removed
boolean loggedOut = privilegeContext != null; boolean loggedOut = privilegeContext != null;
@ -1592,7 +1663,7 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
throw new PrivilegeException(msg); throw new PrivilegeException(msg);
} }
certificate.setLastAccess(new Date()); certificate.setLastAccess(LocalDateTime.now());
if (!certificate.getSource().equals(this.identifier)) if (!certificate.getSource().equals(this.identifier))
throw new IllegalStateException( throw new IllegalStateException(
@ -1624,15 +1695,14 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
// validate that challenge certificate is not expired (1 hour only) // validate that challenge certificate is not expired (1 hour only)
if (sessionCertificate.getUsage() != Usage.ANY) { if (sessionCertificate.getUsage() != Usage.ANY) {
LocalDateTime dateTime = LocalDateTime LocalDateTime dateTime = sessionCertificate.getLoginTime();
.ofInstant(sessionCertificate.getLoginTime().toInstant(), ZoneId.systemDefault());
if (dateTime.plusHours(1).isBefore(LocalDateTime.now())) { if (dateTime.plusHours(1).isBefore(LocalDateTime.now())) {
invalidate(sessionCertificate); invalidate(sessionCertificate);
throw new NotAuthenticatedException("Certificate has already expired!"); //$NON-NLS-1$ throw new NotAuthenticatedException("Certificate has already expired!"); //$NON-NLS-1$
} }
} }
certificate.setLastAccess(new Date()); certificate.setLastAccess(LocalDateTime.now());
// TODO decide if we want to assert source did not change! // TODO decide if we want to assert source did not change!
if (!source.equals(SOURCE_UNKNOWN) && !certificate.getSource().equals(source)) { if (!source.equals(SOURCE_UNKNOWN) && !certificate.getSource().equals(source)) {
@ -1728,6 +1798,8 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
handleConflictResolutionParam(parameterMap); handleConflictResolutionParam(parameterMap);
handleSecretParams(parameterMap); handleSecretParams(parameterMap);
this.allowSessionRefresh = Boolean.parseBoolean(parameterMap.get(PARAM_ALLOW_SESSION_REFRESH));
// validate policies on privileges of Roles // validate policies on privileges of Roles
for (Role role : persistenceHandler.getAllRoles()) { for (Role role : persistenceHandler.getAllRoles()) {
validatePolicies(role); validatePolicies(role);
@ -1938,11 +2010,8 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
* the char array containing the passwort which is to be set to zeroes * the char array containing the passwort which is to be set to zeroes
*/ */
private void clearPassword(char[] password) { private void clearPassword(char[] password) {
if (password != null) { if (password != null)
for (int i = 0; i < password.length; i++) { Arrays.fill(password, (char) 0);
password[i] = 0;
}
}
} }
@Override @Override
@ -2054,7 +2123,7 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
// create a new certificate, with details of the user // create a new certificate, with details of the user
Certificate systemUserCertificate = buildCertificate(Usage.ANY, user, authToken, sessionId, this.identifier, Certificate systemUserCertificate = buildCertificate(Usage.ANY, user, authToken, sessionId, this.identifier,
new Date()); LocalDateTime.now(), false);
// create and save a new privilege context // create and save a new privilege context
PrivilegeContext privilegeContext = buildPrivilegeContext(systemUserCertificate, user); PrivilegeContext privilegeContext = buildPrivilegeContext(systemUserCertificate, user);

View File

@ -166,6 +166,11 @@ public interface PrivilegeHandler {
*/ */
String PARAM_SECRET_KEY = "secretKey"; //$NON-NLS-1$ String PARAM_SECRET_KEY = "secretKey"; //$NON-NLS-1$
/**
* configuration parameter to define if session refreshing is allowed
*/
String PARAM_ALLOW_SESSION_REFRESH = "allowSessionRefresh"; //$NON-NLS-1$
/** /**
* configuration parameter to define a secret salt * configuration parameter to define a secret salt
*/ */
@ -608,13 +613,15 @@ public interface PrivilegeHandler {
* @param password * @param password
* the password with which this user is to be authenticated. Null passwords are not accepted and they must meet * 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(char[])}-method * the requirements of the {@link #validatePassword(char[])}-method
* @param keepAlive
* should this session be kept alive
* *
* @return a {@link Certificate} with which this user may then perform actions * @return a {@link Certificate} with which this user may then perform actions
* *
* @throws AccessDeniedException * @throws AccessDeniedException
* if the user credentials are not valid * if the user credentials are not valid
*/ */
Certificate authenticate(String username, char[] password) throws AccessDeniedException; Certificate authenticate(String username, char[] password, boolean keepAlive) throws AccessDeniedException;
/** /**
* Authenticates a user by validating that a {@link User} for the given username and password exist and then returns * Authenticates a user by validating that a {@link User} for the given username and password exist and then returns
@ -629,26 +636,31 @@ public interface PrivilegeHandler {
* the source of the authentication request, i.e. remote IP * the source of the authentication request, i.e. remote IP
* @param usage * @param usage
* the usage type for this authentication * the usage type for this authentication
* @param keepAlive
* should this session be kept alive
* *
* @return a {@link Certificate} with which this user may then perform actions * @return a {@link Certificate} with which this user may then perform actions
* *
* @throws AccessDeniedException * @throws AccessDeniedException
* if the user credentials are not valid * if the user credentials are not valid
*/ */
Certificate authenticate(String username, char[] password, String source, Usage usage) throws AccessDeniedException; Certificate authenticate(String username, char[] password, String source, Usage usage, boolean keepAlive)
throws AccessDeniedException;
/** /**
* Authenticates a user on a remote Single Sign On service. This is implemented by the * Authenticates a user on a remote Single Sign On service. This is implemented by the
* *
* @param data * @param data
* the data to perform the SSO * the data to perform the SSO
* @param keepAlive
* should this session be kept alive
* *
* @return the {@link Certificate} for the user * @return the {@link Certificate} for the user
* *
* @throws PrivilegeException * @throws PrivilegeException
* if something goes wrong with the SSO * if something goes wrong with the SSO
*/ */
Certificate authenticateSingleSignOn(Object data) throws PrivilegeException; Certificate authenticateSingleSignOn(Object data, boolean keepAlive) throws PrivilegeException;
/** /**
* Authenticates a user on a remote Single Sign On service. This is implemented by the * Authenticates a user on a remote Single Sign On service. This is implemented by the
@ -657,13 +669,37 @@ public interface PrivilegeHandler {
* the data to perform the SSO * the data to perform the SSO
* @param source * @param source
* the source of the SSO authentication * the source of the SSO authentication
* @param keepAlive
* may the certificate be kept alive
* *
* @return the {@link Certificate} for the user * @return the {@link Certificate} for the user
* *
* @throws PrivilegeException * @throws PrivilegeException
* if something goes wrong with the SSO * if something goes wrong with the SSO
*/ */
Certificate authenticateSingleSignOn(Object data, String source) throws PrivilegeException; Certificate authenticateSingleSignOn(Object data, String source, boolean keepAlive) throws PrivilegeException;
/**
* Refreshes the given certificate's session with a new session, i.e. a new certificate
*
* @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
*/
Certificate refresh(Certificate certificate, String source) throws AccessDeniedException;
/**
* Return true if refreshing sessions is allowed
*
* @return true if refreshing sessions is allowed
*/
boolean isRefreshAllowed();
/** /**
* Invalidates the session for the given {@link Certificate}, effectively logging out the user who was authenticated * Invalidates the session for the given {@link Certificate}, effectively logging out the user who was authenticated

View File

@ -183,6 +183,11 @@ public class XmlConstants {
*/ */
public static final String 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" : * XML_ATTR_LAST_ACCESS = "lastAccess" :
*/ */

View File

@ -18,7 +18,11 @@ package li.strolch.privilege.model;
import static li.strolch.privilege.base.PrivilegeConstants.*; import static li.strolch.privilege.base.PrivilegeConstants.*;
import java.io.Serializable; import java.io.Serializable;
import java.util.*; import java.time.LocalDateTime;
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.PrivilegeConstants;
import li.strolch.privilege.base.PrivilegeException; import li.strolch.privilege.base.PrivilegeException;
@ -29,7 +33,7 @@ import li.strolch.utils.helper.StringHelper;
/** /**
* The {@link Certificate} is the object a client keeps when accessing a Privilege enabled system. This object is the * The {@link Certificate} is the object a client keeps when accessing a Privilege enabled system. This object is the
* instance which is always used when performing an access and is returned when a user performs a login through {@link * instance which is always used when performing an access and is returned when a user performs a login through {@link
* PrivilegeHandler#authenticate(String, char[])} * PrivilegeHandler#authenticate(String, char[], boolean)}
* *
* @author Robert von Burg <eitch@eitchnet.ch> * @author Robert von Burg <eitch@eitchnet.ch>
*/ */
@ -43,13 +47,14 @@ public final class Certificate implements Serializable {
private final UserState userState; private final UserState userState;
private final String authToken; private final String authToken;
private final String source; private final String source;
private final Date loginTime; private final LocalDateTime loginTime;
private final boolean keepAlive;
private final Set<String> userRoles; private final Set<String> userRoles;
private final Map<String, String> propertyMap; private final Map<String, String> propertyMap;
private Locale locale; private Locale locale;
private Date lastAccess; private LocalDateTime lastAccess;
/** /**
* Default constructor initializing with all information needed for this certificate * Default constructor initializing with all information needed for this certificate
@ -65,9 +70,9 @@ public final class Certificate implements Serializable {
* the users session id * the users session id
* @param username * @param username
* the users login name * the users login name
* @param firstname * @param firstName
* the users first name * the users first name
* @param lastname * @param lastName
* the users last name * the users last name
* @param authToken * @param authToken
* the authentication token defining the users unique session and is a private field of this certificate. * the authentication token defining the users unique session and is a private field of this certificate.
@ -79,9 +84,9 @@ public final class Certificate implements Serializable {
* a {@link Map} containing string value pairs of properties for the logged in user. These properties can be * 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 * edited and can be used for the user to change settings of this session
*/ */
public Certificate(Usage usage, String sessionId, String username, String firstname, String lastname, public Certificate(Usage usage, String sessionId, String username, String firstName, String lastName,
UserState userState, String authToken, String source, Date loginTime, Locale locale, Set<String> userRoles, UserState userState, String authToken, String source, LocalDateTime loginTime, boolean keepAlive,
Map<String, String> propertyMap) { Locale locale, Set<String> userRoles, Map<String, String> propertyMap) {
// validate arguments are not null // validate arguments are not null
if (StringHelper.isEmpty(sessionId)) { if (StringHelper.isEmpty(sessionId)) {
@ -106,12 +111,13 @@ public final class Certificate implements Serializable {
this.usage = usage; this.usage = usage;
this.sessionId = sessionId; this.sessionId = sessionId;
this.username = username; this.username = username;
this.firstname = firstname; this.firstname = firstName;
this.lastname = lastname; this.lastname = lastName;
this.userState = userState; this.userState = userState;
this.authToken = authToken; this.authToken = authToken;
this.source = source; this.source = source;
this.loginTime = loginTime; this.loginTime = loginTime;
this.keepAlive = keepAlive;
// if no locale is given, set default // if no locale is given, set default
if (locale == null) if (locale == null)
@ -125,7 +131,7 @@ public final class Certificate implements Serializable {
this.propertyMap = Collections.unmodifiableMap(propertyMap); this.propertyMap = Collections.unmodifiableMap(propertyMap);
this.userRoles = Collections.unmodifiableSet(userRoles); this.userRoles = Collections.unmodifiableSet(userRoles);
this.lastAccess = new Date(); this.lastAccess = LocalDateTime.now();
} }
public Usage getUsage() { public Usage getUsage() {
@ -224,10 +230,14 @@ public final class Certificate implements Serializable {
return userState; return userState;
} }
public Date getLoginTime() { public LocalDateTime getLoginTime() {
return this.loginTime; return this.loginTime;
} }
public boolean isKeepAlive() {
return this.keepAlive;
}
public String getAuthToken() { public String getAuthToken() {
return this.authToken; return this.authToken;
} }
@ -236,11 +246,11 @@ public final class Certificate implements Serializable {
return this.source; return this.source;
} }
public Date getLastAccess() { public LocalDateTime getLastAccess() {
return this.lastAccess; return this.lastAccess;
} }
public void setLastAccess(Date lastAccess) { public void setLastAccess(LocalDateTime lastAccess) {
this.lastAccess = lastAccess; this.lastAccess = lastAccess;
} }

View File

@ -9,8 +9,8 @@ public class UserChallenge {
private final String challenge; private final String challenge;
private final String source; private final String source;
private final LocalDateTime initiated; private final LocalDateTime initiated;
private final Usage usage;
private boolean fulfilled; private boolean fulfilled;
private Usage usage;
public UserChallenge(Usage usage, User user, String challenge, String source) { public UserChallenge(Usage usage, User user, String challenge, String source) {
this.usage = usage; this.usage = usage;

View File

@ -15,13 +15,15 @@
*/ */
package li.strolch.privilege.xml; package li.strolch.privilege.xml;
import static java.util.Comparator.comparing;
import static li.strolch.privilege.helper.XmlConstants.*;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.List; import java.util.List;
import li.strolch.privilege.helper.XmlConstants;
import li.strolch.privilege.model.Certificate; import li.strolch.privilege.model.Certificate;
import li.strolch.utils.helper.XmlHelper; import li.strolch.utils.helper.XmlHelper;
import li.strolch.utils.iso8601.ISO8601FormatFactory; import li.strolch.utils.iso8601.ISO8601;
import org.w3c.dom.Document; import org.w3c.dom.Document;
import org.w3c.dom.Element; import org.w3c.dom.Element;
@ -30,8 +32,8 @@ import org.w3c.dom.Element;
*/ */
public class CertificateStubsDomWriter { public class CertificateStubsDomWriter {
private List<Certificate> certificates; private final List<Certificate> certificates;
private OutputStream outputStream; private final OutputStream outputStream;
public CertificateStubsDomWriter(List<Certificate> certificates, OutputStream outputStream) { public CertificateStubsDomWriter(List<Certificate> certificates, OutputStream outputStream) {
this.certificates = certificates; this.certificates = certificates;
@ -42,40 +44,41 @@ public class CertificateStubsDomWriter {
// create document root // create document root
Document doc = XmlHelper.createDocument(); Document doc = XmlHelper.createDocument();
Element rootElement = doc.createElement(XmlConstants.XML_ROOT_CERTIFICATES); Element rootElement = doc.createElement(XML_ROOT_CERTIFICATES);
doc.appendChild(rootElement); doc.appendChild(rootElement);
this.certificates.stream().sorted((c1, c2) -> c1.getSessionId().compareTo(c2.getSessionId())).forEach(cert -> { this.certificates.stream().sorted(comparing(Certificate::getSessionId)).forEach(cert -> {
// create the certificate element // create the certificate element
Element certElement = doc.createElement(XmlConstants.XML_CERTIFICATE); Element certElement = doc.createElement(XML_CERTIFICATE);
rootElement.appendChild(certElement); rootElement.appendChild(certElement);
// sessionId; // sessionId;
certElement.setAttribute(XmlConstants.XML_ATTR_SESSION_ID, cert.getSessionId()); certElement.setAttribute(XML_ATTR_SESSION_ID, cert.getSessionId());
// usage; // usage;
certElement.setAttribute(XmlConstants.XML_ATTR_USAGE, cert.getUsage().name()); certElement.setAttribute(XML_ATTR_USAGE, cert.getUsage().name());
// username; // username;
certElement.setAttribute(XmlConstants.XML_ATTR_USERNAME, cert.getUsername()); certElement.setAttribute(XML_ATTR_USERNAME, cert.getUsername());
// authToken; // authToken;
certElement.setAttribute(XmlConstants.XML_ATTR_AUTH_TOKEN, cert.getAuthToken()); certElement.setAttribute(XML_ATTR_AUTH_TOKEN, cert.getAuthToken());
// source; // source;
certElement.setAttribute(XmlConstants.XML_ATTR_SOURCE, cert.getSource()); certElement.setAttribute(XML_ATTR_SOURCE, cert.getSource());
// locale; // locale;
certElement.setAttribute(XmlConstants.XML_ATTR_LOCALE, cert.getLocale().toLanguageTag()); certElement.setAttribute(XML_ATTR_LOCALE, cert.getLocale().toLanguageTag());
// loginTime; // loginTime;
certElement.setAttribute(XmlConstants.XML_ATTR_LOGIN_TIME, certElement.setAttribute(XML_ATTR_LOGIN_TIME, ISO8601.toString(cert.getLoginTime()));
ISO8601FormatFactory.getInstance().formatDate(cert.getLoginTime()));
// lastAccess; // lastAccess;
certElement.setAttribute(XmlConstants.XML_ATTR_LAST_ACCESS, certElement.setAttribute(XML_ATTR_LAST_ACCESS, ISO8601.toString(cert.getLastAccess()));
ISO8601FormatFactory.getInstance().formatDate(cert.getLastAccess()));
// keepAlive;
certElement.setAttribute(XML_ATTR_KEEP_ALIVE, String.valueOf(cert.isKeepAlive()));
}); });
// write the container file to disk // write the container file to disk

View File

@ -16,20 +16,20 @@
package li.strolch.privilege.xml; package li.strolch.privilege.xml;
import static li.strolch.privilege.handler.DefaultPrivilegeHandler.SOURCE_UNKNOWN; 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 static li.strolch.utils.helper.StringHelper.isEmpty;
import java.io.InputStream; import java.io.InputStream;
import java.time.LocalDateTime;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import li.strolch.privilege.base.PrivilegeException; import li.strolch.privilege.base.PrivilegeException;
import li.strolch.privilege.helper.XmlConstants;
import li.strolch.privilege.model.Usage; import li.strolch.privilege.model.Usage;
import li.strolch.utils.dbc.DBC; import li.strolch.utils.dbc.DBC;
import li.strolch.utils.helper.XmlHelper; import li.strolch.utils.helper.XmlHelper;
import li.strolch.utils.iso8601.ISO8601FormatFactory; import li.strolch.utils.iso8601.ISO8601;
import org.xml.sax.Attributes; import org.xml.sax.Attributes;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler; import org.xml.sax.helpers.DefaultHandler;
@ -39,7 +39,7 @@ import org.xml.sax.helpers.DefaultHandler;
*/ */
public class CertificateStubsSaxReader extends DefaultHandler { public class CertificateStubsSaxReader extends DefaultHandler {
private InputStream inputStream; private final InputStream inputStream;
private List<CertificateStub> stubs; private List<CertificateStub> stubs;
public CertificateStubsSaxReader(InputStream inputStream) { public CertificateStubsSaxReader(InputStream inputStream) {
@ -56,21 +56,20 @@ public class CertificateStubsSaxReader extends DefaultHandler {
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
switch (qName) { switch (qName) {
case XmlConstants.XML_ROOT_CERTIFICATES: case XML_ROOT_CERTIFICATES:
break; break;
case XmlConstants.XML_CERTIFICATE: case XML_CERTIFICATE:
CertificateStub stub = new CertificateStub(); CertificateStub stub = new CertificateStub();
stub.usage = Usage.valueOf(attributes.getValue(XmlConstants.XML_ATTR_USAGE)); stub.usage = Usage.valueOf(attributes.getValue(XML_ATTR_USAGE));
stub.sessionId = attributes.getValue(XmlConstants.XML_ATTR_SESSION_ID); stub.sessionId = attributes.getValue(XML_ATTR_SESSION_ID);
stub.username = attributes.getValue(XmlConstants.XML_ATTR_USERNAME); stub.username = attributes.getValue(XML_ATTR_USERNAME);
stub.authToken = attributes.getValue(XmlConstants.XML_ATTR_AUTH_TOKEN); stub.authToken = attributes.getValue(XML_ATTR_AUTH_TOKEN);
stub.source = attributes.getValue(XmlConstants.XML_ATTR_SOURCE); stub.source = attributes.getValue(XML_ATTR_SOURCE);
stub.locale = Locale.forLanguageTag(attributes.getValue(XmlConstants.XML_ATTR_LOCALE)); stub.locale = Locale.forLanguageTag(attributes.getValue(XML_ATTR_LOCALE));
stub.loginTime = ISO8601FormatFactory.getInstance() stub.loginTime = ISO8601.parseToZdt(attributes.getValue(XML_ATTR_LOGIN_TIME)).toLocalDateTime();
.parseDate(attributes.getValue(XmlConstants.XML_ATTR_LOGIN_TIME)); stub.lastAccess = ISO8601.parseToZdt(attributes.getValue(XML_ATTR_LAST_ACCESS)).toLocalDateTime();
stub.lastAccess = ISO8601FormatFactory.getInstance() stub.keepAlive = Boolean.parseBoolean(attributes.getValue(XML_ATTR_KEEP_ALIVE));
.parseDate(attributes.getValue(XmlConstants.XML_ATTR_LAST_ACCESS));
DBC.INTERIM.assertNotEmpty("sessionId missing on sessions data!", stub.sessionId); DBC.INTERIM.assertNotEmpty("sessionId missing on sessions data!", stub.sessionId);
DBC.INTERIM.assertNotEmpty("username missing on sessions data!", stub.username); DBC.INTERIM.assertNotEmpty("username missing on sessions data!", stub.username);
@ -87,15 +86,16 @@ public class CertificateStubsSaxReader extends DefaultHandler {
} }
} }
public class CertificateStub { public static class CertificateStub {
private Usage usage; private Usage usage;
private String sessionId; private String sessionId;
private String username; private String username;
private String authToken; private String authToken;
private String source; private String source;
private Locale locale; private Locale locale;
private Date loginTime; private LocalDateTime loginTime;
private Date lastAccess; private LocalDateTime lastAccess;
private boolean keepAlive;
public Usage getUsage() { public Usage getUsage() {
return this.usage; return this.usage;
@ -121,12 +121,16 @@ public class CertificateStubsSaxReader extends DefaultHandler {
return locale; return locale;
} }
public Date getLoginTime() { public LocalDateTime getLoginTime() {
return loginTime; return loginTime;
} }
public Date getLastAccess() { public LocalDateTime getLastAccess() {
return lastAccess; return lastAccess;
} }
public boolean isKeepAlive() {
return this.keepAlive;
}
} }
} }

View File

@ -22,7 +22,7 @@ public class AbstractPrivilegeTest {
protected PrivilegeContext ctx; protected PrivilegeContext ctx;
protected void login(String username, char[] password) { protected void login(String username, char[] password) {
Certificate certificate = privilegeHandler.authenticate(username, password); Certificate certificate = privilegeHandler.authenticate(username, password, false);
assertTrue("Certificate is null!", certificate != null); assertTrue("Certificate is null!", certificate != null);
this.ctx = privilegeHandler.validate(certificate); this.ctx = privilegeHandler.validate(certificate);
} }

View File

@ -390,7 +390,7 @@ public class PrivilegeTest extends AbstractPrivilegeTest {
} }
// change password back // change password back
certificate = this.privilegeHandler.authenticate(ADMIN, PASS_TED); certificate = this.privilegeHandler.authenticate(ADMIN, PASS_TED, false);
this.privilegeHandler.setUserPassword(certificate, ADMIN, ArraysHelper.copyOf(PASS_ADMIN)); this.privilegeHandler.setUserPassword(certificate, ADMIN, ArraysHelper.copyOf(PASS_ADMIN));
this.privilegeHandler.invalidate(certificate); this.privilegeHandler.invalidate(certificate);
} }
@ -742,7 +742,7 @@ public class PrivilegeTest extends AbstractPrivilegeTest {
try { try {
// testFailAuthAsBob // testFailAuthAsBob
// Will fail because user bob is not yet enabled // Will fail because user bob is not yet enabled
this.privilegeHandler.authenticate(BOB, ArraysHelper.copyOf(PASS_BOB)); this.privilegeHandler.authenticate(BOB, ArraysHelper.copyOf(PASS_BOB), false);
fail("User Bob may not authenticate because the user is not yet enabled!"); fail("User Bob may not authenticate because the user is not yet enabled!");
} catch (PrivilegeException e) { } catch (PrivilegeException e) {
String msg = "User bob does not have state ENABLED and can not login!"; String msg = "User bob does not have state ENABLED and can not login!";

View File

@ -42,7 +42,7 @@ public class SsoHandlerTest extends AbstractPrivilegeTest {
data.put("roles", "PrivilegeAdmin, AppUser"); data.put("roles", "PrivilegeAdmin, AppUser");
// auth // auth
Certificate cert = this.privilegeHandler.authenticateSingleSignOn(data); Certificate cert = this.privilegeHandler.authenticateSingleSignOn(data, false);
this.ctx = this.privilegeHandler.validate(cert); this.ctx = this.privilegeHandler.validate(cert);
// validate action // validate action

View File

@ -19,9 +19,7 @@ import static li.strolch.runtime.StrolchConstants.StrolchPrivilegeConstants.PRIV
import static li.strolch.runtime.StrolchConstants.StrolchPrivilegeConstants.PRIVILEGE_INVALIDATE_SESSION; import static li.strolch.runtime.StrolchConstants.StrolchPrivilegeConstants.PRIVILEGE_INVALIDATE_SESSION;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.time.Instant;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit; import java.time.temporal.ChronoUnit;
import java.util.*; import java.util.*;
import java.util.concurrent.Future; import java.util.concurrent.Future;
@ -51,13 +49,15 @@ import org.slf4j.LoggerFactory;
public class DefaultStrolchSessionHandler extends StrolchComponent implements StrolchSessionHandler { public class DefaultStrolchSessionHandler extends StrolchComponent implements StrolchSessionHandler {
public static final String PARAM_SESSION_TTL_MINUTES = "session.ttl.minutes"; //$NON-NLS-1$ public static final String PARAM_SESSION_TTL_MINUTES = "session.ttl.minutes"; //$NON-NLS-1$
public static final String PARAM_SESSION_MAX_KEEP_ALIVE_MINUTES = "session.maxKeepAlive.minutes"; //$NON-NLS-1$
public static final String PARAM_SESSION_RELOAD_SESSIONS = "session.reload"; //$NON-NLS-1$ public static final String PARAM_SESSION_RELOAD_SESSIONS = "session.reload"; //$NON-NLS-1$
private static final Logger logger = LoggerFactory.getLogger(DefaultStrolchSessionHandler.class); private static final Logger logger = LoggerFactory.getLogger(DefaultStrolchSessionHandler.class);
private PrivilegeHandler privilegeHandler; private PrivilegeHandler privilegeHandler;
private Map<String, Certificate> certificateMap; private Map<String, Certificate> certificateMap;
private boolean reloadSessions; private boolean reloadSessions;
private long sessionTtl; private int sessionTtlMinutes;
private int maxKeepAliveMinutes;
private ScheduledFuture<?> validateSessionsTask; private ScheduledFuture<?> validateSessionsTask;
private Future<?> persistSessionsTask; private Future<?> persistSessionsTask;
@ -66,9 +66,26 @@ public class DefaultStrolchSessionHandler extends StrolchComponent implements St
super(container, componentName); super(container, componentName);
} }
@Override
public int getSessionTtlMinutes() {
return this.sessionTtlMinutes;
}
@Override
public int getSessionMaxKeepAliveMinutes() {
return this.maxKeepAliveMinutes;
}
@Override
public boolean isRefreshAllowed() {
return this.privilegeHandler.isRefreshAllowed();
}
@Override @Override
public void initialize(ComponentConfiguration configuration) throws Exception { public void initialize(ComponentConfiguration configuration) throws Exception {
this.sessionTtl = TimeUnit.MINUTES.toMillis(configuration.getInt(PARAM_SESSION_TTL_MINUTES, 30)); this.sessionTtlMinutes = configuration.getInt(PARAM_SESSION_TTL_MINUTES, 30);
this.maxKeepAliveMinutes = configuration
.getInt(PARAM_SESSION_MAX_KEEP_ALIVE_MINUTES, Math.max(this.sessionTtlMinutes, 30));
this.reloadSessions = configuration.getBoolean(PARAM_SESSION_RELOAD_SESSIONS, false); this.reloadSessions = configuration.getBoolean(PARAM_SESSION_RELOAD_SESSIONS, false);
super.initialize(configuration); super.initialize(configuration);
} }
@ -133,24 +150,11 @@ public class DefaultStrolchSessionHandler extends StrolchComponent implements St
} }
@Override @Override
public Certificate authenticate(String username, char[] password) { public Certificate authenticate(String username, char[] password, String source, Usage usage, boolean keepAlive) {
DBC.PRE.assertNotEmpty("Username must be set!", username); //$NON-NLS-1$ DBC.PRE.assertNotEmpty("Username must be set!", username); //$NON-NLS-1$
DBC.PRE.assertNotNull("Passwort must be set", password); //$NON-NLS-1$ DBC.PRE.assertNotNull("Passwort must be set", password); //$NON-NLS-1$
Certificate certificate = this.privilegeHandler.authenticate(username, password); Certificate certificate = this.privilegeHandler.authenticate(username, password, source, usage, keepAlive);
this.certificateMap.put(certificate.getAuthToken(), certificate);
logger.info(MessageFormat.format("{0} sessions currently active.", this.certificateMap.size())); //$NON-NLS-1$
return certificate;
}
@Override
public Certificate authenticate(String username, char[] password, String source, Usage usage) {
DBC.PRE.assertNotEmpty("Username must be set!", username); //$NON-NLS-1$
DBC.PRE.assertNotNull("Passwort must be set", password); //$NON-NLS-1$
Certificate certificate = this.privilegeHandler.authenticate(username, password, source, usage);
this.certificateMap.put(certificate.getAuthToken(), certificate); this.certificateMap.put(certificate.getAuthToken(), certificate);
logger.info(MessageFormat.format("{0} sessions currently active.", this.certificateMap.size())); //$NON-NLS-1$ logger.info(MessageFormat.format("{0} sessions currently active.", this.certificateMap.size())); //$NON-NLS-1$
@ -178,6 +182,17 @@ public class DefaultStrolchSessionHandler extends StrolchComponent implements St
return certificate; return certificate;
} }
@Override
public Certificate refreshSession(Certificate certificate, String source) {
Certificate refreshedSession = this.privilegeHandler.refreshSession(certificate, source);
invalidate(certificate);
this.certificateMap.put(refreshedSession.getAuthToken(), refreshedSession);
logger.info(MessageFormat.format("{0} sessions currently active.", this.certificateMap.size())); //$NON-NLS-1$
return refreshedSession;
}
@Override @Override
public Certificate validate(String authToken) throws StrolchNotAuthenticatedException { public Certificate validate(String authToken) throws StrolchNotAuthenticatedException {
DBC.PRE.assertNotEmpty("authToken must be set!", authToken); //$NON-NLS-1$ DBC.PRE.assertNotEmpty("authToken must be set!", authToken); //$NON-NLS-1$
@ -298,15 +313,24 @@ public class DefaultStrolchSessionHandler extends StrolchComponent implements St
certificateMap = new HashMap<>(this.certificateMap); certificateMap = new HashMap<>(this.certificateMap);
} }
LocalDateTime timeOutTime = LocalDateTime.now().minus(sessionTtl, ChronoUnit.MILLIS); LocalDateTime maxKeepAliveTime = LocalDateTime.now().minus(this.maxKeepAliveMinutes, ChronoUnit.MINUTES);
ZoneId systemDefault = ZoneId.systemDefault(); LocalDateTime timeOutTime = LocalDateTime.now().minus(this.sessionTtlMinutes, ChronoUnit.MINUTES);
for (Certificate certificate : certificateMap.values()) { for (Certificate certificate : certificateMap.values()) {
Instant lastAccess = certificate.getLastAccess().toInstant(); if (certificate.isKeepAlive()) {
if (timeOutTime.isAfter(LocalDateTime.ofInstant(lastAccess, systemDefault))) {
String msg = "Session {0} for user {1} has expired, invalidating session..."; //$NON-NLS-1$ if (maxKeepAliveTime.isAfter(certificate.getLoginTime())) {
logger.info(MessageFormat.format(msg, certificate.getSessionId(), certificate.getUsername())); String msg = "KeepAlive for session {0} for user {1} has expired, invalidating session..."; //$NON-NLS-1$
sessionTimeout(certificate); logger.info(MessageFormat.format(msg, certificate.getSessionId(), certificate.getUsername()));
sessionTimeout(certificate);
}
} else {
if (timeOutTime.isAfter(certificate.getLastAccess())) {
String msg = "Session {0} for user {1} has expired, invalidating session..."; //$NON-NLS-1$
logger.info(MessageFormat.format(msg, certificate.getSessionId(), certificate.getUsername()));
sessionTimeout(certificate);
}
} }
} }
} }

View File

@ -25,6 +25,7 @@ public class StrolchRestfulConstants {
public static final String STROLCH_CERTIFICATE = "strolch.certificate"; //$NON-NLS-1$ public static final String STROLCH_CERTIFICATE = "strolch.certificate"; //$NON-NLS-1$
public static final String STROLCH_REQUEST_SOURCE= "strolch.requestSource"; //$NON-NLS-1$ public static final String STROLCH_REQUEST_SOURCE= "strolch.requestSource"; //$NON-NLS-1$
public static final String STROLCH_AUTHORIZATION = "strolch.authorization"; //$NON-NLS-1$ public static final String STROLCH_AUTHORIZATION = "strolch.authorization"; //$NON-NLS-1$
public static final String STROLCH_AUTHORIZATION_EXPIRATION_DATE = "strolch.authorization.expirationDate"; //$NON-NLS-1$
public static final String MSG = "msg"; public static final String MSG = "msg";
public static final String EXCEPTION_MSG = "exceptionMsg"; public static final String EXCEPTION_MSG = "exceptionMsg";

View File

@ -35,16 +35,25 @@ import li.strolch.rest.model.UserSession;
public interface StrolchSessionHandler { public interface StrolchSessionHandler {
/** /**
* Authenticates a user with the given credentials * Returns the time to live for a session in minutes
* *
* @param username * @return the time to live for a session in minutes
* the username
* @param password
* the password
*
* @return the {@link Certificate} for the logged in user
*/ */
Certificate authenticate(String username, char[] password); int getSessionTtlMinutes();
/**
* Returns the max keep alive for a session in minutes
*
* @return the max keep alive for a session in minutes
*/
int getSessionMaxKeepAliveMinutes();
/**
* Return true if refreshing sessions is allowed
*
* @return true if refreshing sessions is allowed
*/
boolean isRefreshAllowed();
/** /**
* Authenticates a user with the given credentials * Authenticates a user with the given credentials
@ -57,10 +66,12 @@ public interface StrolchSessionHandler {
* the source of the request * the source of the request
* @param usage * @param usage
* the usage for this authentication * the usage for this authentication
* @param keepAlive
* should the session have a keepAlive
* *
* @return the {@link Certificate} for the logged in user * @return the {@link Certificate} for the logged in user
*/ */
Certificate authenticate(String username, char[] password, String source, Usage usage); Certificate authenticate(String username, char[] password, String source, Usage usage, boolean keepAlive);
/** /**
* Performs a single-sign-on with the given data, if SSO is enabled * Performs a single-sign-on with the given data, if SSO is enabled
@ -84,6 +95,18 @@ public interface StrolchSessionHandler {
*/ */
Certificate authenticateSingleSignOn(Object data, String source); Certificate authenticateSingleSignOn(Object data, String source);
/**
* Performs a refresh of the given certificate's session by returning a new certificate
*
* @param certificate
* the certificate to refresh
* @param source
* the source of the request
*
* @return certificate a new certificate
*/
Certificate refreshSession(Certificate certificate, String source);
/** /**
* Validates that a {@link Certificate} exists with the given auth token and is still valid * Validates that a {@link Certificate} exists with the given auth token and is still valid
* *

View File

@ -15,6 +15,8 @@
*/ */
package li.strolch.rest.endpoint; package li.strolch.rest.endpoint;
import static li.strolch.rest.StrolchRestfulConstants.STROLCH_AUTHORIZATION;
import static li.strolch.rest.StrolchRestfulConstants.STROLCH_AUTHORIZATION_EXPIRATION_DATE;
import static li.strolch.rest.filters.AuthenticationRequestFilter.getRemoteIp; import static li.strolch.rest.filters.AuthenticationRequestFilter.getRemoteIp;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -22,6 +24,7 @@ import javax.ws.rs.*;
import javax.ws.rs.core.*; import javax.ws.rs.core.*;
import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.Response.Status;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.time.LocalDateTime;
import java.util.Base64; import java.util.Base64;
import java.util.Set; import java.util.Set;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -36,11 +39,11 @@ import li.strolch.privilege.model.IPrivilege;
import li.strolch.privilege.model.PrivilegeContext; import li.strolch.privilege.model.PrivilegeContext;
import li.strolch.privilege.model.Usage; import li.strolch.privilege.model.Usage;
import li.strolch.rest.RestfulStrolchComponent; import li.strolch.rest.RestfulStrolchComponent;
import li.strolch.rest.StrolchRestfulConstants;
import li.strolch.rest.StrolchSessionHandler; import li.strolch.rest.StrolchSessionHandler;
import li.strolch.rest.helper.ResponseUtil; import li.strolch.rest.helper.ResponseUtil;
import li.strolch.runtime.privilege.PrivilegeHandler; import li.strolch.runtime.privilege.PrivilegeHandler;
import li.strolch.utils.helper.ExceptionHelper; import li.strolch.utils.helper.ExceptionHelper;
import li.strolch.utils.iso8601.ISO8601;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -58,12 +61,12 @@ public class AuthenticationService {
public Response authenticate(@Context HttpServletRequest request, @Context HttpHeaders headers, String data) { public Response authenticate(@Context HttpServletRequest request, @Context HttpHeaders headers, String data) {
JsonObject login = new JsonParser().parse(data).getAsJsonObject(); JsonObject login = new JsonParser().parse(data).getAsJsonObject();
JsonObject loginResult = new JsonObject();
try { try {
if (!login.has("username") || login.get("username").getAsString().length() < 2) { if (!login.has("username") || login.get("username").getAsString().length() < 2) {
logger.error("Authentication failed: Username was not given or is too short!"); logger.error("Authentication failed: Username was not given or is too short!");
JsonObject loginResult = new JsonObject();
loginResult.addProperty("msg", MessageFormat.format("Could not log in due to: {0}", loginResult.addProperty("msg", MessageFormat.format("Could not log in due to: {0}",
"Username was not given or is too short!")); //$NON-NLS-2$ "Username was not given or is too short!")); //$NON-NLS-2$
return Response.status(Status.BAD_REQUEST).entity(loginResult.toString()).build(); return Response.status(Status.BAD_REQUEST).entity(loginResult.toString()).build();
@ -71,6 +74,7 @@ public class AuthenticationService {
if (!login.has("password") || login.get("password").getAsString().length() < 3) { if (!login.has("password") || login.get("password").getAsString().length() < 3) {
logger.error("Authentication failed: Password was not given or is too short!"); logger.error("Authentication failed: Password was not given or is too short!");
JsonObject loginResult = new JsonObject();
loginResult.addProperty("msg", MessageFormat.format("Could not log in due to: {0}", loginResult.addProperty("msg", MessageFormat.format("Could not log in due to: {0}",
"Password was not given or is too short!")); //$NON-NLS-2$ "Password was not given or is too short!")); //$NON-NLS-2$
return Response.status(Status.BAD_REQUEST).entity(loginResult.toString()).build(); return Response.status(Status.BAD_REQUEST).entity(loginResult.toString()).build();
@ -78,12 +82,14 @@ public class AuthenticationService {
String username = login.get("username").getAsString(); String username = login.get("username").getAsString();
String passwordEncoded = login.get("password").getAsString(); String passwordEncoded = login.get("password").getAsString();
boolean keepAlive = login.has("keepAlive") && login.get("keepAlive").getAsBoolean();
byte[] decode = Base64.getDecoder().decode(passwordEncoded); byte[] decode = Base64.getDecoder().decode(passwordEncoded);
char[] password = new String(decode).toCharArray(); char[] password = new String(decode).toCharArray();
if (password.length < 3) { if (password.length < 3) {
logger.error("Authentication failed: Password was not given or is too short!"); logger.error("Authentication failed: Password was not given or is too short!");
JsonObject loginResult = new JsonObject();
loginResult.addProperty("msg", MessageFormat.format("Could not log in due to: {0}", loginResult.addProperty("msg", MessageFormat.format("Could not log in due to: {0}",
"Password was not given or is too short!")); //$NON-NLS-2$ "Password was not given or is too short!")); //$NON-NLS-2$
return Response.status(Status.BAD_REQUEST).entity(loginResult.toString()).build(); return Response.status(Status.BAD_REQUEST).entity(loginResult.toString()).build();
@ -91,27 +97,31 @@ public class AuthenticationService {
StrolchSessionHandler sessionHandler = RestfulStrolchComponent.getInstance().getSessionHandler(); StrolchSessionHandler sessionHandler = RestfulStrolchComponent.getInstance().getSessionHandler();
String source = getRemoteIp(request); String source = getRemoteIp(request);
Certificate certificate = sessionHandler.authenticate(username, password, source, Usage.ANY); Certificate certificate = sessionHandler.authenticate(username, password, source, Usage.ANY, keepAlive);
return getAuthenticationResponse(request, loginResult, certificate, source); return getAuthenticationResponse(request, certificate, source, true);
} catch (InvalidCredentialsException e) { } catch (InvalidCredentialsException e) {
logger.error("Authentication failed due to: " + e.getMessage()); logger.error("Authentication failed due to: " + e.getMessage());
JsonObject loginResult = new JsonObject();
loginResult.addProperty("msg", "Could not log in as the given credentials are invalid"); //$NON-NLS-1$ loginResult.addProperty("msg", "Could not log in as the given credentials are invalid"); //$NON-NLS-1$
return Response.status(Status.UNAUTHORIZED).entity(loginResult.toString()).build(); return Response.status(Status.UNAUTHORIZED).entity(loginResult.toString()).build();
} catch (AccessDeniedException e) { } catch (AccessDeniedException e) {
logger.error("Authentication failed due to: " + e.getMessage()); logger.error("Authentication failed due to: " + e.getMessage());
JsonObject loginResult = new JsonObject();
loginResult.addProperty("msg", loginResult.addProperty("msg",
MessageFormat.format("Could not log in due to: {0}", e.getMessage())); //$NON-NLS-2$ MessageFormat.format("Could not log in due to: {0}", e.getMessage())); //$NON-NLS-2$
return Response.status(Status.UNAUTHORIZED).entity(loginResult.toString()).build(); return Response.status(Status.UNAUTHORIZED).entity(loginResult.toString()).build();
} catch (StrolchException | PrivilegeException e) { } catch (StrolchException | PrivilegeException e) {
logger.error(e.getMessage(), e); logger.error(e.getMessage(), e);
JsonObject loginResult = new JsonObject();
loginResult.addProperty("msg", loginResult.addProperty("msg",
MessageFormat.format("Could not log in due to: {0}", e.getMessage())); //$NON-NLS-2$ MessageFormat.format("Could not log in due to: {0}", e.getMessage())); //$NON-NLS-2$
return Response.status(Status.FORBIDDEN).entity(loginResult.toString()).build(); return Response.status(Status.FORBIDDEN).entity(loginResult.toString()).build();
} catch (Exception e) { } catch (Exception e) {
logger.error(e.getMessage(), e); logger.error(e.getMessage(), e);
String msg = e.getMessage(); String msg = e.getMessage();
JsonObject loginResult = new JsonObject();
loginResult.addProperty("msg", MessageFormat.format("{0}: {1}", e.getClass().getName(), msg)); //$NON-NLS-1$ loginResult.addProperty("msg", MessageFormat.format("{0}: {1}", e.getClass().getName(), msg)); //$NON-NLS-1$
return Response.serverError().entity(loginResult.toString()).build(); return Response.serverError().entity(loginResult.toString()).build();
} }
@ -122,33 +132,35 @@ public class AuthenticationService {
@Path("sso") @Path("sso")
public Response authenticateSingleSignOn(@Context HttpServletRequest request, @Context HttpHeaders headers) { public Response authenticateSingleSignOn(@Context HttpServletRequest request, @Context HttpHeaders headers) {
JsonObject loginResult = new JsonObject();
try { try {
StrolchSessionHandler sessionHandler = RestfulStrolchComponent.getInstance().getSessionHandler(); StrolchSessionHandler sessionHandler = RestfulStrolchComponent.getInstance().getSessionHandler();
String source = getRemoteIp(request); String source = getRemoteIp(request);
Certificate certificate = sessionHandler.authenticateSingleSignOn(request.getUserPrincipal(), source); Certificate certificate = sessionHandler.authenticateSingleSignOn(request.getUserPrincipal(), source);
return getAuthenticationResponse(request, loginResult, certificate, source); return getAuthenticationResponse(request, certificate, source, true);
} catch (InvalidCredentialsException e) { } catch (InvalidCredentialsException e) {
logger.error("Authentication failed due to: " + e.getMessage()); logger.error("Authentication failed due to: " + e.getMessage());
JsonObject loginResult = new JsonObject();
loginResult.addProperty("msg", "Could not log in as the given credentials are invalid"); //$NON-NLS-1$ loginResult.addProperty("msg", "Could not log in as the given credentials are invalid"); //$NON-NLS-1$
return Response.status(Status.UNAUTHORIZED).entity(loginResult.toString()).build(); return Response.status(Status.UNAUTHORIZED).entity(loginResult.toString()).build();
} catch (AccessDeniedException e) { } catch (AccessDeniedException e) {
logger.error("Authentication failed due to: " + e.getMessage()); logger.error("Authentication failed due to: " + e.getMessage());
JsonObject loginResult = new JsonObject();
loginResult.addProperty("msg", loginResult.addProperty("msg",
MessageFormat.format("Could not log in due to: {0}", e.getMessage())); //$NON-NLS-2$ MessageFormat.format("Could not log in due to: {0}", e.getMessage())); //$NON-NLS-2$
return Response.status(Status.UNAUTHORIZED).entity(loginResult.toString()).build(); return Response.status(Status.UNAUTHORIZED).entity(loginResult.toString()).build();
} catch (StrolchException | PrivilegeException e) { } catch (StrolchException | PrivilegeException e) {
logger.error(e.getMessage(), e); logger.error(e.getMessage(), e);
JsonObject loginResult = new JsonObject();
loginResult.addProperty("msg", loginResult.addProperty("msg",
MessageFormat.format("Could not log in due to: {0}", e.getMessage())); //$NON-NLS-2$ MessageFormat.format("Could not log in due to: {0}", e.getMessage())); //$NON-NLS-2$
return Response.status(Status.FORBIDDEN).entity(loginResult.toString()).build(); return Response.status(Status.FORBIDDEN).entity(loginResult.toString()).build();
} catch (Exception e) { } catch (Exception e) {
logger.error(e.getMessage(), e); logger.error(e.getMessage(), e);
String msg = e.getMessage(); String msg = e.getMessage();
JsonObject loginResult = new JsonObject();
loginResult.addProperty("msg", MessageFormat.format("{0}: {1}", e.getClass().getName(), msg)); //$NON-NLS-1$ loginResult.addProperty("msg", MessageFormat.format("{0}: {1}", e.getClass().getName(), msg)); //$NON-NLS-1$
return Response.serverError().entity(loginResult.toString()).build(); return Response.serverError().entity(loginResult.toString()).build();
} }
@ -219,6 +231,68 @@ public class AuthenticationService {
} }
} }
@GET
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Path("{authToken}")
public Response getValidatedSession(@Context HttpServletRequest request, @PathParam("authToken") String authToken) {
try {
StrolchSessionHandler sessionHandler = RestfulStrolchComponent.getInstance().getSessionHandler();
String source = getRemoteIp(request);
Certificate certificate = sessionHandler.validate(authToken, source);
return getAuthenticationResponse(request, certificate, source, false);
} catch (StrolchException | PrivilegeException e) {
logger.error("Session validation failed: " + e.getMessage());
JsonObject root = new JsonObject();
root.addProperty("msg", MessageFormat.format("Session invalid: {0}", e.getMessage()));
String json = new Gson().toJson(root);
return Response.status(Status.UNAUTHORIZED).entity(json).build();
} catch (Exception e) {
logger.error(e.getMessage(), e);
String msg = e.getMessage();
JsonObject root = new JsonObject();
root.addProperty("msg", MessageFormat.format("Session invalid: {0}: {1}", e.getClass().getName(), msg));
String json = new Gson().toJson(root);
return Response.serverError().entity(json).build();
}
}
@PUT
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Path("{authToken}")
public Response refreshSession(@Context HttpServletRequest request, @PathParam("authToken") String authToken) {
try {
StrolchSessionHandler sessionHandler = RestfulStrolchComponent.getInstance().getSessionHandler();
String source = getRemoteIp(request);
Certificate certificate = sessionHandler.validate(authToken, source);
Certificate refreshedCert = sessionHandler.refreshSession(certificate, source);
return getAuthenticationResponse(request, refreshedCert, source, true);
} catch (StrolchException | PrivilegeException e) {
logger.error("Session validation failed: " + e.getMessage());
JsonObject root = new JsonObject();
root.addProperty("msg", MessageFormat.format("Session invalid: {0}", e.getMessage()));
String json = new Gson().toJson(root);
return Response.status(Status.UNAUTHORIZED).entity(json).build();
} catch (Exception e) {
logger.error(e.getMessage(), e);
String msg = e.getMessage();
JsonObject root = new JsonObject();
root.addProperty("msg", MessageFormat.format("Session invalid: {0}: {1}", e.getClass().getName(), msg));
String json = new Gson().toJson(root);
return Response.serverError().entity(json).build();
}
}
@POST @POST
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Path("challenge") @Path("challenge")
@ -268,8 +342,8 @@ public class AuthenticationService {
logger.warn(msg); logger.warn(msg);
} }
NewCookie cookie = new NewCookie(StrolchRestfulConstants.STROLCH_AUTHORIZATION, certificate.getAuthToken(), NewCookie cookie = new NewCookie(STROLCH_AUTHORIZATION, certificate.getAuthToken(), "/", null,
"/", null, "Authorization header", (int) TimeUnit.DAYS.toSeconds(1), secureCookie); "Authorization header", (int) TimeUnit.DAYS.toSeconds(1), secureCookie);
return Response.ok().entity(jsonObject.toString())// return Response.ok().entity(jsonObject.toString())//
.header(HttpHeaders.AUTHORIZATION, certificate.getAuthToken()).cookie(cookie).build(); .header(HttpHeaders.AUTHORIZATION, certificate.getAuthToken()).cookie(cookie).build();
@ -283,17 +357,37 @@ public class AuthenticationService {
} }
} }
private Response getAuthenticationResponse(HttpServletRequest request, JsonObject loginResult, private Response getAuthenticationResponse(HttpServletRequest request, Certificate certificate, String source,
Certificate certificate, String source) { boolean setCookies) {
StrolchSessionHandler sessionHandler = RestfulStrolchComponent.getInstance().getSessionHandler();
int sessionMaxKeepAliveMinutes = sessionHandler.getSessionMaxKeepAliveMinutes();
int cookieMaxAge;
if (certificate.isKeepAlive()) {
cookieMaxAge = (int) TimeUnit.MINUTES.toSeconds(sessionMaxKeepAliveMinutes);
} else {
cookieMaxAge = RestfulStrolchComponent.getInstance().getCookieMaxAge();
}
LocalDateTime expirationDate = LocalDateTime.now().plusSeconds(cookieMaxAge);
String expirationDateS = ISO8601.toString(expirationDate);
JsonObject loginResult = new JsonObject();
PrivilegeHandler privilegeHandler = RestfulStrolchComponent.getInstance().getContainer().getPrivilegeHandler(); PrivilegeHandler privilegeHandler = RestfulStrolchComponent.getInstance().getContainer().getPrivilegeHandler();
PrivilegeContext privilegeContext = privilegeHandler.validate(certificate, source); PrivilegeContext privilegeContext = privilegeHandler.validate(certificate, source);
loginResult.addProperty("sessionId", certificate.getSessionId()); loginResult.addProperty("sessionId", certificate.getSessionId());
loginResult.addProperty("authToken", certificate.getAuthToken()); String authToken = certificate.getAuthToken();
loginResult.addProperty("authToken", authToken);
loginResult.addProperty("username", certificate.getUsername()); loginResult.addProperty("username", certificate.getUsername());
loginResult.addProperty("firstname", certificate.getFirstname()); loginResult.addProperty("firstname", certificate.getFirstname());
loginResult.addProperty("lastname", certificate.getLastname()); loginResult.addProperty("lastname", certificate.getLastname());
loginResult.addProperty("locale", certificate.getLocale().toLanguageTag()); loginResult.addProperty("locale", certificate.getLocale().toLanguageTag());
loginResult.addProperty("keepAlive", certificate.isKeepAlive());
loginResult.addProperty("keepAliveMinutes", sessionMaxKeepAliveMinutes);
loginResult.addProperty("cookieMaxAge", cookieMaxAge);
loginResult.addProperty("authorizationExpiration", expirationDateS);
loginResult.addProperty("refreshAllowed", sessionHandler.isRefreshAllowed());
if (!certificate.getPropertyMap().isEmpty()) { if (!certificate.getPropertyMap().isEmpty()) {
JsonObject propObj = new JsonObject(); JsonObject propObj = new JsonObject();
@ -336,15 +430,24 @@ public class AuthenticationService {
} }
boolean secureCookie = RestfulStrolchComponent.getInstance().isSecureCookie(); boolean secureCookie = RestfulStrolchComponent.getInstance().isSecureCookie();
int cookieMaxAge = RestfulStrolchComponent.getInstance().getCookieMaxAge();
if (secureCookie && !request.getScheme().equals("https")) { if (secureCookie && !request.getScheme().equals("https")) {
logger.error( logger.error(
"Authorization cookie is secure, but connection is not secure! Cookie won't be passed to client!"); "Authorization cookie is secure, but connection is not secure! Cookie won't be passed to client!");
} }
NewCookie cookie = new NewCookie(StrolchRestfulConstants.STROLCH_AUTHORIZATION, certificate.getAuthToken(), "/",
null, "Authorization header", cookieMaxAge, secureCookie);
return Response.ok().entity(loginResult.toString())// if (setCookies) {
.header(HttpHeaders.AUTHORIZATION, certificate.getAuthToken()).cookie(cookie).build(); NewCookie authCookie = new NewCookie(STROLCH_AUTHORIZATION, authToken, "/", null, "Authorization header",
cookieMaxAge, secureCookie);
NewCookie authExpirationCookie = new NewCookie(STROLCH_AUTHORIZATION_EXPIRATION_DATE, expirationDateS, "/",
null, "Authorization Expiration Date", cookieMaxAge, secureCookie);
return Response.ok().entity(loginResult.toString()) //
.header(HttpHeaders.AUTHORIZATION, authToken) //
.cookie(authCookie) //
.cookie(authExpirationCookie) //
.build();
}
return Response.ok().entity(loginResult.toString()).header(HttpHeaders.AUTHORIZATION, authToken).build();
} }
} }

View File

@ -205,7 +205,7 @@ public class AuthenticationRequestFilter implements ContainerRequestFilter {
logger.info("Performing basic auth for user " + parts[0] + "..."); logger.info("Performing basic auth for user " + parts[0] + "...");
StrolchSessionHandler sessionHandler = RestfulStrolchComponent.getInstance().getSessionHandler(); StrolchSessionHandler sessionHandler = RestfulStrolchComponent.getInstance().getSessionHandler();
Certificate certificate = sessionHandler.authenticate(parts[0], parts[1].toCharArray(), remoteIp, Usage.SINGLE); Certificate certificate = sessionHandler.authenticate(parts[0], parts[1].toCharArray(), remoteIp, Usage.SINGLE, false);
requestContext.setProperty(STROLCH_CERTIFICATE, certificate); requestContext.setProperty(STROLCH_CERTIFICATE, certificate);
requestContext.setProperty(STROLCH_REQUEST_SOURCE, remoteIp); requestContext.setProperty(STROLCH_REQUEST_SOURCE, remoteIp);

View File

@ -15,36 +15,38 @@
*/ */
package li.strolch.rest.model; package li.strolch.rest.model;
import java.util.Date; import java.time.LocalDateTime;
import java.util.Locale; import java.util.Locale;
import java.util.Set; import java.util.Set;
import com.google.gson.JsonArray; import com.google.gson.JsonArray;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import li.strolch.privilege.model.Certificate; import li.strolch.privilege.model.Certificate;
import li.strolch.utils.iso8601.ISO8601FormatFactory; import li.strolch.utils.iso8601.ISO8601;
public class UserSession { public class UserSession {
private String sessionId; private final boolean keepAlive;
private Date loginTime; private final String sessionId;
private String username; private final LocalDateTime loginTime;
private String firstname; private final String username;
private String lastname; private final String firstName;
private String source; private final String lastName;
private Set<String> userRoles; private final String source;
private Locale locale; private final Set<String> userRoles;
private Date lastAccess; private final Locale locale;
private final LocalDateTime lastAccess;
public UserSession(Certificate certificate) { public UserSession(Certificate certificate) {
this.sessionId = certificate.getSessionId(); this.sessionId = certificate.getSessionId();
this.loginTime = certificate.getLoginTime(); this.loginTime = certificate.getLoginTime();
this.username = certificate.getUsername(); this.username = certificate.getUsername();
this.firstname = certificate.getFirstname(); this.firstName = certificate.getFirstname();
this.lastname = certificate.getLastname(); this.lastName = certificate.getLastname();
this.source = certificate.getSource(); this.source = certificate.getSource();
this.userRoles = certificate.getUserRoles(); this.userRoles = certificate.getUserRoles();
this.locale = certificate.getLocale(); this.locale = certificate.getLocale();
this.keepAlive = certificate.isKeepAlive();
this.lastAccess = certificate.getLastAccess(); this.lastAccess = certificate.getLastAccess();
} }
@ -52,7 +54,7 @@ public class UserSession {
return locale; return locale;
} }
public Date getLastAccess() { public LocalDateTime getLastAccess() {
return lastAccess; return lastAccess;
} }
@ -60,7 +62,7 @@ public class UserSession {
return sessionId; return sessionId;
} }
public Date getLoginTime() { public LocalDateTime getLoginTime() {
return loginTime; return loginTime;
} }
@ -69,11 +71,11 @@ public class UserSession {
} }
public String getFirstname() { public String getFirstname() {
return firstname; return firstName;
} }
public String getLastname() { public String getLastname() {
return lastname; return lastName;
} }
public String getSource() { public String getSource() {
@ -88,14 +90,15 @@ public class UserSession {
JsonObject jsonObject = new JsonObject(); JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("sessionId", this.sessionId); jsonObject.addProperty("sessionId", this.sessionId);
jsonObject.addProperty("loginTime", ISO8601FormatFactory.getInstance().formatDate(this.loginTime)); jsonObject.addProperty("loginTime", ISO8601.toString(this.loginTime));
jsonObject.addProperty("username", this.username); jsonObject.addProperty("username", this.username);
jsonObject.addProperty("firstname", this.firstname); jsonObject.addProperty("firstname", this.firstName);
jsonObject.addProperty("lastname", this.lastname); jsonObject.addProperty("lastname", this.lastName);
jsonObject.addProperty("source", this.source); jsonObject.addProperty("source", this.source);
jsonObject.addProperty("keepAlive", this.keepAlive);
jsonObject.addProperty("locale", this.locale.toString()); jsonObject.addProperty("locale", this.locale.toString());
jsonObject.addProperty("lastAccess", ISO8601FormatFactory.getInstance().formatDate(this.lastAccess)); jsonObject.addProperty("lastAccess", ISO8601.toString(this.lastAccess));
JsonArray rolesJ = new JsonArray(); JsonArray rolesJ = new JsonArray();
for (String role : this.userRoles) { for (String role : this.userRoles) {

View File

@ -18,7 +18,7 @@ package li.strolch.service.test;
import static org.hamcrest.Matchers.*; import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import java.util.Date; import java.time.LocalDateTime;
import java.util.HashSet; import java.util.HashSet;
import li.strolch.privilege.base.AccessDeniedException; import li.strolch.privilege.base.AccessDeniedException;
@ -49,16 +49,15 @@ public class ServiceTest extends AbstractServiceTest {
this.thrown.expect(PrivilegeException.class); this.thrown.expect(PrivilegeException.class);
TestService testService = new TestService(); TestService testService = new TestService();
getServiceHandler().doService( getServiceHandler().doService(
new Certificate(null, null, null, null, null, null, null, null, new Date(), null, new HashSet<>(), new Certificate(null, null, null, null, null, null, null, null, LocalDateTime.now(), false, null,
null), testService); new HashSet<>(), null), testService);
} }
@Test @Test
public void shouldFailInvalidCertificate2() { public void shouldFailInvalidCertificate2() {
TestService testService = new TestService(); TestService testService = new TestService();
Certificate badCert = new Certificate(Usage.ANY, "1", "bob", "Bob", "Brown", UserState.ENABLED, "dsdf", "asd", Certificate badCert = new Certificate(Usage.ANY, "1", "bob", "Bob", "Brown", UserState.ENABLED, "dsdf", "asd",
//$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ LocalDateTime.now(), false, null, new HashSet<>(), null);
new Date(), null, new HashSet<>(), null);
ServiceResult svcResult = getServiceHandler().doService(badCert, testService); ServiceResult svcResult = getServiceHandler().doService(badCert, testService);
assertThat(svcResult.getThrowable(), instanceOf(NotAuthenticatedException.class)); assertThat(svcResult.getThrowable(), instanceOf(NotAuthenticatedException.class));
} }