1 package org
.argeo
.osgi
.useradmin
;
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
;
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
;
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
;
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
;
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);
44 private final Hashtable
<String
, Object
> properties
;
45 private final LdapName baseDn
;
46 private final String userObjectClass
, userBase
, groupObjectClass
, groupBase
;
48 private final boolean readOnly
;
49 private final URI uri
;
51 private UserAdmin externalRoles
;
52 private List
<String
> indexedUserProperties
= Arrays
53 .asList(new String
[] { LdifName
.uid
.name(), LdifName
.mail
.name(), LdifName
.cn
.name() });
55 private String memberAttributeId
= "member";
56 private List
<String
> credentialAttributeIds
= Arrays
.asList(new String
[] { LdifName
.userPassword
.name() });
59 private TransactionManager transactionManager
;
60 private WcXaResource xaResource
= new WcXaResource(this);
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
));
69 String uriStr
= UserAdminConf
.uri
.getValue(properties
);
74 uri
= new URI(uriStr
);
75 } catch (URISyntaxException e
) {
76 throw new UserDirectoryException("Badly formatted URI " + uriStr
, e
);
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
);
84 String readOnlyStr
= UserAdminConf
.readOnly
.getValue(properties
);
85 if (readOnlyStr
== null) {
86 readOnly
= readOnlyDefault(uri
);
87 properties
.put(UserAdminConf
.readOnly
.name(), Boolean
.toString(readOnly
));
89 readOnly
= new Boolean(readOnlyStr
);
91 userObjectClass
= UserAdminConf
.userObjectClass
.getValue(properties
);
92 userBase
= UserAdminConf
.userBase
.getValue(properties
);
93 groupObjectClass
= UserAdminConf
.groupObjectClass
.getValue(properties
);
94 groupBase
= UserAdminConf
.groupBase
.getValue(properties
);
97 /** Returns the groups this user is a direct member of. */
98 protected abstract List
<LdapName
> getDirectGroups(LdapName dn
);
100 protected abstract Boolean
daoHasRole(LdapName dn
);
102 protected abstract DirectoryUser
daoGetRole(LdapName key
);
104 protected abstract List
<DirectoryUser
> doGetRoles(Filter f
);
110 public void destroy() {
114 protected boolean isEditing() {
115 return xaResource
.wc() != null;
118 protected UserDirectoryWorkingCopy
getWorkingCopy() {
119 UserDirectoryWorkingCopy wc
= xaResource
.wc();
125 protected void checkEdit() {
126 Transaction transaction
;
128 transaction
= transactionManager
.getTransaction();
129 } catch (SystemException e
) {
130 throw new UserDirectoryException("Cannot get transaction", e
);
132 if (transaction
== null)
133 throw new UserDirectoryException("A transaction needs to be active in order to edit");
134 if (xaResource
.wc() == null) {
136 transaction
.enlistResource(xaResource
);
137 } catch (Exception e
) {
138 throw new UserDirectoryException("Cannot enlist " + xaResource
, e
);
144 protected List
<Role
> getAllRoles(DirectoryUser user
) {
145 List
<Role
> allRoles
= new ArrayList
<Role
>();
147 collectRoles(user
, allRoles
);
150 collectAnonymousRoles(allRoles
);
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
);
159 collectRoles(group
, allRoles
);
163 private void collectAnonymousRoles(List
<Role
> allRoles
) {
164 // TODO gather anonymous roles
169 public Role
getRole(String name
) {
170 return doGetRole(toDn(name
));
173 protected DirectoryUser
doGetRole(LdapName dn
) {
174 UserDirectoryWorkingCopy wc
= getWorkingCopy();
175 DirectoryUser user
= daoGetRole(dn
);
177 if (user
== null && wc
.getNewUsers().containsKey(dn
))
178 user
= wc
.getNewUsers().get(dn
);
179 else if (wc
.getDeletedUsers().containsKey(dn
))
185 @SuppressWarnings("unchecked")
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
);
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
))
198 for (DirectoryUser user
: wc
.getNewUsers().values()) {
199 if (f
== null || f
.match(user
.getProperties()))
202 // no need to check modified users,
203 // since doGetRoles was already based on the modified attributes
205 return res
.toArray(new Role
[res
.size()]);
209 public User
getUser(String key
, String value
) {
210 // TODO check value null or empty
211 List
<DirectoryUser
> collectedUsers
= new ArrayList
<DirectoryUser
>(getIndexedUserProperties().size());
213 doGetUser(key
, value
, collectedUsers
);
216 DirectoryUser user
= null;
218 user
= (DirectoryUser
) getRole(value
);
220 collectedUsers
.add(user
);
221 } catch (Exception e
) {
225 for (String attr
: getIndexedUserProperties())
226 doGetUser(attr
, value
, collectedUsers
);
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
);
235 protected void doGetUser(String key
, String value
, List
<DirectoryUser
> collectedUsers
) {
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
);
246 public Authorization
getAuthorization(User user
) {
247 return new LdifAuthorization((DirectoryUser
) user
, getAllRoles((DirectoryUser
) user
));
251 public Role
createRole(String name
, int type
) {
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
);
266 wc
.getModifiedUsers().put(dn
, attrs
);
267 DirectoryUser newRole
= newRole(dn
, type
, attrs
);
268 wc
.getNewUsers().put(dn
, newRole
);
270 return getRole(name
);
273 protected DirectoryUser
newRole(LdapName dn
, int type
, Attributes attrs
) {
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());
285 objClass
.add(top
.name());
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());
294 newRole
= new LdifGroup(this, dn
, attrs
);
296 throw new UserDirectoryException("Unsupported type " + type
);
301 public boolean removeRole(String name
) {
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;
313 for (LdapName groupDn
: getDirectGroups(dn
)) {
314 DirectoryUser group
= doGetRole(groupDn
);
315 group
.getAttributes().get(getMemberAttributeId()).remove(dn
.toString());
317 return actuallyDeleted
;
321 protected void prepare(UserDirectoryWorkingCopy wc
) {
325 protected void commit(UserDirectoryWorkingCopy wc
) {
329 protected void rollback(UserDirectoryWorkingCopy wc
) {
334 protected LdapName
toDn(String name
) {
336 return new LdapName(name
);
337 } catch (InvalidNameException e
) {
338 throw new UserDirectoryException("Badly formatted name", e
);
343 protected String
getMemberAttributeId() {
344 return memberAttributeId
;
347 protected List
<String
> getCredentialAttributeIds() {
348 return credentialAttributeIds
;
351 protected URI
getUri() {
355 protected List
<String
> getIndexedUserProperties() {
356 return indexedUserProperties
;
359 protected void setIndexedUserProperties(List
<String
> indexedUserProperties
) {
360 this.indexedUserProperties
= indexedUserProperties
;
363 private static boolean readOnlyDefault(URI uri
) {
366 if (uri
.getScheme().equals("file")) {
367 File file
= new File(uri
);
369 return !file
.canWrite();
371 return !file
.getParentFile().canWrite();
376 public boolean isReadOnly() {
380 protected UserAdmin
getExternalRoles() {
381 return externalRoles
;
384 public LdapName
getBaseDn() {
385 // always clone so that the property is not modified by reference
386 return (LdapName
) baseDn
.clone();
389 /** dn can be null, in that case a default should be returned. */
390 public String
getUserObjectClass() {
391 return userObjectClass
;
394 public String
getUserBase() {
398 protected String
newUserObjectClass(LdapName dn
) {
399 return getUserObjectClass();
402 public String
getGroupObjectClass() {
403 return groupObjectClass
;
406 public String
getGroupBase() {
410 public Dictionary
<String
, Object
> getProperties() {
414 public void setExternalRoles(UserAdmin externalRoles
) {
415 this.externalRoles
= externalRoles
;
418 public void setTransactionManager(TransactionManager transactionManager
) {
419 this.transactionManager
= transactionManager
;
422 public WcXaResource
getXaResource() {