]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.cms/src/org/argeo/cms/internal/auth/CmsSessionImpl.java
Fix MANIFEST generation issues.
[lgpl/argeo-commons.git] / org.argeo.cms / src / org / argeo / cms / internal / auth / CmsSessionImpl.java
1 package org.argeo.cms.internal.auth;
2
3 import java.io.Serializable;
4 import java.security.AccessControlContext;
5 import java.security.AccessController;
6 import java.security.PrivilegedAction;
7 import java.time.ZonedDateTime;
8 import java.util.ArrayList;
9 import java.util.Collection;
10 import java.util.Collections;
11 import java.util.HashMap;
12 import java.util.Hashtable;
13 import java.util.List;
14 import java.util.Locale;
15 import java.util.Map;
16 import java.util.Set;
17 import java.util.UUID;
18 import java.util.function.Consumer;
19
20 import javax.crypto.SecretKey;
21 import javax.naming.InvalidNameException;
22 import javax.naming.ldap.LdapName;
23 import javax.security.auth.Subject;
24 import javax.security.auth.login.LoginContext;
25 import javax.security.auth.login.LoginException;
26 import javax.security.auth.x500.X500Principal;
27
28 import org.argeo.api.cms.CmsAuth;
29 import org.argeo.api.cms.CmsLog;
30 import org.argeo.api.cms.CmsSession;
31 import org.argeo.cms.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;
38
39 /** Default CMS session implementation. */
40 public class CmsSessionImpl implements CmsSession, Serializable {
41 private static final long serialVersionUID = 1867719354246307225L;
42 private final static BundleContext bc = FrameworkUtil.getBundle(CmsSessionImpl.class).getBundleContext();
43 private final static CmsLog log = CmsLog.getLog(CmsSessionImpl.class);
44
45 // private final Subject initialSubject;
46 private transient AccessControlContext accessControlContext;
47 private final UUID uuid;
48 private final String localSessionId;
49 private Authorization authorization;
50 private final LdapName userDn;
51 private final boolean anonymous;
52
53 private final ZonedDateTime creationTime;
54 private ZonedDateTime end;
55 private final Locale locale;
56
57 private ServiceRegistration<CmsSession> serviceRegistration;
58
59 private Map<String, Object> views = new HashMap<>();
60
61 private List<Consumer<CmsSession>> onCloseCallbacks = Collections.synchronizedList(new ArrayList<>());
62
63 public CmsSessionImpl(Subject initialSubject, Authorization authorization, Locale locale, String localSessionId) {
64 this.creationTime = ZonedDateTime.now();
65 this.locale = locale;
66 this.accessControlContext = Subject.doAs(initialSubject, new PrivilegedAction<AccessControlContext>() {
67
68 @Override
69 public AccessControlContext run() {
70 return AccessController.getContext();
71 }
72
73 });
74 // this.initialSubject = initialSubject;
75 this.localSessionId = localSessionId;
76 this.authorization = authorization;
77 if (authorization.getName() != null)
78 try {
79 this.userDn = new LdapName(authorization.getName());
80 this.anonymous = false;
81 } catch (InvalidNameException e) {
82 throw new IllegalArgumentException("Invalid user name " + authorization.getName(), e);
83 }
84 else {
85 this.userDn = NodeSecurityUtils.ROLE_ANONYMOUS_NAME;
86 this.anonymous = true;
87 }
88 this.uuid = UUID.randomUUID();
89 // register as service
90 Hashtable<String, String> props = new Hashtable<>();
91 props.put(CmsSession.USER_DN, userDn.toString());
92 props.put(CmsSession.SESSION_UUID, uuid.toString());
93 props.put(CmsSession.SESSION_LOCAL_ID, localSessionId);
94 serviceRegistration = bc.registerService(CmsSession.class, this, props);
95 }
96
97 public void close() {
98 end = ZonedDateTime.now();
99 serviceRegistration.unregister();
100
101 for (Consumer<CmsSession> onClose : onCloseCallbacks) {
102 onClose.accept(this);
103 }
104
105 try {
106 LoginContext lc;
107 if (isAnonymous()) {
108 lc = new LoginContext(CmsAuth.LOGIN_CONTEXT_ANONYMOUS, getSubject());
109 } else {
110 lc = new LoginContext(CmsAuth.LOGIN_CONTEXT_USER, getSubject());
111 }
112 lc.logout();
113 } catch (LoginException e) {
114 log.warn("Could not logout " + getSubject() + ": " + e);
115 } finally {
116 accessControlContext = null;
117 }
118 log.debug("Closed " + this);
119 }
120
121 @Override
122 public void addOnCloseCallback(Consumer<CmsSession> onClose) {
123 onCloseCallbacks.add(onClose);
124 }
125
126 public Subject getSubject() {
127 return Subject.getSubject(accessControlContext);
128 }
129
130 public Set<SecretKey> getSecretKeys() {
131 checkValid();
132 return getSubject().getPrivateCredentials(SecretKey.class);
133 }
134
135 @Override
136 public boolean isValid() {
137 return !isClosed();
138 }
139
140 private void checkValid() {
141 if (!isValid())
142 throw new IllegalStateException("CMS session " + uuid + " is not valid since " + end);
143 }
144
145 final protected boolean isClosed() {
146 return getEnd() != null;
147 }
148
149 public Authorization getAuthorization() {
150 checkValid();
151 return authorization;
152 }
153
154 @Override
155 public String getDisplayName() {
156 return authorization.toString();
157 }
158
159 @Override
160 public UUID getUuid() {
161 return uuid;
162 }
163
164 @Override
165 public LdapName getUserDn() {
166 return userDn;
167 }
168
169 @Override
170 public String getUserRole() {
171 return new X500Principal(authorization.getName()).getName();
172 }
173
174 @Override
175 public String getLocalId() {
176 return localSessionId;
177 }
178
179 @Override
180 public boolean isAnonymous() {
181 return anonymous;
182 }
183
184 @Override
185 public Locale getLocale() {
186 return locale;
187 }
188
189 @Override
190 public ZonedDateTime getCreationTime() {
191 return creationTime;
192 }
193
194 @Override
195 public ZonedDateTime getEnd() {
196 return end;
197 }
198
199 @Override
200 public void registerView(String uid, Object view) {
201 checkValid();
202 if (views.containsKey(uid))
203 throw new IllegalArgumentException("View " + uid + " is already registered.");
204 views.put(uid, view);
205 }
206
207 public String toString() {
208 return "CMS Session " + userDn + " local=" + localSessionId + ", uuid=" + uuid;
209 }
210
211 public static CmsSessionImpl getByLocalId(String localId) {
212 Collection<ServiceReference<CmsSession>> sr;
213 try {
214 sr = bc.getServiceReferences(CmsSession.class, "(" + CmsSession.SESSION_LOCAL_ID + "=" + localId + ")");
215 } catch (InvalidSyntaxException e) {
216 throw new IllegalArgumentException("Cannot get CMS session for id " + localId, e);
217 }
218 ServiceReference<CmsSession> cmsSessionRef;
219 if (sr.size() == 1) {
220 cmsSessionRef = sr.iterator().next();
221 return (CmsSessionImpl) bc.getService(cmsSessionRef);
222 } else if (sr.size() == 0) {
223 return null;
224 } else
225 throw new IllegalStateException(sr.size() + " CMS sessions registered for " + localId);
226
227 }
228
229 public static CmsSessionImpl getByUuid(Object uuid) {
230 Collection<ServiceReference<CmsSession>> sr;
231 try {
232 sr = bc.getServiceReferences(CmsSession.class, "(" + CmsSession.SESSION_UUID + "=" + uuid + ")");
233 } catch (InvalidSyntaxException e) {
234 throw new IllegalArgumentException("Cannot get CMS session for uuid " + uuid, e);
235 }
236 ServiceReference<CmsSession> cmsSessionRef;
237 if (sr.size() == 1) {
238 cmsSessionRef = sr.iterator().next();
239 return (CmsSessionImpl) bc.getService(cmsSessionRef);
240 } else if (sr.size() == 0) {
241 return null;
242 } else
243 throw new IllegalStateException(sr.size() + " CMS sessions registered for " + uuid);
244
245 }
246
247 public static void closeInvalidSessions() {
248 Collection<ServiceReference<CmsSession>> srs;
249 try {
250 srs = bc.getServiceReferences(CmsSession.class, null);
251 for (ServiceReference<CmsSession> sr : srs) {
252 CmsSession cmsSession = bc.getService(sr);
253 if (!cmsSession.isValid()) {
254 ((CmsSessionImpl) cmsSession).close();
255 if (log.isDebugEnabled())
256 log.debug("Closed expired CMS session " + cmsSession);
257 }
258 }
259 } catch (InvalidSyntaxException e) {
260 throw new IllegalArgumentException("Cannot get CMS sessions", e);
261 }
262 }
263 }