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