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