]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.security.core/src/org/argeo/osgi/useradmin/AbstractUserDirectory.java
Use SWT operation mode
[lgpl/argeo-commons.git] / org.argeo.security.core / src / org / argeo / osgi / useradmin / AbstractUserDirectory.java
1 package org.argeo.osgi.useradmin;
2
3 import static org.argeo.osgi.useradmin.LdifName.inetOrgPerson;
4 import static org.argeo.osgi.useradmin.LdifName.objectClass;
5 import static org.argeo.osgi.useradmin.LdifName.organizationalPerson;
6 import static org.argeo.osgi.useradmin.LdifName.person;
7 import static org.argeo.osgi.useradmin.LdifName.top;
8
9 import java.io.File;
10 import java.net.URI;
11 import java.net.URISyntaxException;
12 import java.util.ArrayList;
13 import java.util.Arrays;
14 import java.util.Dictionary;
15 import java.util.Enumeration;
16 import java.util.Hashtable;
17 import java.util.Iterator;
18 import java.util.List;
19
20 import javax.naming.InvalidNameException;
21 import javax.naming.directory.Attributes;
22 import javax.naming.directory.BasicAttribute;
23 import javax.naming.directory.BasicAttributes;
24 import javax.naming.ldap.LdapName;
25 import javax.naming.ldap.Rdn;
26 import javax.transaction.SystemException;
27 import javax.transaction.Transaction;
28 import javax.transaction.TransactionManager;
29 import javax.transaction.xa.Xid;
30
31 import org.osgi.framework.Filter;
32 import org.osgi.framework.FrameworkUtil;
33 import org.osgi.framework.InvalidSyntaxException;
34 import org.osgi.service.useradmin.Authorization;
35 import org.osgi.service.useradmin.Role;
36 import org.osgi.service.useradmin.User;
37 import org.osgi.service.useradmin.UserAdmin;
38
39 /** Base class for a {@link UserDirectory}. */
40 abstract class AbstractUserDirectory implements UserAdmin, UserDirectory {
41 private final Hashtable<String, Object> properties;
42 private final String baseDn;
43 private final String userObjectClass;
44 private final String groupObjectClass;
45
46 private final boolean readOnly;
47 private final URI uri;
48
49 private UserAdmin externalRoles;
50 private List<String> indexedUserProperties = Arrays.asList(new String[] {
51 LdifName.uid.name(), LdifName.mail.name(), LdifName.cn.name() });
52
53 private String memberAttributeId = "member";
54 private List<String> credentialAttributeIds = Arrays
55 .asList(new String[] { LdifName.userpassword.name() });
56
57 private TransactionManager transactionManager;
58 private ThreadLocal<UserDirectoryWorkingCopy> workingCopy = new ThreadLocal<UserDirectoryWorkingCopy>();
59 private Xid editingTransactionXid = null;
60
61 AbstractUserDirectory(Dictionary<String, ?> props) {
62 properties = new Hashtable<String, Object>();
63 for (Enumeration<String> keys = props.keys(); keys.hasMoreElements();) {
64 String key = keys.nextElement();
65 properties.put(key, props.get(key));
66 }
67
68 String uriStr = UserAdminConf.uri.getValue(properties);
69 if (uriStr == null)
70 uri = null;
71 else
72 try {
73 uri = new URI(uriStr);
74 } catch (URISyntaxException e) {
75 throw new UserDirectoryException("Badly formatted URI "
76 + uriStr, e);
77 }
78
79 baseDn = UserAdminConf.baseDn.getValue(properties).toString();
80 String readOnlyStr = UserAdminConf.readOnly.getValue(properties);
81 if (readOnlyStr == null) {
82 readOnly = readOnlyDefault(uri);
83 properties.put(UserAdminConf.readOnly.property(),
84 Boolean.toString(readOnly));
85 } else
86 readOnly = new Boolean(readOnlyStr);
87
88 userObjectClass = UserAdminConf.userObjectClass.getValue(properties);
89 groupObjectClass = UserAdminConf.groupObjectClass.getValue(properties);
90 }
91
92 /** Returns the groups this user is a direct member of. */
93 protected abstract List<LdapName> getDirectGroups(LdapName dn);
94
95 protected abstract Boolean daoHasRole(LdapName dn);
96
97 protected abstract DirectoryUser daoGetRole(LdapName key);
98
99 protected abstract List<DirectoryUser> doGetRoles(Filter f);
100
101 public void init() {
102
103 }
104
105 public void destroy() {
106
107 }
108
109 boolean isEditing() {
110 if (editingTransactionXid == null)
111 return false;
112 return workingCopy.get() != null;
113 }
114
115 protected UserDirectoryWorkingCopy getWorkingCopy() {
116 UserDirectoryWorkingCopy wc = workingCopy.get();
117 if (wc == null)
118 return null;
119 if (wc.getXid() == null) {
120 workingCopy.set(null);
121 return null;
122 }
123 return wc;
124 }
125
126 void checkEdit() {
127 Transaction transaction;
128 try {
129 transaction = transactionManager.getTransaction();
130 } catch (SystemException e) {
131 throw new UserDirectoryException("Cannot get transaction", e);
132 }
133 if (transaction == null)
134 throw new UserDirectoryException(
135 "A transaction needs to be active in order to edit");
136 if (editingTransactionXid == null) {
137 UserDirectoryWorkingCopy wc = new UserDirectoryWorkingCopy(this);
138 try {
139 transaction.enlistResource(wc);
140 editingTransactionXid = wc.getXid();
141 workingCopy.set(wc);
142 } catch (Exception e) {
143 throw new UserDirectoryException("Cannot enlist " + wc, e);
144 }
145 } else {
146 if (workingCopy.get() == null)
147 throw new UserDirectoryException("Transaction "
148 + editingTransactionXid + " already editing");
149 else if (!editingTransactionXid.equals(workingCopy.get().getXid()))
150 throw new UserDirectoryException("Working copy Xid "
151 + workingCopy.get().getXid() + " inconsistent with"
152 + editingTransactionXid);
153 }
154 }
155
156 List<Role> getAllRoles(DirectoryUser user) {
157 List<Role> allRoles = new ArrayList<Role>();
158 if (user != null) {
159 collectRoles(user, allRoles);
160 allRoles.add(user);
161 } else
162 collectAnonymousRoles(allRoles);
163 return allRoles;
164 }
165
166 private void collectRoles(DirectoryUser user, List<Role> allRoles) {
167 for (LdapName groupDn : getDirectGroups(user.getDn())) {
168 // TODO check for loops
169 DirectoryUser group = doGetRole(groupDn);
170 allRoles.add(group);
171 collectRoles(group, allRoles);
172 }
173 }
174
175 private void collectAnonymousRoles(List<Role> allRoles) {
176 // TODO gather anonymous roles
177 }
178
179 // USER ADMIN
180 @Override
181 public Role getRole(String name) {
182 return doGetRole(toDn(name));
183 }
184
185 protected DirectoryUser doGetRole(LdapName dn) {
186 UserDirectoryWorkingCopy wc = getWorkingCopy();
187 DirectoryUser user = daoGetRole(dn);
188 if (wc != null) {
189 if (user == null && wc.getNewUsers().containsKey(dn))
190 user = wc.getNewUsers().get(dn);
191 else if (wc.getDeletedUsers().containsKey(dn))
192 user = null;
193 }
194 return user;
195 }
196
197 @SuppressWarnings("unchecked")
198 @Override
199 public Role[] getRoles(String filter) throws InvalidSyntaxException {
200 UserDirectoryWorkingCopy wc = getWorkingCopy();
201 Filter f = filter != null ? FrameworkUtil.createFilter(filter) : null;
202 List<DirectoryUser> res = doGetRoles(f);
203 if (wc != null) {
204 for (Iterator<DirectoryUser> it = res.iterator(); it.hasNext();) {
205 DirectoryUser user = it.next();
206 LdapName dn = user.getDn();
207 if (wc.getDeletedUsers().containsKey(dn))
208 it.remove();
209 }
210 for (DirectoryUser user : wc.getNewUsers().values()) {
211 if (f == null || f.match(user.getProperties()))
212 res.add(user);
213 }
214 // no need to check modified users,
215 // since doGetRoles was already based on the modified attributes
216 }
217 return res.toArray(new Role[res.size()]);
218 }
219
220 @Override
221 public User getUser(String key, String value) {
222 // TODO check value null or empty
223 List<DirectoryUser> collectedUsers = new ArrayList<DirectoryUser>(
224 getIndexedUserProperties().size());
225 if (key != null) {
226 doGetUser(key, value, collectedUsers);
227 } else {
228 // try dn
229 DirectoryUser user = null;
230 try {
231 user = (DirectoryUser) getRole(value);
232 if (user != null)
233 collectedUsers.add(user);
234 } catch (Exception e) {
235 // silent
236 }
237 // try all indexes
238 for (String attr : getIndexedUserProperties())
239 doGetUser(attr, value, collectedUsers);
240 }
241 if (collectedUsers.size() == 1)
242 return collectedUsers.get(0);
243 return null;
244 }
245
246 protected void doGetUser(String key, String value,
247 List<DirectoryUser> collectedUsers) {
248 try {
249 Filter f = FrameworkUtil.createFilter("(&(" + objectClass + "="
250 + getUserObjectClass() + ")(" + key + "=" + value + "))");
251 List<DirectoryUser> users = doGetRoles(f);
252 collectedUsers.addAll(users);
253 } catch (InvalidSyntaxException e) {
254 throw new UserDirectoryException("Cannot get user with " + key
255 + "=" + value, e);
256 }
257 }
258
259 @Override
260 public Authorization getAuthorization(User user) {
261 return new LdifAuthorization((DirectoryUser) user,
262 getAllRoles((DirectoryUser) user));
263 }
264
265 @Override
266 public Role createRole(String name, int type) {
267 checkEdit();
268 UserDirectoryWorkingCopy wc = getWorkingCopy();
269 LdapName dn = toDn(name);
270 if ((daoHasRole(dn) && !wc.getDeletedUsers().containsKey(dn))
271 || wc.getNewUsers().containsKey(dn))
272 throw new UserDirectoryException("Already a role " + name);
273 BasicAttributes attrs = new BasicAttributes();
274 attrs.put("dn", dn.toString());
275 Rdn nameRdn = dn.getRdn(dn.size() - 1);
276 // TODO deal with multiple attr RDN
277 attrs.put(nameRdn.getType(), nameRdn.getValue());
278 if (wc.getDeletedUsers().containsKey(dn)) {
279 wc.getDeletedUsers().remove(dn);
280 wc.getModifiedUsers().put(dn, attrs);
281 } else {
282 wc.getModifiedUsers().put(dn, attrs);
283 DirectoryUser newRole = newRole(dn, type, attrs);
284 wc.getNewUsers().put(dn, newRole);
285 }
286 return getRole(name);
287 }
288
289 protected DirectoryUser newRole(LdapName dn, int type, Attributes attrs) {
290 LdifUser newRole;
291 BasicAttribute objClass = new BasicAttribute(objectClass.name());
292 if (type == Role.USER) {
293 String userObjClass = getUserObjectClass();
294 objClass.add(userObjClass);
295 if (inetOrgPerson.name().equals(userObjClass)) {
296 objClass.add(organizationalPerson.name());
297 objClass.add(person.name());
298 } else if (organizationalPerson.name().equals(userObjClass)) {
299 objClass.add(person.name());
300 }
301 objClass.add(top);
302 attrs.put(objClass);
303 newRole = new LdifUser(this, dn, attrs);
304 } else if (type == Role.GROUP) {
305 objClass.add(getGroupObjectClass());
306 objClass.add(top);
307 attrs.put(objClass);
308 newRole = new LdifGroup(this, dn, attrs);
309 } else
310 throw new UserDirectoryException("Unsupported type " + type);
311 return newRole;
312 }
313
314 @Override
315 public boolean removeRole(String name) {
316 checkEdit();
317 UserDirectoryWorkingCopy wc = getWorkingCopy();
318 LdapName dn = toDn(name);
319 boolean actuallyDeleted;
320 if (daoHasRole(dn) || wc.getNewUsers().containsKey(dn)) {
321 DirectoryUser user = (DirectoryUser) getRole(name);
322 wc.getDeletedUsers().put(dn, user);
323 actuallyDeleted = true;
324 } else {// just removing from groups (e.g. system roles)
325 actuallyDeleted = false;
326 }
327 for (LdapName groupDn : getDirectGroups(dn)) {
328 DirectoryUser group = doGetRole(groupDn);
329 group.getAttributes().get(getMemberAttributeId())
330 .remove(dn.toString());
331 }
332 return actuallyDeleted;
333 }
334
335 // TRANSACTION
336 protected void prepare(UserDirectoryWorkingCopy wc) {
337
338 }
339
340 protected void commit(UserDirectoryWorkingCopy wc) {
341
342 }
343
344 protected void rollback(UserDirectoryWorkingCopy wc) {
345
346 }
347
348 void clearEditingTransactionXid() {
349 editingTransactionXid = null;
350 }
351
352 // UTILITIES
353 protected LdapName toDn(String name) {
354 try {
355 return new LdapName(name);
356 } catch (InvalidNameException e) {
357 throw new UserDirectoryException("Badly formatted name", e);
358 }
359 }
360
361 // GETTERS
362
363 String getMemberAttributeId() {
364 return memberAttributeId;
365 }
366
367 List<String> getCredentialAttributeIds() {
368 return credentialAttributeIds;
369 }
370
371 protected URI getUri() {
372 return uri;
373 }
374
375 protected List<String> getIndexedUserProperties() {
376 return indexedUserProperties;
377 }
378
379 protected void setIndexedUserProperties(List<String> indexedUserProperties) {
380 this.indexedUserProperties = indexedUserProperties;
381 }
382
383 private static boolean readOnlyDefault(URI uri) {
384 if (uri == null)
385 return true;
386 if (uri.getScheme().equals("file")) {
387 File file = new File(uri);
388 if (file.exists())
389 return !file.canWrite();
390 else
391 return !file.getParentFile().canWrite();
392 }
393 return true;
394 }
395
396 public boolean isReadOnly() {
397 return readOnly;
398 }
399
400 UserAdmin getExternalRoles() {
401 return externalRoles;
402 }
403
404 public String getBaseDn() {
405 return baseDn;
406 }
407
408 protected String getUserObjectClass() {
409 return userObjectClass;
410 }
411
412 protected String getGroupObjectClass() {
413 return groupObjectClass;
414 }
415
416 public Dictionary<String, ?> getProperties() {
417 return properties;
418 }
419
420 public void setExternalRoles(UserAdmin externalRoles) {
421 this.externalRoles = externalRoles;
422 }
423
424 public void setTransactionManager(TransactionManager transactionManager) {
425 this.transactionManager = transactionManager;
426 }
427
428 }