package org.argeo.osgi.useradmin;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import javax.naming.InvalidNameException;
import javax.naming.NamingEnumeration;
import javax.naming.directory.Attributes;
+import javax.naming.directory.BasicAttributes;
import javax.naming.ldap.LdapName;
+import javax.naming.ldap.Rdn;
+import org.apache.commons.io.IOUtils;
+import org.osgi.framework.Filter;
+import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.service.useradmin.Authorization;
import org.osgi.service.useradmin.Role;
import org.osgi.service.useradmin.User;
-import org.osgi.service.useradmin.UserAdmin;
-public class LdifUserAdmin implements UserAdmin {
- private SortedMap<LdapName, Role> roles = new TreeMap<LdapName, Role>();
+/** User admin implementation using LDIF file(s) as backend. */
+public class LdifUserAdmin extends AbstractLdapUserAdmin {
+ SortedMap<LdapName, LdifUser> users = new TreeMap<LdapName, LdifUser>();
+ SortedMap<LdapName, LdifGroup> groups = new TreeMap<LdapName, LdifGroup>();
+
+ private Map<String, Map<String, LdifUser>> userIndexes = new LinkedHashMap<String, Map<String, LdifUser>>();
+
+ public LdifUserAdmin(String uri) {
+ this(uri, true);
+ }
+
+ public LdifUserAdmin(String uri, boolean isReadOnly) {
+ setReadOnly(isReadOnly);
+ try {
+ setUri(new URI(uri));
+ } catch (URISyntaxException e) {
+ throw new ArgeoUserAdminException("Invalid URI " + uri, e);
+ }
+
+ if (!isReadOnly && !getUri().getScheme().equals("file:"))
+ throw new UnsupportedOperationException(getUri().getScheme()
+ + "not supported read-write.");
+
+ }
public LdifUserAdmin(InputStream in) {
+ load(in);
+ setReadOnly(true);
+ setUri(null);
+ }
+
+ public void init() {
+ try {
+ load(getUri().toURL().openStream());
+ } catch (Exception e) {
+ throw new ArgeoUserAdminException("Cannot open URL " + getUri(), e);
+ }
+ }
+
+ public void save() {
+ if (getUri() == null || isReadOnly())
+ throw new ArgeoUserAdminException("Cannot save LDIF user admin");
+ try (FileOutputStream out = new FileOutputStream(new File(getUri()))) {
+ save(out);
+ } catch (IOException e) {
+ throw new ArgeoUserAdminException("Cannot save user admin to "
+ + getUri(), e);
+ }
+ }
+
+ public void save(OutputStream out) throws IOException {
+ try {
+ LdifWriter ldifWriter = new LdifWriter(out);
+ for (LdapName name : groups.keySet())
+ ldifWriter.writeEntry(name, groups.get(name).getAttributes());
+ for (LdapName name : users.keySet())
+ ldifWriter.writeEntry(name, users.get(name).getAttributes());
+ } finally {
+ IOUtils.closeQuietly(out);
+ }
+ }
+
+ protected void load(InputStream in) {
try {
LdifParser ldifParser = new LdifParser();
SortedMap<LdapName, Attributes> allEntries = ldifParser.read(in);
for (LdapName key : allEntries.keySet()) {
Attributes attributes = allEntries.get(key);
- NamingEnumeration objectClasses = attributes.get("objectClass")
- .getAll();
+ NamingEnumeration<?> objectClasses = attributes.get(
+ "objectClass").getAll();
objectClasses: while (objectClasses.hasMore()) {
String objectClass = objectClasses.next().toString();
if (objectClass.equals("inetOrgPerson")) {
- roles.put(key, new LdifUser(key, attributes));
+ users.put(key, new LdifUser(key, attributes));
break objectClasses;
} else if (objectClass.equals("groupOfNames")) {
- roles.put(key, new LdifGroup(key, attributes));
+ groups.put(key, new LdifGroup(key, attributes));
break objectClasses;
}
}
}
+
+ // optimise
+ for (LdifGroup group : groups.values())
+ loadMembers(group);
+
+ // indexes
+ for (String attr : getIndexedUserProperties())
+ userIndexes.put(attr, new TreeMap<String, LdifUser>());
+
+ for (LdifUser user : users.values()) {
+ Dictionary<String, Object> properties = user.getProperties();
+ for (String attr : getIndexedUserProperties()) {
+ Object value = properties.get(attr);
+ if (value != null) {
+ LdifUser otherUser = userIndexes.get(attr).put(
+ value.toString(), user);
+ if (otherUser != null)
+ throw new ArgeoUserAdminException("User " + user
+ + " and user " + otherUser
+ + " both have property " + attr
+ + " set to " + value);
+ }
+ }
+ }
} catch (Exception e) {
throw new ArgeoUserAdminException(
- "Cannot initialise user admin service from LDIF", e);
+ "Cannot load user admin service from LDIF", e);
}
}
+ public void destroy() {
+ users.clear();
+ users = null;
+ groups.clear();
+ groups = null;
+ }
+
@Override
public Role getRole(String name) {
LdapName key;
+ name, e);
}
- if (!roles.containsKey(key))
- return null;
- return roles.get(key);
+ if (groups.containsKey(key))
+ return groups.get(key);
+ if (users.containsKey(key))
+ return users.get(key);
+ return null;
}
@Override
public Authorization getAuthorization(User user) {
- // TODO Auto-generated method stub
- return null;
+ return new LdifAuthorization((LdifUser) user);
}
@Override
public Role createRole(String name, int type) {
- throw new UnsupportedOperationException();
+ try {
+ LdapName dn = new LdapName(name);
+ if (users.containsKey(dn) || groups.containsKey(dn))
+ throw new ArgeoUserAdminException("Already a role " + name);
+
+ BasicAttributes attrs = new BasicAttributes();
+ attrs.put("dn", dn.toString());
+ Rdn nameRdn = dn.getRdn(dn.size() - 1);
+ // TODO deal with multiple attr RDN
+ attrs.put(nameRdn.getType(), nameRdn.getValue());
+ LdifUser newRole;
+ if (type == Role.USER) {
+ newRole = new LdifUser(dn, attrs);
+ users.put(dn, newRole);
+ } else if (type == Role.GROUP) {
+ newRole = new LdifGroup(dn, attrs);
+ groups.put(dn, (LdifGroup) newRole);
+ } else
+ throw new ArgeoUserAdminException("Unsupported type " + type);
+ return newRole;
+ } catch (InvalidNameException e) {
+ throw new ArgeoUserAdminException("Cannot create role " + name, e);
+ }
}
@Override
public boolean removeRole(String name) {
- throw new UnsupportedOperationException();
+ try {
+ LdapName dn = new LdapName(name);
+ LdifUser role = null;
+ if (users.containsKey(dn))
+ role = users.remove(dn);
+ else if (groups.containsKey(dn))
+ role = groups.remove(dn);
+ else
+ throw new ArgeoUserAdminException("There is no role " + name);
+ if (role == null)
+ return false;
+ for (LdifGroup group : role.directMemberOf) {
+ group.directMembers.remove(role);
+ group.getAttributes().get(group.getMemberAttrName())
+ .remove(dn.toString());
+ }
+ if (role instanceof LdifGroup) {
+ LdifGroup group = (LdifGroup) role;
+ for (Role user : group.directMembers) {
+ if (user instanceof LdifUser)
+ ((LdifUser) user).directMemberOf.remove(group);
+ }
+ }
+ return true;
+ } catch (InvalidNameException e) {
+ throw new ArgeoUserAdminException("Cannot create role " + name, e);
+ }
}
@Override
public Role[] getRoles(String filter) throws InvalidSyntaxException {
- throw new UnsupportedOperationException();
+ ArrayList<Role> res = new ArrayList<Role>();
+ if (filter == null) {
+ res.addAll(users.values());
+ res.addAll(groups.values());
+ } else {
+ Filter f = FrameworkUtil.createFilter(filter);
+ for (LdifUser user : users.values())
+ if (f.match(user.getProperties()))
+ res.add(user);
+ for (LdifUser group : groups.values())
+ if (f.match(group.getProperties()))
+ res.add(group);
+ }
+ return res.toArray(new Role[res.size()]);
}
@Override
public User getUser(String key, String value) {
- throw new UnsupportedOperationException();
+ // TODO check value null or empty
+ if (key != null) {
+ if (!userIndexes.containsKey(key))
+ return null;
+ return userIndexes.get(key).get(value);
+ }
+
+ // Try all indexes
+ List<LdifUser> collectedUsers = new ArrayList<LdifUser>(
+ getIndexedUserProperties().size());
+ // try dn
+ LdifUser user = null;
+ try {
+ user = (LdifUser) getRole(value);
+ if (user != null)
+ collectedUsers.add(user);
+ } catch (Exception e) {
+ // silent
+ }
+ for (String attr : userIndexes.keySet()) {
+ user = userIndexes.get(attr).get(value);
+ if (user != null)
+ collectedUsers.add(user);
+ }
+
+ if (collectedUsers.size() == 1)
+ return collectedUsers.get(0);
+ return null;
+ // throw new UnsupportedOperationException();
+ }
+
+ protected void loadMembers(LdifGroup group) {
+ group.directMembers = new ArrayList<Role>();
+ for (LdapName ldapName : group.getMemberNames()) {
+ LdifUser role = null;
+ if (groups.containsKey(ldapName))
+ role = groups.get(ldapName);
+ else if (users.containsKey(ldapName))
+ role = users.get(ldapName);
+ else {
+ if (getExternalRoles() != null)
+ role = (LdifUser) getExternalRoles().getRole(
+ ldapName.toString());
+ if (role == null)
+ throw new ArgeoUserAdminException("No role found for "
+ + ldapName);
+ }
+ role.directMemberOf.add(group);
+ group.directMembers.add(role);
+ }
}
}