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