From 2ee0d2fb4e4b472b22688569b157a77cfdfc7f24 Mon Sep 17 00:00:00 2001 From: Robert von Burg Date: Sat, 14 Mar 2015 21:23:10 +0100 Subject: [PATCH] [New] Added UserSession querying and modifying --- ch.eitchnet.privilege | 2 +- .../rest/DefaultStrolchSessionHandler.java | 83 +++++++++++++++++-- .../strolch/rest/StrolchSessionHandler.java | 14 +++- .../rest/endpoint/AuthenticationService.java | 2 +- .../rest/endpoint/UserSessionsService.java | 78 +++++++++++++++++ .../li/strolch/rest/model/UserSession.java | 78 +++++++++++++++++ .../li/strolch/service/test/ServiceTest.java | 6 +- 7 files changed, 252 insertions(+), 11 deletions(-) create mode 100644 li.strolch.rest/src/main/java/li/strolch/rest/endpoint/UserSessionsService.java create mode 100644 li.strolch.rest/src/main/java/li/strolch/rest/model/UserSession.java diff --git a/ch.eitchnet.privilege b/ch.eitchnet.privilege index 9870513be..c2f4d7468 160000 --- a/ch.eitchnet.privilege +++ b/ch.eitchnet.privilege @@ -1 +1 @@ -Subproject commit 9870513beb0a656a7d9153ea30aa7a0000e17417 +Subproject commit c2f4d7468b124f2579ac21d080d47c179f007afd diff --git a/li.strolch.rest/src/main/java/li/strolch/rest/DefaultStrolchSessionHandler.java b/li.strolch.rest/src/main/java/li/strolch/rest/DefaultStrolchSessionHandler.java index e0edc9637..34833263c 100644 --- a/li.strolch.rest/src/main/java/li/strolch/rest/DefaultStrolchSessionHandler.java +++ b/li.strolch.rest/src/main/java/li/strolch/rest/DefaultStrolchSessionHandler.java @@ -16,7 +16,15 @@ package li.strolch.rest; 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.List; +import java.util.Locale; import java.util.Map; import java.util.Timer; import java.util.TimerTask; @@ -25,13 +33,17 @@ import java.util.concurrent.TimeUnit; import li.strolch.agent.api.ComponentContainer; import li.strolch.agent.api.StrolchComponent; import li.strolch.exception.StrolchException; +import li.strolch.rest.model.UserSession; import li.strolch.runtime.configuration.ComponentConfiguration; import li.strolch.runtime.privilege.PrivilegeHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import ch.eitchnet.privilege.base.AccessDeniedException; import ch.eitchnet.privilege.model.Certificate; +import ch.eitchnet.privilege.model.PrivilegeContext; +import ch.eitchnet.privilege.model.SimpleRestrictable; import ch.eitchnet.utils.dbc.DBC; /** @@ -106,7 +118,7 @@ public class DefaultStrolchSessionHandler extends StrolchComponent implements St synchronized (this.certificateMap) { Certificate certificate = this.privilegeHandler.authenticate(username, password); - certificate.setLastAccess(System.currentTimeMillis()); + certificate.setLastAccess(new Date()); this.certificateMap.put(certificate.getAuthToken(), certificate); 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 public Certificate validate(Certificate certificate) { this.privilegeHandler.isCertificateValid(certificate); - certificate.setLastAccess(System.currentTimeMillis()); + certificate.setLastAccess(new Date()); return certificate; } @Override - public void invalidateSession(Certificate certificate) { + public void invalidate(Certificate certificate) { DBC.PRE.assertNotNull("Certificate must bet given!", certificate); //$NON-NLS-1$ Certificate removedCert; @@ -174,15 +186,74 @@ public class DefaultStrolchSessionHandler extends StrolchComponent implements St 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()) { - 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$ 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 getSessions(Certificate certificate) { + PrivilegeContext ctx = this.privilegeHandler.getPrivilegeContext(certificate); + ctx.assertHasPrivilege("GetSession"); + List 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); + } + } + } } diff --git a/li.strolch.rest/src/main/java/li/strolch/rest/StrolchSessionHandler.java b/li.strolch.rest/src/main/java/li/strolch/rest/StrolchSessionHandler.java index ac0962e10..b3d6604a8 100644 --- a/li.strolch.rest/src/main/java/li/strolch/rest/StrolchSessionHandler.java +++ b/li.strolch.rest/src/main/java/li/strolch/rest/StrolchSessionHandler.java @@ -15,6 +15,10 @@ */ package li.strolch.rest; +import java.util.List; +import java.util.Locale; + +import li.strolch.rest.model.UserSession; import ch.eitchnet.privilege.model.Certificate; /** @@ -28,5 +32,13 @@ public interface StrolchSessionHandler { public Certificate validate(Certificate certificate); - public void invalidateSession(Certificate certificate); + public void invalidate(Certificate certificate); + + public List 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); } diff --git a/li.strolch.rest/src/main/java/li/strolch/rest/endpoint/AuthenticationService.java b/li.strolch.rest/src/main/java/li/strolch/rest/endpoint/AuthenticationService.java index 24b3112fb..6b36abf9d 100644 --- a/li.strolch.rest/src/main/java/li/strolch/rest/endpoint/AuthenticationService.java +++ b/li.strolch.rest/src/main/java/li/strolch/rest/endpoint/AuthenticationService.java @@ -133,7 +133,7 @@ public class AuthenticationService { StrolchSessionHandler sessionHandlerHandler = RestfulStrolchComponent.getInstance().getComponent( StrolchSessionHandler.class); Certificate certificate = sessionHandlerHandler.validate(sessionId); - sessionHandlerHandler.invalidateSession(certificate); + sessionHandlerHandler.invalidate(certificate); logoutResult.setUsername(certificate.getUsername()); logoutResult.setSessionId(sessionId); diff --git a/li.strolch.rest/src/main/java/li/strolch/rest/endpoint/UserSessionsService.java b/li.strolch.rest/src/main/java/li/strolch/rest/endpoint/UserSessionsService.java new file mode 100644 index 000000000..523a380b8 --- /dev/null +++ b/li.strolch.rest/src/main/java/li/strolch/rest/endpoint/UserSessionsService.java @@ -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 sessions = sessionHandler.getSessions(cert); + GenericEntity> entity = new GenericEntity>(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(); + } +} diff --git a/li.strolch.rest/src/main/java/li/strolch/rest/model/UserSession.java b/li.strolch.rest/src/main/java/li/strolch/rest/model/UserSession.java new file mode 100644 index 000000000..a885632dc --- /dev/null +++ b/li.strolch.rest/src/main/java/li/strolch/rest/model/UserSession.java @@ -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 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 getUserRoles() { + return userRoles; + } +} diff --git a/li.strolch.service/src/test/java/li/strolch/service/test/ServiceTest.java b/li.strolch.service/src/test/java/li/strolch/service/test/ServiceTest.java index c66aef8e1..30969f266 100644 --- a/li.strolch.service/src/test/java/li/strolch/service/test/ServiceTest.java +++ b/li.strolch.service/src/test/java/li/strolch/service/test/ServiceTest.java @@ -20,6 +20,7 @@ import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.instanceOf; import static org.junit.Assert.assertThat; +import java.util.Date; import java.util.HashSet; import li.strolch.service.api.ServiceResult; @@ -56,14 +57,15 @@ public class ServiceTest extends AbstractServiceTest { this.thrown.expect(PrivilegeException.class); TestService testService = new TestService(); getServiceHandler().doService( - new Certificate(null, 0, null, null, null, null, null, new HashSet(), null), testService); + new Certificate(null, new Date(), null, null, null, null, null, new HashSet(), null), + testService); } @Test public void shouldFailInvalidCertificate2() { TestService testService = new TestService(); Certificate badCert = new Certificate( - "1", System.currentTimeMillis(), "bob", "Bob", "Brown", "dsdf", null, new HashSet(), null); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + "1", new Date(), "bob", "Bob", "Brown", "dsdf", null, new HashSet(), null); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ ServiceResult svcResult = getServiceHandler().doService(badCert, testService); assertThat(svcResult.getThrowable(), instanceOf(AccessDeniedException.class)); }