]> git.argeo.org Git - lgpl/argeo-commons.git/blob - CmsSessionImpl.java
42f5e6ae08def8ed2e93779f469b4d5b7a19d31e
[lgpl/argeo-commons.git] / CmsSessionImpl.java
1 package org.argeo.cms.internal.auth;
2
3 import java.security.AccessControlContext;
4 import java.security.AccessController;
5 import java.security.PrivilegedAction;
6 import java.security.PrivilegedExceptionAction;
7 import java.util.Collection;
8 import java.util.HashMap;
9 import java.util.HashSet;
10 import java.util.Hashtable;
11 import java.util.LinkedHashSet;
12 import java.util.Map;
13 import java.util.Set;
14 import java.util.UUID;
15
16 import javax.jcr.Repository;
17 import javax.jcr.Session;
18 import javax.naming.InvalidNameException;
19 import javax.naming.ldap.LdapName;
20 import javax.security.auth.Subject;
21
22 import org.apache.commons.logging.Log;
23 import org.apache.commons.logging.LogFactory;
24 import org.argeo.cms.CmsException;
25 import org.argeo.cms.auth.CmsSession;
26 import org.argeo.jcr.JcrUtils;
27 import org.argeo.node.security.NodeSecurityUtils;
28 import org.osgi.framework.BundleContext;
29 import org.osgi.framework.FrameworkUtil;
30 import org.osgi.framework.InvalidSyntaxException;
31 import org.osgi.framework.ServiceReference;
32 import org.osgi.framework.ServiceRegistration;
33 import org.osgi.service.useradmin.Authorization;
34
35 public class CmsSessionImpl implements CmsSession {
36 private final static BundleContext bc = FrameworkUtil.getBundle(CmsSessionImpl.class).getBundleContext();
37 private final static Log log = LogFactory.getLog(CmsSessionImpl.class);
38
39 // private final Subject initialSubject;
40 private final AccessControlContext initialContext;
41 private final UUID uuid;
42 private final String localSessionId;
43 private final Authorization authorization;
44 private final LdapName userDn;
45 private final boolean anonymous;
46
47 private ServiceRegistration<CmsSession> serviceRegistration;
48
49 private Map<String, Session> dataSessions = new HashMap<>();
50 private Set<String> dataSessionsInUse = new HashSet<>();
51 private LinkedHashSet<Session> additionalDataSessions = new LinkedHashSet<>();
52
53 public CmsSessionImpl(Subject initialSubject, Authorization authorization, String localSessionId) {
54 this.initialContext = Subject.doAs(initialSubject, new PrivilegedAction<AccessControlContext>() {
55
56 @Override
57 public AccessControlContext run() {
58 return AccessController.getContext();
59 }
60
61 });
62 // this.initialSubject = initialSubject;
63 this.localSessionId = localSessionId;
64 this.authorization = authorization;
65 if (authorization.getName() != null)
66 try {
67 this.userDn = new LdapName(authorization.getName());
68 this.anonymous = false;
69 } catch (InvalidNameException e) {
70 throw new CmsException("Invalid user name " + authorization.getName(), e);
71 }
72 else {
73 this.userDn = NodeSecurityUtils.ROLE_ANONYMOUS_NAME;
74 this.anonymous = true;
75 }
76 this.uuid = UUID.randomUUID();
77 // register as service
78 Hashtable<String, String> props = new Hashtable<>();
79 props.put(CmsSession.USER_DN, userDn.toString());
80 props.put(CmsSession.SESSION_UUID, uuid.toString());
81 props.put(CmsSession.SESSION_LOCAL_ID, localSessionId);
82 serviceRegistration = bc.registerService(CmsSession.class, this, props);
83 }
84
85 public synchronized void cleanUp() {
86 serviceRegistration.unregister();
87
88 // TODO check data session in use ?
89 for (String path : dataSessions.keySet())
90 JcrUtils.logoutQuietly(dataSessions.get(path));
91 for (Session session : additionalDataSessions)
92 JcrUtils.logoutQuietly(session);
93 notifyAll();
94 }
95
96 @Override
97 public synchronized Session getDataSession(String cn, String workspace, Repository repository) {
98 // FIXME make it more robust
99 if (workspace == null)
100 workspace = "main";
101 String path = cn + '/' + workspace;
102 if (dataSessionsInUse.contains(path)) {
103 try {
104 wait(1000);
105 if (dataSessionsInUse.contains(path)) {
106 Session session = login(repository, workspace);
107 additionalDataSessions.add(session);
108 if (log.isTraceEnabled())
109 log.trace("Additional data session " + path + " for " + userDn);
110 return session;
111 }
112 } catch (InterruptedException e) {
113 // silent
114 }
115 }
116
117 Session session = null;
118 if (dataSessions.containsKey(path)) {
119 session = dataSessions.get(path);
120 } else {
121 session = login(repository, workspace);
122 dataSessions.put(path, session);
123 if (log.isTraceEnabled())
124 log.trace("New data session " + path + " for " + userDn);
125 }
126 dataSessionsInUse.add(path);
127 return session;
128 }
129
130 private Session login(Repository repository, String workspace) {
131 try {
132 Subject initialSubject = Subject.getSubject(initialContext);
133 return Subject.doAs(initialSubject, new PrivilegedExceptionAction<Session>() {
134 @Override
135 public Session run() throws Exception {
136 return repository.login(workspace);
137 }
138 });
139 } catch (Exception e) {
140 throw new CmsException("Cannot log in " + userDn + " to JCR", e);
141 }
142 }
143
144 @Override
145 public synchronized void releaseDataSession(String cn, Session session) {
146 if (additionalDataSessions.contains(session)) {
147 JcrUtils.logoutQuietly(session);
148 additionalDataSessions.remove(session);
149 return;
150 }
151 String path = cn + '/' + session.getWorkspace().getName();
152 if (!dataSessionsInUse.contains(path))
153 log.warn("Data session " + path + " was not in use for " + userDn);
154 dataSessionsInUse.remove(path);
155 Session registeredSession = dataSessions.get(path);
156 if (session != registeredSession)
157 log.warn("Data session " + path + " not consistent for " + userDn);
158 notifyAll();
159 }
160
161 @Override
162 public Authorization getAuthorization() {
163 return authorization;
164 }
165
166 @Override
167 public UUID getUuid() {
168 return uuid;
169 }
170
171 public String getLocalSessionId() {
172 return localSessionId;
173 }
174
175 public ServiceRegistration<CmsSession> getServiceRegistration() {
176 return serviceRegistration;
177 }
178
179 @Override
180 public LdapName getUserDn() {
181 return userDn;
182 }
183
184 @Override
185 public String getLocalId() {
186 return localSessionId;
187 }
188
189 public boolean isAnonymous() {
190 return anonymous;
191 }
192
193 public String toString() {
194 return "CMS Session local=" + localSessionId + ", uuid=" + uuid;
195 }
196
197 public static CmsSession getByLocalId(String localId) {
198 Collection<ServiceReference<CmsSession>> sr;
199 try {
200 sr = bc.getServiceReferences(CmsSession.class, "(" + CmsSession.SESSION_LOCAL_ID + "=" + localId + ")");
201 } catch (InvalidSyntaxException e) {
202 throw new CmsException("Cannot get CMS session for id " + localId, e);
203 }
204 ServiceReference<CmsSession> cmsSessionRef;
205 if (sr.size() == 1) {
206 cmsSessionRef = sr.iterator().next();
207 return bc.getService(cmsSessionRef);
208 } else if (sr.size() == 0) {
209 return null;
210 } else
211 throw new CmsException(sr.size() + " CMS sessions registered for " + localId);
212
213 }
214
215 public static CmsSession getByUuid(String uuid) {
216 Collection<ServiceReference<CmsSession>> sr;
217 try {
218 sr = bc.getServiceReferences(CmsSession.class, "(" + CmsSession.SESSION_UUID + "=" + uuid + ")");
219 } catch (InvalidSyntaxException e) {
220 throw new CmsException("Cannot get CMS session for uuid " + uuid, e);
221 }
222 ServiceReference<CmsSession> cmsSessionRef;
223 if (sr.size() == 1) {
224 cmsSessionRef = sr.iterator().next();
225 return bc.getService(cmsSessionRef);
226 } else if (sr.size() == 0) {
227 return null;
228 } else
229 throw new CmsException(sr.size() + " CMS sessions registered for " + uuid);
230
231 }
232 }