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.RepositoryException;
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.cms.CmsException;
import org.argeo.cms.auth.CmsSession;
import org.argeo.jcr.JcrUtils;
+import org.argeo.node.NodeConstants;
import org.argeo.node.security.NodeSecurityUtils;
import org.osgi.framework.BundleContext;
import org.osgi.framework.FrameworkUtil;
private final LdapName userDn;
private final boolean anonymous;
+ private final ZonedDateTime creationTime;
+ private ZonedDateTime end;
+ private final Locale locale;
+
private ServiceRegistration<CmsSession> serviceRegistration;
private Map<String, Session> dataSessions = new HashMap<>();
private Set<String> dataSessionsInUse = new HashSet<>();
private LinkedHashSet<Session> additionalDataSessions = new LinkedHashSet<>();
- public CmsSessionImpl(Subject initialSubject, Authorization authorization, String localSessionId) {
+ public CmsSessionImpl(Subject initialSubject, Authorization authorization, Locale locale, String localSessionId) {
+ this.creationTime = ZonedDateTime.now();
+ this.locale = locale;
this.initialContext = Subject.doAs(initialSubject, new PrivilegedAction<AccessControlContext>() {
@Override
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<SecretKey> getSecretKeys() {
+ return getSubject().getPrivateCredentials(SecretKey.class);
}
- @Override
public synchronized Session getDataSession(String cn, String workspace, Repository repository) {
// FIXME make it more robust
if (workspace == null)
private Session login(Repository repository, String workspace) {
try {
- Subject initialSubject = Subject.getSubject(initialContext);
- return Subject.doAs(initialSubject, new PrivilegedExceptionAction<Session>() {
+ return Subject.doAs(getSubject(), new PrivilegedExceptionAction<Session>() {
@Override
public Session run() throws Exception {
return repository.login(workspace);
}
}
- @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();
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;
return uuid;
}
- public String getLocalSessionId() {
- return localSessionId;
- }
-
- public ServiceRegistration<CmsSession> getServiceRegistration() {
- return serviceRegistration;
- }
-
@Override
public LdapName getUserDn() {
return userDn;
return localSessionId;
}
+ @Override
public boolean isAnonymous() {
return anonymous;
}
+ @Override
+ public Locale getLocale() {
+ return locale;
+ }
+
+ @Override
+ 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<ServiceReference<CmsSession>> sr;
try {
sr = bc.getServiceReferences(CmsSession.class, "(" + CmsSession.SESSION_LOCAL_ID + "=" + localId + ")");
ServiceReference<CmsSession> 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
}
- public static CmsSession getByUuid(String uuid) {
+ public static CmsSessionImpl getByUuid(Object uuid) {
Collection<ServiceReference<CmsSession>> sr;
try {
sr = bc.getServiceReferences(CmsSession.class, "(" + CmsSession.SESSION_UUID + "=" + uuid + ")");
ServiceReference<CmsSession> 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<ServiceReference<CmsSession>> srs;
+ try {
+ srs = bc.getServiceReferences(CmsSession.class, null);
+ for (ServiceReference<CmsSession> 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);
+ }
+ }
}