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