]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifUserAdmin.java
Proper LDIF backend for deploy configs.
[lgpl/argeo-commons.git] / org.argeo.security.core / src / org / argeo / osgi / useradmin / 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(
75 "Cannot save LDIF user admin: no URI is set");
76 if (isReadOnly())
77 throw new UserDirectoryException("Cannot save LDIF user admin: "
78 + getUri() + " is read-only");
79 try (FileOutputStream out = new FileOutputStream(new File(getUri()))) {
80 save(out);
81 } catch (IOException e) {
82 throw new UserDirectoryException("Cannot save user admin to "
83 + getUri(), e);
84 }
85 }
86
87 public void save(OutputStream out) throws IOException {
88 try {
89 LdifWriter ldifWriter = new LdifWriter(out);
90 for (LdapName name : groups.keySet())
91 ldifWriter.writeEntry(name, groups.get(name).getAttributes());
92 for (LdapName name : users.keySet())
93 ldifWriter.writeEntry(name, users.get(name).getAttributes());
94 } finally {
95 IOUtils.closeQuietly(out);
96 }
97 }
98
99 protected void load(InputStream in) {
100 try {
101 users.clear();
102 groups.clear();
103
104 LdifParser ldifParser = new LdifParser();
105 SortedMap<LdapName, Attributes> allEntries = ldifParser.read(in);
106 for (LdapName key : allEntries.keySet()) {
107 Attributes attributes = allEntries.get(key);
108 // check for inconsistency
109 Set<String> lowerCase = new HashSet<String>();
110 NamingEnumeration<String> ids = attributes.getIDs();
111 while (ids.hasMoreElements()) {
112 String id = ids.nextElement().toLowerCase();
113 if (lowerCase.contains(id))
114 throw new UserDirectoryException(key
115 + " has duplicate id " + id);
116 lowerCase.add(id);
117 }
118
119 // analyse object classes
120 NamingEnumeration<?> objectClasses = attributes.get(
121 objectClass.name()).getAll();
122 // System.out.println(key);
123 objectClasses: while (objectClasses.hasMore()) {
124 String objectClass = objectClasses.next().toString();
125 // System.out.println(" " + objectClass);
126 if (objectClass.equals(inetOrgPerson.name())) {
127 users.put(key, new LdifUser(this, key, attributes));
128 break objectClasses;
129 } else if (objectClass.equals(getGroupObjectClass())) {
130 groups.put(key, new LdifGroup(this, key, attributes));
131 break objectClasses;
132 }
133 }
134 }
135 } catch (Exception e) {
136 throw new UserDirectoryException(
137 "Cannot load user admin service from LDIF", e);
138 }
139 }
140
141 public void destroy() {
142 users.clear();
143 users = null;
144 groups.clear();
145 groups = null;
146 }
147
148 protected DirectoryUser daoGetRole(LdapName key) {
149 if (groups.containsKey(key))
150 return groups.get(key);
151 if (users.containsKey(key))
152 return users.get(key);
153 return null;
154 }
155
156 protected Boolean daoHasRole(LdapName dn) {
157 return users.containsKey(dn) || groups.containsKey(dn);
158 }
159
160 @SuppressWarnings("unchecked")
161 protected List<DirectoryUser> doGetRoles(Filter f) {
162 ArrayList<DirectoryUser> res = new ArrayList<DirectoryUser>();
163 if (f == null) {
164 res.addAll(users.values());
165 res.addAll(groups.values());
166 } else {
167 for (DirectoryUser user : users.values()) {
168 // System.out.println("\n" + user.getName());
169 // Dictionary<String, Object> props = user.getProperties();
170 // for (Enumeration<String> keys = props.keys(); keys
171 // .hasMoreElements();) {
172 // String key = keys.nextElement();
173 // System.out.println(" " + key + "=" + props.get(key));
174 // }
175 if (f.match(user.getProperties()))
176 res.add(user);
177 }
178 for (DirectoryUser group : groups.values())
179 if (f.match(group.getProperties()))
180 res.add(group);
181 }
182 return res;
183 }
184
185 @Override
186 protected List<LdapName> getDirectGroups(LdapName dn) {
187 List<LdapName> directGroups = new ArrayList<LdapName>();
188 for (LdapName name : groups.keySet()) {
189 DirectoryGroup group = groups.get(name);
190 if (group.getMemberNames().contains(dn))
191 directGroups.add(group.getDn());
192 }
193 return directGroups;
194 }
195
196 @Override
197 protected void prepare(UserDirectoryWorkingCopy wc) {
198 // delete
199 for (LdapName dn : wc.getDeletedUsers().keySet()) {
200 if (users.containsKey(dn))
201 users.remove(dn);
202 else if (groups.containsKey(dn))
203 groups.remove(dn);
204 else
205 throw new UserDirectoryException("User to delete not found "
206 + dn);
207 }
208 // add
209 for (LdapName dn : wc.getNewUsers().keySet()) {
210 DirectoryUser user = wc.getNewUsers().get(dn);
211 if (users.containsKey(dn) || groups.containsKey(dn))
212 throw new UserDirectoryException("User to create found " + dn);
213 else if (Role.USER == user.getType())
214 users.put(dn, user);
215 else if (Role.GROUP == user.getType())
216 groups.put(dn, (DirectoryGroup) user);
217 else
218 throw new UserDirectoryException("Unsupported role type "
219 + user.getType() + " for new user " + dn);
220 }
221 // modify
222 for (LdapName dn : wc.getModifiedUsers().keySet()) {
223 Attributes modifiedAttrs = wc.getModifiedUsers().get(dn);
224 DirectoryUser user;
225 if (users.containsKey(dn))
226 user = users.get(dn);
227 else if (groups.containsKey(dn))
228 user = groups.get(dn);
229 else
230 throw new UserDirectoryException("User to modify no found "
231 + dn);
232 user.publishAttributes(modifiedAttrs);
233 }
234 }
235
236 @Override
237 protected void commit(UserDirectoryWorkingCopy wc) {
238 save();
239 }
240
241 @Override
242 protected void rollback(UserDirectoryWorkingCopy wc) {
243 init();
244 }
245
246 }