]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifUserAdmin.java
- Improve transaction support
[lgpl/argeo-commons.git] / org.argeo.security.core / src / org / argeo / osgi / useradmin / LdifUserAdmin.java
1 package org.argeo.osgi.useradmin;
2
3 import java.io.File;
4 import java.io.FileOutputStream;
5 import java.io.IOException;
6 import java.io.InputStream;
7 import java.io.OutputStream;
8 import java.net.URI;
9 import java.net.URISyntaxException;
10 import java.util.ArrayList;
11 import java.util.Dictionary;
12 import java.util.LinkedHashMap;
13 import java.util.List;
14 import java.util.Map;
15 import java.util.SortedMap;
16 import java.util.TreeMap;
17
18 import javax.naming.InvalidNameException;
19 import javax.naming.NamingEnumeration;
20 import javax.naming.directory.Attributes;
21 import javax.naming.ldap.LdapName;
22
23 import org.apache.commons.io.IOUtils;
24 import org.osgi.framework.Filter;
25 import org.osgi.service.useradmin.Role;
26 import org.osgi.service.useradmin.User;
27
28 /** User admin implementation using LDIF file(s) as backend. */
29 public class LdifUserAdmin extends AbstractUserDirectory {
30 SortedMap<LdapName, DirectoryUser> users = new TreeMap<LdapName, DirectoryUser>();
31 SortedMap<LdapName, DirectoryGroup> groups = new TreeMap<LdapName, DirectoryGroup>();
32
33 private Map<String, Map<String, DirectoryUser>> userIndexes = new LinkedHashMap<String, Map<String, DirectoryUser>>();
34
35 // private Map<LdapName, List<LdifGroup>> directMemberOf = new
36 // TreeMap<LdapName, List<LdifGroup>>();
37
38 public LdifUserAdmin(String uri) {
39 this(uri, readOnlyDefault(uri));
40 }
41
42 public LdifUserAdmin(String uri, boolean isReadOnly) {
43 setReadOnly(isReadOnly);
44 try {
45 setUri(new URI(uri));
46 } catch (URISyntaxException e) {
47 throw new UserDirectoryException("Invalid URI " + uri, e);
48 }
49
50 if (!isReadOnly && !getUri().getScheme().equals("file"))
51 throw new UnsupportedOperationException(getUri().getScheme()
52 + " not supported read-write.");
53
54 }
55
56 public LdifUserAdmin(URI uri, boolean isReadOnly) {
57 setReadOnly(isReadOnly);
58 setUri(uri);
59 if (!isReadOnly && !getUri().getScheme().equals("file"))
60 throw new UnsupportedOperationException(getUri().getScheme()
61 + " not supported read-write.");
62
63 }
64
65 public LdifUserAdmin(InputStream in) {
66 load(in);
67 setReadOnly(true);
68 setUri(null);
69 }
70
71 private static boolean readOnlyDefault(String uriStr) {
72 URI uri;
73 try {
74 uri = new URI(uriStr);
75 } catch (Exception e) {
76 throw new UserDirectoryException("Invalid URI " + uriStr, e);
77 }
78 if (uri.getScheme().equals("file")) {
79 File file = new File(uri);
80 return !file.canWrite();
81 }
82 return true;
83 }
84
85 public void init() {
86 try {
87 load(getUri().toURL().openStream());
88 } catch (Exception e) {
89 throw new UserDirectoryException("Cannot open URL " + getUri(), e);
90 }
91 }
92
93 public void save() {
94 if (getUri() == null || isReadOnly())
95 throw new UserDirectoryException("Cannot save LDIF user admin");
96 try (FileOutputStream out = new FileOutputStream(new File(getUri()))) {
97 save(out);
98 } catch (IOException e) {
99 throw new UserDirectoryException("Cannot save user admin to "
100 + getUri(), e);
101 }
102 }
103
104 public void save(OutputStream out) throws IOException {
105 try {
106 LdifWriter ldifWriter = new LdifWriter(out);
107 for (LdapName name : groups.keySet())
108 ldifWriter.writeEntry(name, groups.get(name).getAttributes());
109 for (LdapName name : users.keySet())
110 ldifWriter.writeEntry(name, users.get(name).getAttributes());
111 } finally {
112 IOUtils.closeQuietly(out);
113 }
114 }
115
116 protected void load(InputStream in) {
117 try {
118 users.clear();
119 groups.clear();
120
121 LdifParser ldifParser = new LdifParser();
122 SortedMap<LdapName, Attributes> allEntries = ldifParser.read(in);
123 for (LdapName key : allEntries.keySet()) {
124 Attributes attributes = allEntries.get(key);
125 NamingEnumeration<?> objectClasses = attributes.get(
126 "objectClass").getAll();
127 objectClasses: while (objectClasses.hasMore()) {
128 String objectClass = objectClasses.next().toString();
129 if (objectClass.equals("inetOrgPerson")) {
130 users.put(key, new LdifUser(this, key, attributes));
131 break objectClasses;
132 } else if (objectClass.equals("groupOfNames")) {
133 groups.put(key, new LdifGroup(this, key, attributes));
134 break objectClasses;
135 }
136 }
137 }
138
139 // optimise
140 // for (LdifGroup group : groups.values())
141 // loadMembers(group);
142
143 // indexes
144 for (String attr : getIndexedUserProperties())
145 userIndexes.put(attr, new TreeMap<String, DirectoryUser>());
146
147 for (DirectoryUser user : users.values()) {
148 Dictionary<String, Object> properties = user.getProperties();
149 for (String attr : getIndexedUserProperties()) {
150 Object value = properties.get(attr);
151 if (value != null) {
152 DirectoryUser otherUser = userIndexes.get(attr).put(
153 value.toString(), user);
154 if (otherUser != null)
155 throw new UserDirectoryException("User " + user
156 + " and user " + otherUser
157 + " both have property " + attr
158 + " set to " + value);
159 }
160 }
161 }
162 } catch (Exception e) {
163 throw new UserDirectoryException(
164 "Cannot load user admin service from LDIF", e);
165 }
166 }
167
168 public void destroy() {
169 users.clear();
170 users = null;
171 groups.clear();
172 groups = null;
173 }
174
175 protected DirectoryUser daoGetRole(LdapName key) {
176 if (groups.containsKey(key))
177 return groups.get(key);
178 if (users.containsKey(key))
179 return users.get(key);
180 return null;
181 }
182
183 protected Boolean daoHasRole(LdapName dn) {
184 return users.containsKey(dn) || groups.containsKey(dn);
185 }
186
187 // @Override
188 // public boolean removeRole(String name) {
189 // LdapName dn = toDn(name);
190 // LdifUser role = null;
191 // if (users.containsKey(dn))
192 // role = users.remove(dn);
193 // else if (groups.containsKey(dn))
194 // role = groups.remove(dn);
195 // else
196 // throw new UserDirectoryException("There is no role " + name);
197 // if (role == null)
198 // return false;
199 // for (LdifGroup group : getDirectGroups(role)) {
200 // group.getAttributes().get(getMemberAttributeId())
201 // .remove(dn.toString());
202 // }
203 // return true;
204 // }
205
206 protected List<DirectoryUser> doGetRoles(Filter f) {
207 ArrayList<DirectoryUser> res = new ArrayList<DirectoryUser>();
208 if (f == null) {
209 res.addAll(users.values());
210 res.addAll(groups.values());
211 } else {
212 // Filter f = FrameworkUtil.createFilter(filter);
213 for (DirectoryUser user : users.values())
214 if (f.match(user.getProperties()))
215 res.add(user);
216 for (DirectoryUser group : groups.values())
217 if (f.match(group.getProperties()))
218 res.add(group);
219 }
220 return res;
221 }
222
223 protected void doGetUser(String key, String value,
224 List<DirectoryUser> collectedUsers) {
225 assert key != null;
226 DirectoryUser user = userIndexes.get(key).get(value);
227 if (user != null)
228 collectedUsers.add(user);
229 }
230
231 @Override
232 protected List<DirectoryGroup> getDirectGroups(User user) {
233 LdapName dn;
234 if (user instanceof LdifUser)
235 dn = ((LdifUser) user).getDn();
236 else
237 try {
238 dn = new LdapName(user.getName());
239 } catch (InvalidNameException e) {
240 throw new UserDirectoryException("Badly formatted user name "
241 + user.getName(), e);
242 }
243
244 List<DirectoryGroup> directGroups = new ArrayList<DirectoryGroup>();
245 for (LdapName name : groups.keySet()) {
246 DirectoryGroup group = groups.get(name);
247 if (group.getMemberNames().contains(dn))
248 directGroups.add(group);
249 }
250 return directGroups;
251 }
252
253 @Override
254 protected void prepare(WorkingCopy wc) {
255 // delete
256 for (LdapName dn : wc.getDeletedUsers().keySet()) {
257 if (users.containsKey(dn))
258 users.remove(dn);
259 else if (groups.containsKey(dn))
260 groups.remove(dn);
261 else
262 throw new UserDirectoryException("User to delete no found "
263 + dn);
264 }
265 // add
266 for (LdapName dn : wc.getNewUsers().keySet()) {
267 DirectoryUser user = wc.getNewUsers().get(dn);
268 if (Role.USER == user.getType())
269 users.put(dn, user);
270 else if (Role.GROUP == user.getType())
271 groups.put(dn, (DirectoryGroup) user);
272 else
273 throw new UserDirectoryException("Unsupported role type "
274 + user.getType() + " for new user " + dn);
275 }
276 // modify
277 for (LdapName dn : wc.getModifiedUsers().keySet()) {
278 Attributes modifiedAttrs = wc.getModifiedUsers().get(dn);
279 DirectoryUser user;
280 if (users.containsKey(dn))
281 user = users.get(dn);
282 else if (groups.containsKey(dn))
283 user = groups.get(dn);
284 else
285 throw new UserDirectoryException("User to modify no found "
286 + dn);
287 user.publishAttributes(modifiedAttrs);
288 }
289 }
290
291 @Override
292 protected void commit(WorkingCopy wc) {
293 save();
294 }
295
296 @Override
297 protected void rollback(WorkingCopy wc) {
298 init();
299 }
300
301 }