X-Git-Url: https://git.argeo.org/?a=blobdiff_plain;f=org.argeo.cms%2Fsrc%2Forg%2Fargeo%2Fcms%2Finternal%2Fauth%2FCmsSessionImpl.java;h=f8d5863e69321f530ca818fc89c86b23b5f9ed64;hb=5b3108fe285bca50565b58b63fa4feddc96c0765;hp=b571fcf8602519c10be1baa01d6a83302259fc6d;hpb=3714331f776988facff3632d86ad3f6d6352220c;p=lgpl%2Fargeo-commons.git diff --git a/org.argeo.cms/src/org/argeo/cms/internal/auth/CmsSessionImpl.java b/org.argeo.cms/src/org/argeo/cms/internal/auth/CmsSessionImpl.java index b571fcf86..f8d5863e6 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/auth/CmsSessionImpl.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/auth/CmsSessionImpl.java @@ -1,23 +1,33 @@ package org.argeo.cms.internal.auth; +import java.security.AccessControlContext; +import java.security.AccessController; +import java.security.PrivilegedAction; import java.security.PrivilegedExceptionAction; +import java.time.ZonedDateTime; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Hashtable; import java.util.LinkedHashSet; +import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.UUID; +import javax.crypto.SecretKey; import javax.jcr.Repository; import javax.jcr.Session; import javax.naming.InvalidNameException; import javax.naming.ldap.LdapName; import javax.security.auth.Subject; +import javax.security.auth.login.LoginContext; +import javax.security.auth.login.LoginException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.argeo.api.NodeConstants; +import org.argeo.api.security.NodeSecurityUtils; import org.argeo.cms.CmsException; import org.argeo.cms.auth.CmsSession; import org.argeo.jcr.JcrUtils; @@ -32,11 +42,17 @@ public class CmsSessionImpl implements CmsSession { private final static BundleContext bc = FrameworkUtil.getBundle(CmsSessionImpl.class).getBundleContext(); private final static Log log = LogFactory.getLog(CmsSessionImpl.class); - private final Subject initialSubject; + // private final Subject initialSubject; + private final AccessControlContext initialContext; private final UUID uuid; private final String localSessionId; private final Authorization authorization; private final LdapName userDn; + private final boolean anonymous; + + private final ZonedDateTime creationTime; + private ZonedDateTime end; + private final Locale locale; private ServiceRegistration serviceRegistration; @@ -44,36 +60,78 @@ public class CmsSessionImpl implements CmsSession { private Set dataSessionsInUse = new HashSet<>(); private LinkedHashSet additionalDataSessions = new LinkedHashSet<>(); - public CmsSessionImpl(Subject initialSubject, Authorization authorization, String localSessionId) { - this.initialSubject = initialSubject; + public CmsSessionImpl(Subject initialSubject, Authorization authorization, Locale locale, String localSessionId) { + this.creationTime = ZonedDateTime.now(); + this.locale = locale; + this.initialContext = Subject.doAs(initialSubject, new PrivilegedAction() { + + @Override + public AccessControlContext run() { + return AccessController.getContext(); + } + + }); + // this.initialSubject = initialSubject; this.localSessionId = localSessionId; this.authorization = authorization; - try { - this.userDn = new LdapName(authorization.getName()); - } catch (InvalidNameException e) { - throw new CmsException("Invalid user name " + authorization.getName(), e); + if (authorization.getName() != null) + try { + this.userDn = new LdapName(authorization.getName()); + this.anonymous = false; + } catch (InvalidNameException e) { + throw new CmsException("Invalid user name " + authorization.getName(), e); + } + else { + this.userDn = NodeSecurityUtils.ROLE_ANONYMOUS_NAME; + this.anonymous = true; } this.uuid = UUID.randomUUID(); // register as service Hashtable props = new Hashtable<>(); - props.put(CmsSession.USER_DN, authorization.getName()); + props.put(CmsSession.USER_DN, userDn.toString()); props.put(CmsSession.SESSION_UUID, uuid.toString()); props.put(CmsSession.SESSION_LOCAL_ID, localSessionId); serviceRegistration = bc.registerService(CmsSession.class, this, props); } - public synchronized void cleanUp() { + public void close() { + end = ZonedDateTime.now(); serviceRegistration.unregister(); - // TODO check data session in use ? - for (String path : dataSessions.keySet()) - JcrUtils.logoutQuietly(dataSessions.get(path)); - for (Session session : additionalDataSessions) - JcrUtils.logoutQuietly(session); - notifyAll(); + synchronized (this) { + // TODO check data session in use ? + for (String path : dataSessions.keySet()) + JcrUtils.logoutQuietly(dataSessions.get(path)); + for (Session session : additionalDataSessions) + JcrUtils.logoutQuietly(session); + } + + try { + LoginContext lc; + if (isAnonymous()) { + lc = new LoginContext(NodeConstants.LOGIN_CONTEXT_ANONYMOUS, getSubject()); + } else { + lc = new LoginContext(NodeConstants.LOGIN_CONTEXT_USER, getSubject()); + } + lc.logout(); + } catch (LoginException e) { + log.warn("Could not logout " + getSubject() + ": " + e); + } + log.debug("Closed " + this); + } + + private Subject getSubject() { + return Subject.getSubject(initialContext); + } + + public Set getSecretKeys() { + return getSubject().getPrivateCredentials(SecretKey.class); + } + + public Session newDataSession(String cn, String workspace, Repository repository) { + return login(repository, workspace); } - @Override public synchronized Session getDataSession(String cn, String workspace, Repository repository) { // FIXME make it more robust if (workspace == null) @@ -109,7 +167,7 @@ public class CmsSessionImpl implements CmsSession { private Session login(Repository repository, String workspace) { try { - return Subject.doAs(initialSubject, new PrivilegedExceptionAction() { + return Subject.doAs(getSubject(), new PrivilegedExceptionAction() { @Override public Session run() throws Exception { return repository.login(workspace); @@ -120,11 +178,12 @@ public class CmsSessionImpl implements CmsSession { } } - @Override public synchronized void releaseDataSession(String cn, Session session) { if (additionalDataSessions.contains(session)) { JcrUtils.logoutQuietly(session); additionalDataSessions.remove(session); + if (log.isTraceEnabled()) + log.trace("Remove additional data session " + session); return; } String path = cn + '/' + session.getWorkspace().getName(); @@ -134,9 +193,20 @@ public class CmsSessionImpl implements CmsSession { Session registeredSession = dataSessions.get(path); if (session != registeredSession) log.warn("Data session " + path + " not consistent for " + userDn); + if (log.isTraceEnabled()) + log.trace("Released data session " + session + " for " + path); notifyAll(); } + @Override + public boolean isValid() { + return !isClosed(); + } + + protected boolean isClosed() { + return getEnd() != null; + } + @Override public Authorization getAuthorization() { return authorization; @@ -147,33 +217,41 @@ public class CmsSessionImpl implements CmsSession { return uuid; } - public Subject getInitialSubject() { - return initialSubject; + @Override + public LdapName getUserDn() { + return userDn; } - public String getLocalSessionId() { + @Override + public String getLocalId() { return localSessionId; } - public ServiceRegistration getServiceRegistration() { - return serviceRegistration; + @Override + public boolean isAnonymous() { + return anonymous; } @Override - public LdapName getUserDn() { - return userDn; + public Locale getLocale() { + return locale; } @Override - public String getLocalId() { - return localSessionId; + public ZonedDateTime getCreationTime() { + return creationTime; + } + + @Override + public ZonedDateTime getEnd() { + return end; } public String toString() { - return "CMS Session local=" + localSessionId + ", uuid=" + uuid; + return "CMS Session " + userDn + " local=" + localSessionId + ", uuid=" + uuid; } - public static CmsSession getByLocalId(String localId) { + public static CmsSessionImpl getByLocalId(String localId) { Collection> sr; try { sr = bc.getServiceReferences(CmsSession.class, "(" + CmsSession.SESSION_LOCAL_ID + "=" + localId + ")"); @@ -183,7 +261,7 @@ public class CmsSessionImpl implements CmsSession { ServiceReference cmsSessionRef; if (sr.size() == 1) { cmsSessionRef = sr.iterator().next(); - return bc.getService(cmsSessionRef); + return (CmsSessionImpl) bc.getService(cmsSessionRef); } else if (sr.size() == 0) { return null; } else @@ -191,7 +269,7 @@ public class CmsSessionImpl implements CmsSession { } - public static CmsSession getByUuid(String uuid) { + public static CmsSessionImpl getByUuid(Object uuid) { Collection> sr; try { sr = bc.getServiceReferences(CmsSession.class, "(" + CmsSession.SESSION_UUID + "=" + uuid + ")"); @@ -201,11 +279,28 @@ public class CmsSessionImpl implements CmsSession { ServiceReference cmsSessionRef; if (sr.size() == 1) { cmsSessionRef = sr.iterator().next(); - return bc.getService(cmsSessionRef); + return (CmsSessionImpl) bc.getService(cmsSessionRef); } else if (sr.size() == 0) { return null; } else throw new CmsException(sr.size() + " CMS sessions registered for " + uuid); } + + public static void closeInvalidSessions() { + Collection> srs; + try { + srs = bc.getServiceReferences(CmsSession.class, null); + for (ServiceReference sr : srs) { + CmsSession cmsSession = bc.getService(sr); + if (!cmsSession.isValid()) { + ((CmsSessionImpl) cmsSession).close(); + if (log.isDebugEnabled()) + log.debug("Closed expired CMS session " + cmsSession); + } + } + } catch (InvalidSyntaxException e) { + throw new CmsException("Cannot get CMS sessions", e); + } + } }