[New] Added UserSession querying and modifying
This commit is contained in:
parent
85e34cabe1
commit
2ee0d2fb4e
|
@ -1 +1 @@
|
||||||
Subproject commit 9870513beb0a656a7d9153ea30aa7a0000e17417
|
Subproject commit c2f4d7468b124f2579ac21d080d47c179f007afd
|
|
@ -16,7 +16,15 @@
|
||||||
package li.strolch.rest;
|
package li.strolch.rest;
|
||||||
|
|
||||||
import java.text.MessageFormat;
|
import java.text.MessageFormat;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.ZoneId;
|
||||||
|
import java.time.temporal.ChronoUnit;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Timer;
|
import java.util.Timer;
|
||||||
import java.util.TimerTask;
|
import java.util.TimerTask;
|
||||||
|
@ -25,13 +33,17 @@ import java.util.concurrent.TimeUnit;
|
||||||
import li.strolch.agent.api.ComponentContainer;
|
import li.strolch.agent.api.ComponentContainer;
|
||||||
import li.strolch.agent.api.StrolchComponent;
|
import li.strolch.agent.api.StrolchComponent;
|
||||||
import li.strolch.exception.StrolchException;
|
import li.strolch.exception.StrolchException;
|
||||||
|
import li.strolch.rest.model.UserSession;
|
||||||
import li.strolch.runtime.configuration.ComponentConfiguration;
|
import li.strolch.runtime.configuration.ComponentConfiguration;
|
||||||
import li.strolch.runtime.privilege.PrivilegeHandler;
|
import li.strolch.runtime.privilege.PrivilegeHandler;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import ch.eitchnet.privilege.base.AccessDeniedException;
|
||||||
import ch.eitchnet.privilege.model.Certificate;
|
import ch.eitchnet.privilege.model.Certificate;
|
||||||
|
import ch.eitchnet.privilege.model.PrivilegeContext;
|
||||||
|
import ch.eitchnet.privilege.model.SimpleRestrictable;
|
||||||
import ch.eitchnet.utils.dbc.DBC;
|
import ch.eitchnet.utils.dbc.DBC;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -106,7 +118,7 @@ public class DefaultStrolchSessionHandler extends StrolchComponent implements St
|
||||||
|
|
||||||
synchronized (this.certificateMap) {
|
synchronized (this.certificateMap) {
|
||||||
Certificate certificate = this.privilegeHandler.authenticate(username, password);
|
Certificate certificate = this.privilegeHandler.authenticate(username, password);
|
||||||
certificate.setLastAccess(System.currentTimeMillis());
|
certificate.setLastAccess(new Date());
|
||||||
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$
|
||||||
|
@ -132,12 +144,12 @@ public class DefaultStrolchSessionHandler extends StrolchComponent implements St
|
||||||
@Override
|
@Override
|
||||||
public Certificate validate(Certificate certificate) {
|
public Certificate validate(Certificate certificate) {
|
||||||
this.privilegeHandler.isCertificateValid(certificate);
|
this.privilegeHandler.isCertificateValid(certificate);
|
||||||
certificate.setLastAccess(System.currentTimeMillis());
|
certificate.setLastAccess(new Date());
|
||||||
return certificate;
|
return certificate;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void invalidateSession(Certificate certificate) {
|
public void invalidate(Certificate certificate) {
|
||||||
DBC.PRE.assertNotNull("Certificate must bet given!", certificate); //$NON-NLS-1$
|
DBC.PRE.assertNotNull("Certificate must bet given!", certificate); //$NON-NLS-1$
|
||||||
|
|
||||||
Certificate removedCert;
|
Certificate removedCert;
|
||||||
|
@ -174,15 +186,74 @@ public class DefaultStrolchSessionHandler extends StrolchComponent implements St
|
||||||
certificateMap = new HashMap<>(map);
|
certificateMap = new HashMap<>(map);
|
||||||
}
|
}
|
||||||
|
|
||||||
long reqLastAccessTime = System.currentTimeMillis() - DefaultStrolchSessionHandler.this.sessionTtl;
|
LocalDateTime timeOutTime = LocalDateTime.now().minus(sessionTtl, ChronoUnit.MILLIS);
|
||||||
|
ZoneId systemDefault = ZoneId.systemDefault();
|
||||||
|
|
||||||
for (Certificate certificate : certificateMap.values()) {
|
for (Certificate certificate : certificateMap.values()) {
|
||||||
if (certificate.getLastAccess() < reqLastAccessTime) {
|
Instant lastAccess = certificate.getLastAccess().toInstant();
|
||||||
|
if (timeOutTime.isAfter(LocalDateTime.ofInstant(lastAccess, systemDefault))) {
|
||||||
String msg = "Session {0} for user {1} has expired, invalidating session..."; //$NON-NLS-1$
|
String msg = "Session {0} for user {1} has expired, invalidating session..."; //$NON-NLS-1$
|
||||||
logger.info(MessageFormat.format(msg, certificate.getAuthToken(), certificate.getUsername()));
|
logger.info(MessageFormat.format(msg, certificate.getAuthToken(), certificate.getUsername()));
|
||||||
invalidateSession(certificate);
|
invalidate(certificate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserSession getSession(Certificate certificate, String sessionId) {
|
||||||
|
PrivilegeContext ctx = this.privilegeHandler.getPrivilegeContext(certificate);
|
||||||
|
ctx.assertHasPrivilege("GetSession");
|
||||||
|
for (Certificate cert : certificateMap.values()) {
|
||||||
|
if (cert.getSessionId().equals(sessionId)) {
|
||||||
|
ctx.validateAction(new SimpleRestrictable("GetSession", cert));
|
||||||
|
return new UserSession(cert);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<UserSession> getSessions(Certificate certificate) {
|
||||||
|
PrivilegeContext ctx = this.privilegeHandler.getPrivilegeContext(certificate);
|
||||||
|
ctx.assertHasPrivilege("GetSession");
|
||||||
|
List<UserSession> sessions = new ArrayList<>(this.certificateMap.size());
|
||||||
|
for (Certificate cert : certificateMap.values()) {
|
||||||
|
try {
|
||||||
|
ctx.validateAction(new SimpleRestrictable("GetSession", cert));
|
||||||
|
sessions.add(new UserSession(cert));
|
||||||
|
} catch (AccessDeniedException e) {
|
||||||
|
// so no, user may not get this session
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sessions;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void invalidateSession(Certificate certificate, String sessionId) {
|
||||||
|
PrivilegeContext ctx = this.privilegeHandler.getPrivilegeContext(certificate);
|
||||||
|
ctx.assertHasPrivilege("InvalidateSession");
|
||||||
|
for (Certificate cert : certificateMap.values()) {
|
||||||
|
if (cert.getSessionId().equals(sessionId)) {
|
||||||
|
ctx.validateAction(new SimpleRestrictable("InvalidateSession", cert));
|
||||||
|
invalidate(cert);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setSessionLocale(Certificate certificate, String sessionId, Locale locale) {
|
||||||
|
if (!certificate.getSessionId().equals(sessionId)) {
|
||||||
|
String msg = "User''s can only change their own session locale: {0} may not change locale of session {1}";
|
||||||
|
throw new AccessDeniedException(MessageFormat.format(msg, certificate.getUsername(), sessionId));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Certificate cert : certificateMap.values()) {
|
||||||
|
if (cert.getSessionId().equals(sessionId)) {
|
||||||
|
cert.setLocale(locale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,10 @@
|
||||||
*/
|
*/
|
||||||
package li.strolch.rest;
|
package li.strolch.rest;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import li.strolch.rest.model.UserSession;
|
||||||
import ch.eitchnet.privilege.model.Certificate;
|
import ch.eitchnet.privilege.model.Certificate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -28,5 +32,13 @@ public interface StrolchSessionHandler {
|
||||||
|
|
||||||
public Certificate validate(Certificate certificate);
|
public Certificate validate(Certificate certificate);
|
||||||
|
|
||||||
public void invalidateSession(Certificate certificate);
|
public void invalidate(Certificate certificate);
|
||||||
|
|
||||||
|
public List<UserSession> getSessions(Certificate certificate);
|
||||||
|
|
||||||
|
public UserSession getSession(Certificate certificate, String sessionId);
|
||||||
|
|
||||||
|
public void invalidateSession(Certificate certificate, String sessionId);
|
||||||
|
|
||||||
|
public void setSessionLocale(Certificate certificate, String sessionId, Locale locale);
|
||||||
}
|
}
|
||||||
|
|
|
@ -133,7 +133,7 @@ public class AuthenticationService {
|
||||||
StrolchSessionHandler sessionHandlerHandler = RestfulStrolchComponent.getInstance().getComponent(
|
StrolchSessionHandler sessionHandlerHandler = RestfulStrolchComponent.getInstance().getComponent(
|
||||||
StrolchSessionHandler.class);
|
StrolchSessionHandler.class);
|
||||||
Certificate certificate = sessionHandlerHandler.validate(sessionId);
|
Certificate certificate = sessionHandlerHandler.validate(sessionId);
|
||||||
sessionHandlerHandler.invalidateSession(certificate);
|
sessionHandlerHandler.invalidate(certificate);
|
||||||
|
|
||||||
logoutResult.setUsername(certificate.getUsername());
|
logoutResult.setUsername(certificate.getUsername());
|
||||||
logoutResult.setSessionId(sessionId);
|
logoutResult.setSessionId(sessionId);
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
package li.strolch.rest.endpoint;
|
||||||
|
|
||||||
|
import java.text.MessageFormat;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.ws.rs.DELETE;
|
||||||
|
import javax.ws.rs.GET;
|
||||||
|
import javax.ws.rs.PUT;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.PathParam;
|
||||||
|
import javax.ws.rs.Produces;
|
||||||
|
import javax.ws.rs.core.Context;
|
||||||
|
import javax.ws.rs.core.GenericEntity;
|
||||||
|
import javax.ws.rs.core.MediaType;
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
|
||||||
|
import li.strolch.rest.RestfulStrolchComponent;
|
||||||
|
import li.strolch.rest.StrolchRestfulConstants;
|
||||||
|
import li.strolch.rest.StrolchSessionHandler;
|
||||||
|
import li.strolch.rest.model.Result;
|
||||||
|
import li.strolch.rest.model.UserSession;
|
||||||
|
import ch.eitchnet.privilege.model.Certificate;
|
||||||
|
|
||||||
|
@Path("strolch/sessions")
|
||||||
|
public class UserSessionsService {
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
public Response getSessions(@Context HttpServletRequest request) {
|
||||||
|
Certificate cert = (Certificate) request.getAttribute(StrolchRestfulConstants.STROLCH_CERTIFICATE);
|
||||||
|
StrolchSessionHandler sessionHandler = RestfulStrolchComponent.getInstance().getSessionHandler();
|
||||||
|
List<UserSession> sessions = sessionHandler.getSessions(cert);
|
||||||
|
GenericEntity<List<UserSession>> entity = new GenericEntity<List<UserSession>>(sessions) {
|
||||||
|
};
|
||||||
|
return Response.ok(entity, MediaType.APPLICATION_JSON).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
@Path("{sessionId}")
|
||||||
|
public Response getSession(@Context HttpServletRequest request, @PathParam("sessionId") String sessionId) {
|
||||||
|
Certificate cert = (Certificate) request.getAttribute(StrolchRestfulConstants.STROLCH_CERTIFICATE);
|
||||||
|
StrolchSessionHandler sessionHandler = RestfulStrolchComponent.getInstance().getSessionHandler();
|
||||||
|
UserSession session = sessionHandler.getSession(cert, sessionId);
|
||||||
|
return Response.ok(session, MediaType.APPLICATION_JSON).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@DELETE
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
@Path("{sessionId}")
|
||||||
|
public Response invalidateSession(@Context HttpServletRequest request, @PathParam("sessionId") String sessionId) {
|
||||||
|
Certificate cert = (Certificate) request.getAttribute(StrolchRestfulConstants.STROLCH_CERTIFICATE);
|
||||||
|
StrolchSessionHandler sessionHandler = RestfulStrolchComponent.getInstance().getSessionHandler();
|
||||||
|
sessionHandler.invalidateSession(cert, sessionId);
|
||||||
|
return Response.ok(new Result(), MediaType.APPLICATION_JSON).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PUT
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
@Path("{sessionId}/locale/{locale}")
|
||||||
|
public Response setSessionLocale(@Context HttpServletRequest request, @PathParam("sessionId") String sessionId,
|
||||||
|
@PathParam("locale") String localeS) {
|
||||||
|
Certificate cert = (Certificate) request.getAttribute(StrolchRestfulConstants.STROLCH_CERTIFICATE);
|
||||||
|
StrolchSessionHandler sessionHandler = RestfulStrolchComponent.getInstance().getSessionHandler();
|
||||||
|
Locale locale;
|
||||||
|
try {
|
||||||
|
locale = new Locale(localeS);
|
||||||
|
} catch (Exception e) {
|
||||||
|
String msg = MessageFormat.format("Locale {0} is not valid!", localeS);
|
||||||
|
return Response.serverError().entity(new Result(msg)).type(MediaType.APPLICATION_JSON).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
sessionHandler.setSessionLocale(cert, sessionId, locale);
|
||||||
|
return Response.ok(new Result(), MediaType.APPLICATION_JSON).build();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
package li.strolch.rest.model;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import javax.xml.bind.annotation.XmlAccessType;
|
||||||
|
import javax.xml.bind.annotation.XmlAccessorType;
|
||||||
|
import javax.xml.bind.annotation.XmlAttribute;
|
||||||
|
import javax.xml.bind.annotation.XmlElement;
|
||||||
|
import javax.xml.bind.annotation.XmlRootElement;
|
||||||
|
|
||||||
|
import ch.eitchnet.privilege.model.Certificate;
|
||||||
|
|
||||||
|
@XmlRootElement(name = "UserSession")
|
||||||
|
@XmlAccessorType(XmlAccessType.NONE)
|
||||||
|
public class UserSession {
|
||||||
|
|
||||||
|
@XmlAttribute(name = "sessionId")
|
||||||
|
private String sessionId;
|
||||||
|
@XmlAttribute(name = "loginTime")
|
||||||
|
private Date loginTime;
|
||||||
|
@XmlAttribute(name = "username")
|
||||||
|
private String username;
|
||||||
|
@XmlAttribute(name = "firstname")
|
||||||
|
private String firstname;
|
||||||
|
@XmlAttribute(name = "lastname")
|
||||||
|
private String lastname;
|
||||||
|
@XmlElement(name = "roles")
|
||||||
|
private Set<String> userRoles;
|
||||||
|
@XmlAttribute(name = "locale")
|
||||||
|
private Locale locale;
|
||||||
|
@XmlAttribute(name = "lastAccess")
|
||||||
|
private Date lastAccess;
|
||||||
|
|
||||||
|
public UserSession(Certificate certificate) {
|
||||||
|
this.sessionId = certificate.getSessionId();
|
||||||
|
this.loginTime = certificate.getLoginTime();
|
||||||
|
this.username = certificate.getUsername();
|
||||||
|
this.firstname = certificate.getFirstname();
|
||||||
|
this.lastname = certificate.getLastname();
|
||||||
|
this.userRoles = certificate.getUserRoles();
|
||||||
|
this.locale = certificate.getLocale();
|
||||||
|
this.lastAccess = certificate.getLastAccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Locale getLocale() {
|
||||||
|
return locale;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getLastAccess() {
|
||||||
|
return lastAccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSessionId() {
|
||||||
|
return sessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getLoginTime() {
|
||||||
|
return loginTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUsername() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFirstname() {
|
||||||
|
return firstname;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLastname() {
|
||||||
|
return lastname;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> getUserRoles() {
|
||||||
|
return userRoles;
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,6 +20,7 @@ import static org.hamcrest.Matchers.equalTo;
|
||||||
import static org.hamcrest.Matchers.instanceOf;
|
import static org.hamcrest.Matchers.instanceOf;
|
||||||
import static org.junit.Assert.assertThat;
|
import static org.junit.Assert.assertThat;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
|
||||||
import li.strolch.service.api.ServiceResult;
|
import li.strolch.service.api.ServiceResult;
|
||||||
|
@ -56,14 +57,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, 0, null, null, null, null, null, new HashSet<String>(), null), testService);
|
new Certificate(null, new Date(), null, null, null, null, null, new HashSet<String>(), null),
|
||||||
|
testService);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void shouldFailInvalidCertificate2() {
|
public void shouldFailInvalidCertificate2() {
|
||||||
TestService testService = new TestService();
|
TestService testService = new TestService();
|
||||||
Certificate badCert = new Certificate(
|
Certificate badCert = new Certificate(
|
||||||
"1", System.currentTimeMillis(), "bob", "Bob", "Brown", "dsdf", null, new HashSet<String>(), null); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
|
"1", new Date(), "bob", "Bob", "Brown", "dsdf", null, new HashSet<String>(), null); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
|
||||||
ServiceResult svcResult = getServiceHandler().doService(badCert, testService);
|
ServiceResult svcResult = getServiceHandler().doService(badCert, testService);
|
||||||
assertThat(svcResult.getThrowable(), instanceOf(AccessDeniedException.class));
|
assertThat(svcResult.getThrowable(), instanceOf(AccessDeniedException.class));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue