X-Git-Url: https://git.argeo.org/?a=blobdiff_plain;f=org.argeo.cms%2Fsrc%2Forg%2Fargeo%2Fcms%2Finternal%2Fauth%2FCmsSessionImpl.java;h=164d319f197ae92074b2c5965233deb6f7c1be5c;hb=da9d144b6b241e1526a3bd255dff905a7969a5bc;hp=42f5e6ae08def8ed2e93779f469b4d5b7a19d31e;hpb=7864616a19aefc440239c1c8854c6b3969be7af0;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 42f5e6ae0..164d319f1 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,57 +1,68 @@ package org.argeo.cms.internal.auth; +import java.io.Serializable; import java.security.AccessControlContext; import java.security.AccessController; import java.security.PrivilegedAction; -import java.security.PrivilegedExceptionAction; -import java.util.Collection; +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; -import java.util.Hashtable; -import java.util.LinkedHashSet; +import java.util.List; +import java.util.Locale; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.UUID; +import java.util.function.Consumer; -import javax.jcr.Repository; -import javax.jcr.Session; +import javax.crypto.SecretKey; import javax.naming.InvalidNameException; import javax.naming.ldap.LdapName; import javax.security.auth.Subject; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.argeo.cms.CmsException; -import org.argeo.cms.auth.CmsSession; -import org.argeo.jcr.JcrUtils; -import org.argeo.node.security.NodeSecurityUtils; -import org.osgi.framework.BundleContext; -import org.osgi.framework.FrameworkUtil; -import org.osgi.framework.InvalidSyntaxException; -import org.osgi.framework.ServiceReference; +import javax.security.auth.login.LoginContext; +import javax.security.auth.login.LoginException; +import javax.security.auth.x500.X500Principal; + +import org.argeo.api.cms.CmsAuth; +import org.argeo.api.cms.CmsLog; +import org.argeo.api.cms.CmsSession; +import org.argeo.cms.internal.runtime.CmsContextImpl; +import org.argeo.cms.security.NodeSecurityUtils; import org.osgi.framework.ServiceRegistration; import org.osgi.service.useradmin.Authorization; -public class CmsSessionImpl implements CmsSession { - private final static BundleContext bc = FrameworkUtil.getBundle(CmsSessionImpl.class).getBundleContext(); - private final static Log log = LogFactory.getLog(CmsSessionImpl.class); +/** Default CMS session implementation. */ +public class CmsSessionImpl implements CmsSession, Serializable { + private static final long serialVersionUID = 1867719354246307225L; +// private final static BundleContext bc = FrameworkUtil.getBundle(CmsSessionImpl.class).getBundleContext(); + private final static CmsLog log = CmsLog.getLog(CmsSessionImpl.class); // private final Subject initialSubject; - private final AccessControlContext initialContext; + private transient AccessControlContext accessControlContext; private final UUID uuid; private final String localSessionId; - private final Authorization authorization; + private Authorization authorization; private final LdapName userDn; private final boolean anonymous; + private final ZonedDateTime creationTime; + private ZonedDateTime end; + private final Locale locale; + private ServiceRegistration serviceRegistration; - private Map dataSessions = new HashMap<>(); - private Set dataSessionsInUse = new HashSet<>(); - private LinkedHashSet additionalDataSessions = new LinkedHashSet<>(); + private Map views = new HashMap<>(); + + private List> onCloseCallbacks = Collections.synchronizedList(new ArrayList<>()); - public CmsSessionImpl(Subject initialSubject, Authorization authorization, String localSessionId) { - this.initialContext = Subject.doAs(initialSubject, new PrivilegedAction() { + public CmsSessionImpl(UUID uuid, Subject initialSubject, Authorization authorization, Locale locale, + String localSessionId) { + Objects.requireNonNull(uuid); + + this.creationTime = ZonedDateTime.now(); + this.locale = locale; + this.accessControlContext = Subject.doAs(initialSubject, new PrivilegedAction() { @Override public AccessControlContext run() { @@ -67,113 +78,81 @@ public class CmsSessionImpl implements CmsSession { this.userDn = new LdapName(authorization.getName()); this.anonymous = false; } catch (InvalidNameException e) { - throw new CmsException("Invalid user name " + authorization.getName(), e); + throw new IllegalArgumentException("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, userDn.toString()); - props.put(CmsSession.SESSION_UUID, uuid.toString()); - props.put(CmsSession.SESSION_LOCAL_ID, localSessionId); - serviceRegistration = bc.registerService(CmsSession.class, this, props); + this.uuid = uuid; } - public synchronized void cleanUp() { - serviceRegistration.unregister(); + public void close() { + end = ZonedDateTime.now(); + CmsContextImpl.getCmsContext().unregisterCmsSession(this); +// 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(); - } + for (Consumer onClose : onCloseCallbacks) { + onClose.accept(this); + } - @Override - public synchronized Session getDataSession(String cn, String workspace, Repository repository) { - // FIXME make it more robust - if (workspace == null) - workspace = "main"; - String path = cn + '/' + workspace; - if (dataSessionsInUse.contains(path)) { - try { - wait(1000); - if (dataSessionsInUse.contains(path)) { - Session session = login(repository, workspace); - additionalDataSessions.add(session); - if (log.isTraceEnabled()) - log.trace("Additional data session " + path + " for " + userDn); - return session; - } - } catch (InterruptedException e) { - // silent + try { + LoginContext lc; + if (isAnonymous()) { + lc = new LoginContext(CmsAuth.LOGIN_CONTEXT_ANONYMOUS, getSubject()); + } else { + lc = new LoginContext(CmsAuth.LOGIN_CONTEXT_USER, getSubject()); } + lc.logout(); + } catch (LoginException e) { + log.warn("Could not logout " + getSubject() + ": " + e); + } finally { + accessControlContext = null; } + log.debug("Closed " + this); + } - Session session = null; - if (dataSessions.containsKey(path)) { - session = dataSessions.get(path); - } else { - session = login(repository, workspace); - dataSessions.put(path, session); - if (log.isTraceEnabled()) - log.trace("New data session " + path + " for " + userDn); - } - dataSessionsInUse.add(path); - return session; + @Override + public void addOnCloseCallback(Consumer onClose) { + onCloseCallbacks.add(onClose); } - private Session login(Repository repository, String workspace) { - try { - Subject initialSubject = Subject.getSubject(initialContext); - return Subject.doAs(initialSubject, new PrivilegedExceptionAction() { - @Override - public Session run() throws Exception { - return repository.login(workspace); - } - }); - } catch (Exception e) { - throw new CmsException("Cannot log in " + userDn + " to JCR", e); - } + public Subject getSubject() { + return Subject.getSubject(accessControlContext); } - @Override - public synchronized void releaseDataSession(String cn, Session session) { - if (additionalDataSessions.contains(session)) { - JcrUtils.logoutQuietly(session); - additionalDataSessions.remove(session); - return; - } - String path = cn + '/' + session.getWorkspace().getName(); - if (!dataSessionsInUse.contains(path)) - log.warn("Data session " + path + " was not in use for " + userDn); - dataSessionsInUse.remove(path); - Session registeredSession = dataSessions.get(path); - if (session != registeredSession) - log.warn("Data session " + path + " not consistent for " + userDn); - notifyAll(); + public Set getSecretKeys() { + checkValid(); + return getSubject().getPrivateCredentials(SecretKey.class); } @Override + public boolean isValid() { + return !isClosed(); + } + + private void checkValid() { + if (!isValid()) + throw new IllegalStateException("CMS session " + uuid + " is not valid since " + end); + } + + final protected boolean isClosed() { + return getEnd() != null; + } + public Authorization getAuthorization() { + checkValid(); return authorization; } @Override - public UUID getUuid() { - return uuid; - } - - public String getLocalSessionId() { - return localSessionId; + public String getDisplayName() { + return authorization.toString(); } - public ServiceRegistration getServiceRegistration() { - return serviceRegistration; + @Override + public UUID getUuid() { + return uuid; } @Override @@ -181,52 +160,45 @@ public class CmsSessionImpl implements CmsSession { return userDn; } + @Override + public String getUserRole() { + return new X500Principal(authorization.getName()).getName(); + } + @Override public String getLocalId() { return localSessionId; } + @Override public boolean isAnonymous() { return anonymous; } - public String toString() { - return "CMS Session local=" + localSessionId + ", uuid=" + uuid; + @Override + public Locale getLocale() { + return locale; } - public static CmsSession getByLocalId(String localId) { - Collection> sr; - try { - sr = bc.getServiceReferences(CmsSession.class, "(" + CmsSession.SESSION_LOCAL_ID + "=" + localId + ")"); - } catch (InvalidSyntaxException e) { - throw new CmsException("Cannot get CMS session for id " + localId, e); - } - ServiceReference cmsSessionRef; - if (sr.size() == 1) { - cmsSessionRef = sr.iterator().next(); - return bc.getService(cmsSessionRef); - } else if (sr.size() == 0) { - return null; - } else - throw new CmsException(sr.size() + " CMS sessions registered for " + localId); + @Override + public ZonedDateTime getCreationTime() { + return creationTime; + } + @Override + public ZonedDateTime getEnd() { + return end; } - public static CmsSession getByUuid(String uuid) { - Collection> sr; - try { - sr = bc.getServiceReferences(CmsSession.class, "(" + CmsSession.SESSION_UUID + "=" + uuid + ")"); - } catch (InvalidSyntaxException e) { - throw new CmsException("Cannot get CMS session for uuid " + uuid, e); - } - ServiceReference cmsSessionRef; - if (sr.size() == 1) { - cmsSessionRef = sr.iterator().next(); - return bc.getService(cmsSessionRef); - } else if (sr.size() == 0) { - return null; - } else - throw new CmsException(sr.size() + " CMS sessions registered for " + uuid); + @Override + public void registerView(String uid, Object view) { + checkValid(); + if (views.containsKey(uid)) + throw new IllegalArgumentException("View " + uid + " is already registered."); + views.put(uid, view); + } + public String toString() { + return "CMS Session " + userDn + " localId=" + localSessionId + ", uuid=" + uuid; } }