1 package org
.argeo
.cms
.internal
.auth
;
3 import java
.security
.AccessControlContext
;
4 import java
.security
.AccessController
;
5 import java
.security
.PrivilegedAction
;
6 import java
.security
.PrivilegedExceptionAction
;
7 import java
.time
.ZonedDateTime
;
8 import java
.util
.Collection
;
9 import java
.util
.HashMap
;
10 import java
.util
.HashSet
;
11 import java
.util
.Hashtable
;
12 import java
.util
.LinkedHashSet
;
15 import java
.util
.UUID
;
17 import javax
.jcr
.Repository
;
18 import javax
.jcr
.Session
;
19 import javax
.naming
.InvalidNameException
;
20 import javax
.naming
.ldap
.LdapName
;
21 import javax
.security
.auth
.Subject
;
22 import javax
.security
.auth
.login
.LoginContext
;
23 import javax
.security
.auth
.login
.LoginException
;
25 import org
.apache
.commons
.logging
.Log
;
26 import org
.apache
.commons
.logging
.LogFactory
;
27 import org
.argeo
.cms
.CmsException
;
28 import org
.argeo
.cms
.auth
.CmsSession
;
29 import org
.argeo
.jcr
.JcrUtils
;
30 import org
.argeo
.node
.NodeConstants
;
31 import org
.argeo
.node
.security
.NodeSecurityUtils
;
32 import org
.osgi
.framework
.BundleContext
;
33 import org
.osgi
.framework
.FrameworkUtil
;
34 import org
.osgi
.framework
.InvalidSyntaxException
;
35 import org
.osgi
.framework
.ServiceReference
;
36 import org
.osgi
.framework
.ServiceRegistration
;
37 import org
.osgi
.service
.useradmin
.Authorization
;
39 public class CmsSessionImpl
implements CmsSession
{
40 private final static BundleContext bc
= FrameworkUtil
.getBundle(CmsSessionImpl
.class).getBundleContext();
41 private final static Log log
= LogFactory
.getLog(CmsSessionImpl
.class);
43 // private final Subject initialSubject;
44 private final AccessControlContext initialContext
;
45 private final UUID uuid
;
46 private final String localSessionId
;
47 private final Authorization authorization
;
48 private final LdapName userDn
;
49 private final boolean anonymous
;
51 private final ZonedDateTime creationTime
;
52 private ZonedDateTime end
;
54 private ServiceRegistration
<CmsSession
> serviceRegistration
;
56 private Map
<String
, Session
> dataSessions
= new HashMap
<>();
57 private Set
<String
> dataSessionsInUse
= new HashSet
<>();
58 private LinkedHashSet
<Session
> additionalDataSessions
= new LinkedHashSet
<>();
60 public CmsSessionImpl(Subject initialSubject
, Authorization authorization
, String localSessionId
) {
61 this.creationTime
= ZonedDateTime
.now();
62 this.initialContext
= Subject
.doAs(initialSubject
, new PrivilegedAction
<AccessControlContext
>() {
65 public AccessControlContext
run() {
66 return AccessController
.getContext();
70 // this.initialSubject = initialSubject;
71 this.localSessionId
= localSessionId
;
72 this.authorization
= authorization
;
73 if (authorization
.getName() != null)
75 this.userDn
= new LdapName(authorization
.getName());
76 this.anonymous
= false;
77 } catch (InvalidNameException e
) {
78 throw new CmsException("Invalid user name " + authorization
.getName(), e
);
81 this.userDn
= NodeSecurityUtils
.ROLE_ANONYMOUS_NAME
;
82 this.anonymous
= true;
84 this.uuid
= UUID
.randomUUID();
85 // register as service
86 Hashtable
<String
, String
> props
= new Hashtable
<>();
87 props
.put(CmsSession
.USER_DN
, userDn
.toString());
88 props
.put(CmsSession
.SESSION_UUID
, uuid
.toString());
89 props
.put(CmsSession
.SESSION_LOCAL_ID
, localSessionId
);
90 serviceRegistration
= bc
.registerService(CmsSession
.class, this, props
);
93 public synchronized void close() {
94 end
= ZonedDateTime
.now();
95 serviceRegistration
.unregister();
97 // TODO check data session in use ?
98 for (String path
: dataSessions
.keySet())
99 JcrUtils
.logoutQuietly(dataSessions
.get(path
));
100 for (Session session
: additionalDataSessions
)
101 JcrUtils
.logoutQuietly(session
);
106 lc
= new LoginContext(NodeConstants
.LOGIN_CONTEXT_ANONYMOUS
, getSubject());
108 lc
= new LoginContext(NodeConstants
.LOGIN_CONTEXT_USER
, getSubject());
111 } catch (LoginException e
) {
112 log
.warn("Could not logout " + getSubject() + ": " + e
);
117 private Subject
getSubject() {
118 return Subject
.getSubject(initialContext
);
121 public synchronized Session
getDataSession(String cn
, String workspace
, Repository repository
) {
122 // FIXME make it more robust
123 if (workspace
== null)
125 String path
= cn
+ '/' + workspace
;
126 if (dataSessionsInUse
.contains(path
)) {
129 if (dataSessionsInUse
.contains(path
)) {
130 Session session
= login(repository
, workspace
);
131 additionalDataSessions
.add(session
);
132 if (log
.isTraceEnabled())
133 log
.trace("Additional data session " + path
+ " for " + userDn
);
136 } catch (InterruptedException e
) {
141 Session session
= null;
142 if (dataSessions
.containsKey(path
)) {
143 session
= dataSessions
.get(path
);
145 session
= login(repository
, workspace
);
146 dataSessions
.put(path
, session
);
147 if (log
.isTraceEnabled())
148 log
.trace("New data session " + path
+ " for " + userDn
);
150 dataSessionsInUse
.add(path
);
154 private Session
login(Repository repository
, String workspace
) {
156 return Subject
.doAs(getSubject(), new PrivilegedExceptionAction
<Session
>() {
158 public Session
run() throws Exception
{
159 return repository
.login(workspace
);
162 } catch (Exception e
) {
163 throw new CmsException("Cannot log in " + userDn
+ " to JCR", e
);
167 public synchronized void releaseDataSession(String cn
, Session session
) {
168 if (additionalDataSessions
.contains(session
)) {
169 JcrUtils
.logoutQuietly(session
);
170 additionalDataSessions
.remove(session
);
173 String path
= cn
+ '/' + session
.getWorkspace().getName();
174 if (!dataSessionsInUse
.contains(path
))
175 log
.warn("Data session " + path
+ " was not in use for " + userDn
);
176 dataSessionsInUse
.remove(path
);
177 Session registeredSession
= dataSessions
.get(path
);
178 if (session
!= registeredSession
)
179 log
.warn("Data session " + path
+ " not consistent for " + userDn
);
184 public boolean isValid() {
188 protected boolean isClosed() {
189 return getEnd() != null;
193 public Authorization
getAuthorization() {
194 return authorization
;
198 public UUID
getUuid() {
202 public String
getLocalSessionId() {
203 return localSessionId
;
206 public ServiceRegistration
<CmsSession
> getServiceRegistration() {
207 return serviceRegistration
;
211 public LdapName
getUserDn() {
216 public String
getLocalId() {
217 return localSessionId
;
220 public boolean isAnonymous() {
225 public ZonedDateTime
getCreationTime() {
230 public ZonedDateTime
getEnd() {
234 public String
toString() {
235 return "CMS Session " + userDn
+ " local=" + localSessionId
+ ", uuid=" + uuid
;
238 public static CmsSession
getByLocalId(String localId
) {
239 Collection
<ServiceReference
<CmsSession
>> sr
;
241 sr
= bc
.getServiceReferences(CmsSession
.class, "(" + CmsSession
.SESSION_LOCAL_ID
+ "=" + localId
+ ")");
242 } catch (InvalidSyntaxException e
) {
243 throw new CmsException("Cannot get CMS session for id " + localId
, e
);
245 ServiceReference
<CmsSession
> cmsSessionRef
;
246 if (sr
.size() == 1) {
247 cmsSessionRef
= sr
.iterator().next();
248 return bc
.getService(cmsSessionRef
);
249 } else if (sr
.size() == 0) {
252 throw new CmsException(sr
.size() + " CMS sessions registered for " + localId
);
256 public static CmsSession
getByUuid(String uuid
) {
257 Collection
<ServiceReference
<CmsSession
>> sr
;
259 sr
= bc
.getServiceReferences(CmsSession
.class, "(" + CmsSession
.SESSION_UUID
+ "=" + uuid
+ ")");
260 } catch (InvalidSyntaxException e
) {
261 throw new CmsException("Cannot get CMS session for uuid " + uuid
, e
);
263 ServiceReference
<CmsSession
> cmsSessionRef
;
264 if (sr
.size() == 1) {
265 cmsSessionRef
= sr
.iterator().next();
266 return bc
.getService(cmsSessionRef
);
267 } else if (sr
.size() == 0) {
270 throw new CmsException(sr
.size() + " CMS sessions registered for " + uuid
);
274 public static void closeInvalidSessions() {
275 Collection
<ServiceReference
<CmsSession
>> srs
;
277 srs
= bc
.getServiceReferences(CmsSession
.class, null);
278 for (ServiceReference
<CmsSession
> sr
: srs
) {
279 CmsSession cmsSession
= bc
.getService(sr
);
280 if (!cmsSession
.isValid()) {
281 ((CmsSessionImpl
) cmsSession
).close();
282 if (log
.isDebugEnabled())
283 log
.debug("Closed expired CMS session " + cmsSession
);
286 } catch (InvalidSyntaxException e
) {
287 throw new CmsException("Cannot get CMS sessions", e
);