1 package org
.argeo
.osgi
.useradmin
;
3 import static org
.argeo
.naming
.LdapAttrs
.objectClass
;
4 import static org
.argeo
.naming
.LdapObjs
.inetOrgPerson
;
7 import java
.io
.FileOutputStream
;
8 import java
.io
.IOException
;
9 import java
.io
.InputStream
;
10 import java
.io
.OutputStream
;
12 import java
.net
.URISyntaxException
;
13 import java
.util
.ArrayList
;
14 import java
.util
.Collections
;
15 import java
.util
.Dictionary
;
16 import java
.util
.HashSet
;
17 import java
.util
.Hashtable
;
18 import java
.util
.List
;
20 import java
.util
.SortedMap
;
21 import java
.util
.TreeMap
;
23 import javax
.naming
.NameNotFoundException
;
24 import javax
.naming
.NamingEnumeration
;
25 import javax
.naming
.directory
.Attributes
;
26 import javax
.naming
.ldap
.LdapName
;
27 import javax
.transaction
.TransactionManager
;
29 import org
.argeo
.naming
.LdifParser
;
30 import org
.argeo
.naming
.LdifWriter
;
31 import org
.osgi
.framework
.Filter
;
32 import org
.osgi
.service
.useradmin
.Role
;
33 import org
.osgi
.service
.useradmin
.User
;
36 * A user admin based on a LDIF files. Requires a {@link TransactionManager} and
37 * an open transaction for write access.
39 public class LdifUserAdmin
extends AbstractUserDirectory
{
40 private SortedMap
<LdapName
, DirectoryUser
> users
= new TreeMap
<LdapName
, DirectoryUser
>();
41 private SortedMap
<LdapName
, DirectoryGroup
> groups
= new TreeMap
<LdapName
, DirectoryGroup
>();
43 public LdifUserAdmin(String uri
, String baseDn
) {
44 this(fromUri(uri
, baseDn
), false);
47 public LdifUserAdmin(Dictionary
<String
, ?
> properties
) {
48 this(properties
, false);
51 protected LdifUserAdmin(Dictionary
<String
, ?
> properties
, boolean scoped
) {
52 super(null, properties
, scoped
);
55 public LdifUserAdmin(URI uri
, Dictionary
<String
, ?
> properties
) {
56 super(uri
, properties
, false);
60 protected AbstractUserDirectory
scope(User user
) {
61 Dictionary
<String
, Object
> credentials
= user
.getCredentials();
62 String username
= (String
) credentials
.get(SHARED_STATE_USERNAME
);
64 username
= user
.getName();
65 Object pwdCred
= credentials
.get(SHARED_STATE_PASSWORD
);
66 byte[] pwd
= (byte[]) pwdCred
;
68 char[] password
= DigestUtils
.bytesToChars(pwd
);
69 User directoryUser
= (User
) getRole(username
);
70 if (!directoryUser
.hasCredential(null, password
))
71 throw new UserDirectoryException("Invalid credentials");
73 throw new UserDirectoryException("Password is required");
75 Dictionary
<String
, Object
> properties
= cloneProperties();
76 properties
.put(UserAdminConf
.readOnly
.name(), "true");
77 LdifUserAdmin scopedUserAdmin
= new LdifUserAdmin(properties
, true);
78 scopedUserAdmin
.groups
= Collections
.unmodifiableSortedMap(groups
);
79 scopedUserAdmin
.users
= Collections
.unmodifiableSortedMap(users
);
80 return scopedUserAdmin
;
83 private static Dictionary
<String
, Object
> fromUri(String uri
, String baseDn
) {
84 Hashtable
<String
, Object
> res
= new Hashtable
<String
, Object
>();
85 res
.put(UserAdminConf
.uri
.name(), uri
);
86 res
.put(UserAdminConf
.baseDn
.name(), baseDn
);
93 URI u
= new URI(getUri());
94 if (u
.getScheme().equals("file")) {
95 File file
= new File(u
);
99 load(u
.toURL().openStream());
100 } catch (Exception e
) {
101 throw new UserDirectoryException("Cannot open URL " + getUri(), e
);
106 if (getUri() == null)
107 throw new UserDirectoryException("Cannot save LDIF user admin: no URI is set");
109 throw new UserDirectoryException("Cannot save LDIF user admin: " + getUri() + " is read-only");
110 try (FileOutputStream out
= new FileOutputStream(new File(new URI(getUri())))) {
112 } catch (IOException
| URISyntaxException e
) {
113 throw new UserDirectoryException("Cannot save user admin to " + getUri(), e
);
117 public void save(OutputStream out
) throws IOException
{
119 LdifWriter ldifWriter
= new LdifWriter(out
);
120 for (LdapName name
: groups
.keySet())
121 ldifWriter
.writeEntry(name
, groups
.get(name
).getAttributes());
122 for (LdapName name
: users
.keySet())
123 ldifWriter
.writeEntry(name
, users
.get(name
).getAttributes());
129 protected void load(InputStream in
) {
134 LdifParser ldifParser
= new LdifParser();
135 SortedMap
<LdapName
, Attributes
> allEntries
= ldifParser
.read(in
);
136 for (LdapName key
: allEntries
.keySet()) {
137 Attributes attributes
= allEntries
.get(key
);
138 // check for inconsistency
139 Set
<String
> lowerCase
= new HashSet
<String
>();
140 NamingEnumeration
<String
> ids
= attributes
.getIDs();
141 while (ids
.hasMoreElements()) {
142 String id
= ids
.nextElement().toLowerCase();
143 if (lowerCase
.contains(id
))
144 throw new UserDirectoryException(key
+ " has duplicate id " + id
);
148 // analyse object classes
149 NamingEnumeration
<?
> objectClasses
= attributes
.get(objectClass
.name()).getAll();
150 // System.out.println(key);
151 objectClasses
: while (objectClasses
.hasMore()) {
152 String objectClass
= objectClasses
.next().toString();
153 // System.out.println(" " + objectClass);
154 if (objectClass
.toLowerCase().equals(inetOrgPerson
.name().toLowerCase())) {
155 users
.put(key
, new LdifUser(this, key
, attributes
));
157 } else if (objectClass
.toLowerCase().equals(getGroupObjectClass().toLowerCase())) {
158 groups
.put(key
, new LdifGroup(this, key
, attributes
));
163 } catch (Exception e
) {
164 throw new UserDirectoryException("Cannot load user admin service from LDIF", e
);
168 public void destroy() {
169 if (users
== null || groups
== null)
170 throw new UserDirectoryException("User directory " + getBaseDn() + " is already destroyed");
176 protected DirectoryUser
daoGetRole(LdapName key
) throws NameNotFoundException
{
177 if (groups
.containsKey(key
))
178 return groups
.get(key
);
179 if (users
.containsKey(key
))
180 return users
.get(key
);
181 throw new NameNotFoundException(key
+ " not persisted");
185 protected Boolean
daoHasRole(LdapName dn
) {
186 return users
.containsKey(dn
) || groups
.containsKey(dn
);
189 protected List
<DirectoryUser
> doGetRoles(Filter f
) {
190 ArrayList
<DirectoryUser
> res
= new ArrayList
<DirectoryUser
>();
192 res
.addAll(users
.values());
193 res
.addAll(groups
.values());
195 for (DirectoryUser user
: users
.values()) {
196 if (f
.match(user
.getProperties()))
199 for (DirectoryUser group
: groups
.values())
200 if (f
.match(group
.getProperties()))
207 protected List
<LdapName
> getDirectGroups(LdapName dn
) {
208 List
<LdapName
> directGroups
= new ArrayList
<LdapName
>();
209 for (LdapName name
: groups
.keySet()) {
210 DirectoryGroup group
= groups
.get(name
);
211 if (group
.getMemberNames().contains(dn
))
212 directGroups
.add(group
.getDn());
218 protected void prepare(UserDirectoryWorkingCopy wc
) {
220 for (LdapName dn
: wc
.getDeletedUsers().keySet()) {
221 if (users
.containsKey(dn
))
223 else if (groups
.containsKey(dn
))
226 throw new UserDirectoryException("User to delete not found " + dn
);
229 for (LdapName dn
: wc
.getNewUsers().keySet()) {
230 DirectoryUser user
= wc
.getNewUsers().get(dn
);
231 if (users
.containsKey(dn
) || groups
.containsKey(dn
))
232 throw new UserDirectoryException("User to create found " + dn
);
233 else if (Role
.USER
== user
.getType())
235 else if (Role
.GROUP
== user
.getType())
236 groups
.put(dn
, (DirectoryGroup
) user
);
238 throw new UserDirectoryException("Unsupported role type " + user
.getType() + " for new user " + dn
);
241 for (LdapName dn
: wc
.getModifiedUsers().keySet()) {
242 Attributes modifiedAttrs
= wc
.getModifiedUsers().get(dn
);
244 if (users
.containsKey(dn
))
245 user
= users
.get(dn
);
246 else if (groups
.containsKey(dn
))
247 user
= groups
.get(dn
);
249 throw new UserDirectoryException("User to modify no found " + dn
);
250 user
.publishAttributes(modifiedAttrs
);
255 protected void commit(UserDirectoryWorkingCopy wc
) {
260 protected void rollback(UserDirectoryWorkingCopy wc
) {