]> git.argeo.org Git - lgpl/argeo-commons.git/blob - LdifUserAdmin.java
8b1206a72b1655b38c6f111e8bd335dc954222ff
[lgpl/argeo-commons.git] / LdifUserAdmin.java
1 package org.argeo.osgi.useradmin;
2
3 import static org.argeo.util.naming.LdapAttrs.objectClass;
4 import static org.argeo.util.naming.LdapObjs.inetOrgPerson;
5
6 import java.io.File;
7 import java.io.FileOutputStream;
8 import java.io.IOException;
9 import java.io.InputStream;
10 import java.io.OutputStream;
11 import java.net.URI;
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;
19 import java.util.Set;
20 import java.util.SortedMap;
21 import java.util.TreeMap;
22
23 import javax.naming.NameNotFoundException;
24 import javax.naming.NamingEnumeration;
25 import javax.naming.directory.Attributes;
26 import javax.naming.ldap.LdapName;
27
28 import org.argeo.util.naming.LdifParser;
29 import org.argeo.util.naming.LdifWriter;
30 import org.osgi.framework.Filter;
31 import org.osgi.service.useradmin.Role;
32 import org.osgi.service.useradmin.User;
33
34 /** A user admin based on a LDIF files. */
35 public class LdifUserAdmin extends AbstractUserDirectory {
36 private SortedMap<LdapName, DirectoryUser> users = new TreeMap<LdapName, DirectoryUser>();
37 private SortedMap<LdapName, DirectoryGroup> groups = new TreeMap<LdapName, DirectoryGroup>();
38
39 public LdifUserAdmin(String uri, String baseDn) {
40 this(fromUri(uri, baseDn), false);
41 }
42
43 public LdifUserAdmin(Dictionary<String, ?> properties) {
44 this(properties, false);
45 }
46
47 protected LdifUserAdmin(Dictionary<String, ?> properties, boolean scoped) {
48 super(null, properties, scoped);
49 }
50
51 public LdifUserAdmin(URI uri, Dictionary<String, ?> properties) {
52 super(uri, properties, false);
53 }
54
55 @Override
56 protected AbstractUserDirectory scope(User user) {
57 Dictionary<String, Object> credentials = user.getCredentials();
58 String username = (String) credentials.get(SHARED_STATE_USERNAME);
59 if (username == null)
60 username = user.getName();
61 Object pwdCred = credentials.get(SHARED_STATE_PASSWORD);
62 byte[] pwd = (byte[]) pwdCred;
63 if (pwd != null) {
64 char[] password = DigestUtils.bytesToChars(pwd);
65 User directoryUser = (User) getRole(username);
66 if (!directoryUser.hasCredential(null, password))
67 throw new UserDirectoryException("Invalid credentials");
68 } else {
69 throw new UserDirectoryException("Password is required");
70 }
71 Dictionary<String, Object> properties = cloneProperties();
72 properties.put(UserAdminConf.readOnly.name(), "true");
73 LdifUserAdmin scopedUserAdmin = new LdifUserAdmin(properties, true);
74 scopedUserAdmin.groups = Collections.unmodifiableSortedMap(groups);
75 scopedUserAdmin.users = Collections.unmodifiableSortedMap(users);
76 return scopedUserAdmin;
77 }
78
79 private static Dictionary<String, Object> fromUri(String uri, String baseDn) {
80 Hashtable<String, Object> res = new Hashtable<String, Object>();
81 res.put(UserAdminConf.uri.name(), uri);
82 res.put(UserAdminConf.baseDn.name(), baseDn);
83 return res;
84 }
85
86 public void init() {
87
88 try {
89 URI u = new URI(getUri());
90 if (u.getScheme().equals("file")) {
91 File file = new File(u);
92 if (!file.exists())
93 return;
94 }
95 load(u.toURL().openStream());
96 } catch (Exception e) {
97 throw new UserDirectoryException("Cannot open URL " + getUri(), e);
98 }
99 }
100
101 public void save() {
102 if (getUri() == null)
103 throw new UserDirectoryException("Cannot save LDIF user admin: no URI is set");
104 if (isReadOnly())
105 throw new UserDirectoryException("Cannot save LDIF user admin: " + getUri() + " is read-only");
106 try (FileOutputStream out = new FileOutputStream(new File(new URI(getUri())))) {
107 save(out);
108 } catch (IOException | URISyntaxException e) {
109 throw new UserDirectoryException("Cannot save user admin to " + getUri(), e);
110 }
111 }
112
113 public void save(OutputStream out) throws IOException {
114 try {
115 LdifWriter ldifWriter = new LdifWriter(out);
116 for (LdapName name : groups.keySet())
117 ldifWriter.writeEntry(name, groups.get(name).getAttributes());
118 for (LdapName name : users.keySet())
119 ldifWriter.writeEntry(name, users.get(name).getAttributes());
120 } finally {
121 out.close();
122 }
123 }
124
125 protected void load(InputStream in) {
126 try {
127 users.clear();
128 groups.clear();
129
130 LdifParser ldifParser = new LdifParser();
131 SortedMap<LdapName, Attributes> allEntries = ldifParser.read(in);
132 for (LdapName key : allEntries.keySet()) {
133 Attributes attributes = allEntries.get(key);
134 // check for inconsistency
135 Set<String> lowerCase = new HashSet<String>();
136 NamingEnumeration<String> ids = attributes.getIDs();
137 while (ids.hasMoreElements()) {
138 String id = ids.nextElement().toLowerCase();
139 if (lowerCase.contains(id))
140 throw new UserDirectoryException(key + " has duplicate id " + id);
141 lowerCase.add(id);
142 }
143
144 // analyse object classes
145 NamingEnumeration<?> objectClasses = attributes.get(objectClass.name()).getAll();
146 // System.out.println(key);
147 objectClasses: while (objectClasses.hasMore()) {
148 String objectClass = objectClasses.next().toString();
149 // System.out.println(" " + objectClass);
150 if (objectClass.toLowerCase().equals(inetOrgPerson.name().toLowerCase())) {
151 users.put(key, new LdifUser(this, key, attributes));
152 break objectClasses;
153 } else if (objectClass.toLowerCase().equals(getGroupObjectClass().toLowerCase())) {
154 groups.put(key, new LdifGroup(this, key, attributes));
155 break objectClasses;
156 }
157 }
158 }
159 } catch (Exception e) {
160 throw new UserDirectoryException("Cannot load user admin service from LDIF", e);
161 }
162 }
163
164 public void destroy() {
165 if (users == null || groups == null)
166 throw new UserDirectoryException("User directory " + getBaseDn() + " is already destroyed");
167 users = null;
168 groups = null;
169 }
170
171 @Override
172 protected DirectoryUser daoGetRole(LdapName key) throws NameNotFoundException {
173 if (groups.containsKey(key))
174 return groups.get(key);
175 if (users.containsKey(key))
176 return users.get(key);
177 throw new NameNotFoundException(key + " not persisted");
178 }
179
180 @Override
181 protected Boolean daoHasRole(LdapName dn) {
182 return users.containsKey(dn) || groups.containsKey(dn);
183 }
184
185 protected List<DirectoryUser> doGetRoles(Filter f) {
186 ArrayList<DirectoryUser> res = new ArrayList<DirectoryUser>();
187 if (f == null) {
188 res.addAll(users.values());
189 res.addAll(groups.values());
190 } else {
191 for (DirectoryUser user : users.values()) {
192 if (f.match(user.getProperties()))
193 res.add(user);
194 }
195 for (DirectoryUser group : groups.values())
196 if (f.match(group.getProperties()))
197 res.add(group);
198 }
199 return res;
200 }
201
202 @Override
203 protected List<LdapName> getDirectGroups(LdapName dn) {
204 List<LdapName> directGroups = new ArrayList<LdapName>();
205 for (LdapName name : groups.keySet()) {
206 DirectoryGroup group = groups.get(name);
207 if (group.getMemberNames().contains(dn))
208 directGroups.add(group.getDn());
209 }
210 return directGroups;
211 }
212
213 @Override
214 protected void prepare(UserDirectoryWorkingCopy wc) {
215 // delete
216 for (LdapName dn : wc.getDeletedUsers().keySet()) {
217 if (users.containsKey(dn))
218 users.remove(dn);
219 else if (groups.containsKey(dn))
220 groups.remove(dn);
221 else
222 throw new UserDirectoryException("User to delete not found " + dn);
223 }
224 // add
225 for (LdapName dn : wc.getNewUsers().keySet()) {
226 DirectoryUser user = wc.getNewUsers().get(dn);
227 if (users.containsKey(dn) || groups.containsKey(dn))
228 throw new UserDirectoryException("User to create found " + dn);
229 else if (Role.USER == user.getType())
230 users.put(dn, user);
231 else if (Role.GROUP == user.getType())
232 groups.put(dn, (DirectoryGroup) user);
233 else
234 throw new UserDirectoryException("Unsupported role type " + user.getType() + " for new user " + dn);
235 }
236 // modify
237 for (LdapName dn : wc.getModifiedUsers().keySet()) {
238 Attributes modifiedAttrs = wc.getModifiedUsers().get(dn);
239 DirectoryUser user;
240 if (users.containsKey(dn))
241 user = users.get(dn);
242 else if (groups.containsKey(dn))
243 user = groups.get(dn);
244 else
245 throw new UserDirectoryException("User to modify no found " + dn);
246 user.publishAttributes(modifiedAttrs);
247 }
248 }
249
250 @Override
251 protected void commit(UserDirectoryWorkingCopy wc) {
252 save();
253 }
254
255 @Override
256 protected void rollback(UserDirectoryWorkingCopy wc) {
257 init();
258 }
259
260 }