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