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