]> git.argeo.org Git - lgpl/argeo-commons.git/blobdiff - org.argeo.security.core/src/org/argeo/osgi/useradmin/LdapUserAdmin.java
Improve and simplify OSGi Boot
[lgpl/argeo-commons.git] / org.argeo.security.core / src / org / argeo / osgi / useradmin / LdapUserAdmin.java
index dcb639d9643f51718a56ceb0c5172c304cfec2f2..7a617dfd6115ee3012717ad60276f040b5bb87ed 100644 (file)
@@ -1,64 +1,68 @@
 package org.argeo.osgi.useradmin;
 
-import java.net.URI;
+import static org.argeo.osgi.useradmin.LdifName.objectClass;
+
 import java.util.ArrayList;
+import java.util.Dictionary;
 import java.util.Hashtable;
 import java.util.List;
 
 import javax.naming.Binding;
 import javax.naming.Context;
 import javax.naming.InvalidNameException;
+import javax.naming.NameNotFoundException;
 import javax.naming.NamingEnumeration;
 import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
 import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
 import javax.naming.directory.SearchControls;
 import javax.naming.directory.SearchResult;
 import javax.naming.ldap.InitialLdapContext;
-import javax.naming.ldap.LdapContext;
 import javax.naming.ldap.LdapName;
+import javax.transaction.TransactionManager;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
-import org.argeo.ArgeoException;
-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.framework.Filter;
 
-public class LdapUserAdmin extends AbstractLdapUserAdmin {
+/**
+ * A user admin based on a LDAP server. Requires a {@link TransactionManager}
+ * and an open transaction for write access.
+ */
+public class LdapUserAdmin extends AbstractUserDirectory {
        private final static Log log = LogFactory.getLog(LdapUserAdmin.class);
 
-       private String baseDn = "dc=example,dc=com";
        private InitialLdapContext initialLdapContext = null;
 
-       public LdapUserAdmin(String uri) {
+       public LdapUserAdmin(Dictionary<String, ?> properties) {
+               super(properties);
                try {
-                       setUri(new URI(uri));
                        Hashtable<String, Object> connEnv = new Hashtable<String, Object>();
-                       connEnv.put(Context.INITIAL_CONTEXT_FACTORY,
-                                       "com.sun.jndi.ldap.LdapCtxFactory");
+                       connEnv.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
                        connEnv.put(Context.PROVIDER_URL, getUri().toString());
-                       connEnv.put("java.naming.ldap.attributes.binary", "userPassword");
-                       // connEnv.put(Context.SECURITY_AUTHENTICATION, "simple");
-                       // connEnv.put(Context.SECURITY_PRINCIPAL, "uid=admin,ou=system");
-                       // connEnv.put(Context.SECURITY_CREDENTIALS, "secret");
+                       connEnv.put("java.naming.ldap.attributes.binary", LdifName.userPassword.name());
 
                        initialLdapContext = new InitialLdapContext(connEnv, null);
                        // StartTlsResponse tls = (StartTlsResponse) ctx
                        // .extendedOperation(new StartTlsRequest());
                        // tls.negotiate();
-                       initialLdapContext.addToEnvironment(
-                                       Context.SECURITY_AUTHENTICATION, "simple");
-                       initialLdapContext.addToEnvironment(Context.SECURITY_PRINCIPAL,
-                                       "uid=admin,ou=system");
-                       initialLdapContext.addToEnvironment(Context.SECURITY_CREDENTIALS,
-                                       "secret");
-                       LdapContext ldapContext = (LdapContext) initialLdapContext
-                                       .lookup("uid=root,ou=users,dc=example,dc=com");
-                       log.debug(initialLdapContext.getAttributes(
-                                       "uid=root,ou=users,dc=example,dc=com").get("cn"));
+                       initialLdapContext.addToEnvironment(Context.SECURITY_AUTHENTICATION, "simple");
+                       Object principal = properties.get(Context.SECURITY_PRINCIPAL);
+                       if (principal != null) {
+                               initialLdapContext.addToEnvironment(Context.SECURITY_PRINCIPAL, principal.toString());
+                               Object creds = properties.get(Context.SECURITY_CREDENTIALS);
+                               if (creds != null) {
+                                       initialLdapContext.addToEnvironment(Context.SECURITY_CREDENTIALS, creds.toString());
+
+                               }
+                       }
+                       // initialLdapContext.addToEnvironment(Context.SECURITY_PRINCIPAL,
+                       // "uid=admin,ou=system");
+                       // initialLdapContext.addToEnvironment(Context.SECURITY_CREDENTIALS,
+                       // "secret");
                } catch (Exception e) {
-                       throw new ArgeoUserAdminException("Cannot connect to LDAP", e);
+                       throw new UserDirectoryException("Cannot connect to LDAP", e);
                }
        }
 
@@ -71,151 +75,153 @@ public class LdapUserAdmin extends AbstractLdapUserAdmin {
                }
        }
 
-       @Override
-       public Role createRole(String name, int type) {
-               // TODO Auto-generated method stub
-               return null;
+       protected InitialLdapContext getLdapContext() {
+               return initialLdapContext;
        }
 
        @Override
-       public boolean removeRole(String name) {
-               // TODO Auto-generated method stub
-               return false;
+       protected Boolean daoHasRole(LdapName dn) {
+               return daoGetRole(dn) != null;
        }
 
        @Override
-       public Role getRole(String name) {
+       protected DirectoryUser daoGetRole(LdapName name) {
                try {
-                       Attributes attrs = initialLdapContext.getAttributes(name);
+                       Attributes attrs = getLdapContext().getAttributes(name);
+                       if (attrs.size() == 0)
+                               return null;
                        LdifUser res;
-                       if (attrs.get("objectClass").contains("groupOfNames"))
-                               res = new LdifGroup(new LdapName(name), attrs);
-                       else if (attrs.get("objectClass").contains("inetOrgPerson"))
-                               res = new LdifUser(new LdapName(name), attrs);
+                       if (attrs.get(objectClass.name()).contains(getGroupObjectClass()))
+                               res = new LdifGroup(this, name, attrs);
+                       else if (attrs.get(objectClass.name()).contains(getUserObjectClass()))
+                               res = new LdifUser(this, name, attrs);
                        else
-                               throw new ArgeoUserAdminException("Unsupported LDAP type for "
-                                               + name);
+                               throw new UserDirectoryException("Unsupported LDAP type for " + name);
                        return res;
                } catch (NamingException e) {
-                       throw new ArgeoUserAdminException("Cannot get role for " + name, e);
+                       return null;
                }
        }
 
        @Override
-       public Role[] getRoles(String filter) throws InvalidSyntaxException {
+       protected List<DirectoryUser> doGetRoles(Filter f) {
                try {
-                       String searchFilter = filter;
+                       String searchFilter = f != null ? f.toString()
+                                       : "(|(" + objectClass + "=" + getUserObjectClass() + ")(" + objectClass + "="
+                                                       + getGroupObjectClass() + "))";
                        SearchControls searchControls = new SearchControls();
                        searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
 
-                       String searchBase = baseDn;
-                       NamingEnumeration<SearchResult> results = initialLdapContext
-                                       .search(searchBase, searchFilter, searchControls);
+                       LdapName searchBase = getBaseDn();
+                       NamingEnumeration<SearchResult> results = getLdapContext().search(searchBase, searchFilter, searchControls);
 
-                       ArrayList<Role> res = new ArrayList<Role>();
-                       while (results.hasMoreElements()) {
+                       ArrayList<DirectoryUser> res = new ArrayList<DirectoryUser>();
+                       results: while (results.hasMoreElements()) {
                                SearchResult searchResult = results.next();
                                Attributes attrs = searchResult.getAttributes();
-                               String name = searchResult.getName();
+                               Attribute objectClassAttr = attrs.get(objectClass.name());
+                               LdapName dn = toDn(searchBase, searchResult);
                                LdifUser role;
-                               if (attrs.get("objectClass").contains("groupOfNames"))
-                                       role = new LdifGroup(new LdapName(name), attrs);
-                               else if (attrs.get("objectClass").contains("inetOrgPerson"))
-                                       role = new LdifUser(new LdapName(name), attrs);
-                               else
-                                       throw new ArgeoUserAdminException(
-                                                       "Unsupported LDAP type for " + name);
+                               if (objectClassAttr.contains(getGroupObjectClass()))
+                                       role = new LdifGroup(this, dn, attrs);
+                               else if (objectClassAttr.contains(getUserObjectClass()))
+                                       role = new LdifUser(this, dn, attrs);
+                               else {
+                                       log.warn("Unsupported LDAP type for " + searchResult.getName());
+                                       continue results;
+                               }
                                res.add(role);
                        }
-                       return res.toArray(new Role[res.size()]);
+                       return res;
                } catch (Exception e) {
-                       throw new ArgeoUserAdminException("Cannot get roles for filter "
-                                       + filter, e);
+                       throw new UserDirectoryException("Cannot get roles for filter " + f, e);
                }
        }
 
-       @Override
-       public User getUser(String key, String value) {
-               if (key == null) {
-                       List<User> users = new ArrayList<User>();
-                       for (String prop : getIndexedUserProperties()) {
-                               User user = getUser(prop, value);
-                               if (user != null)
-                                       users.add(user);
-                       }
-                       if (users.size() == 1)
-                               return users.get(0);
-                       else
-                               return null;
-               }
+       private LdapName toDn(LdapName baseDn, Binding binding) throws InvalidNameException {
+               return new LdapName(binding.isRelative() ? binding.getName() + "," + baseDn : binding.getName());
+       }
 
+       @Override
+       protected List<LdapName> getDirectGroups(LdapName dn) {
+               List<LdapName> directGroups = new ArrayList<LdapName>();
                try {
-                       String searchFilter = "(&(objectClass=inetOrgPerson)(" + key + "="
-                                       + value + "))";
+                       String searchFilter = "(&(" + objectClass + "=" + getGroupObjectClass() + ")(" + getMemberAttributeId()
+                                       + "=" + dn + "))";
 
                        SearchControls searchControls = new SearchControls();
                        searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
 
-                       String searchBase = baseDn;
-                       NamingEnumeration<SearchResult> results = initialLdapContext
-                                       .search(searchBase, searchFilter, searchControls);
+                       LdapName searchBase = getBaseDn();
+                       NamingEnumeration<SearchResult> results = getLdapContext().search(searchBase, searchFilter, searchControls);
 
-                       SearchResult searchResult = null;
-                       if (results.hasMoreElements()) {
-                               searchResult = (SearchResult) results.nextElement();
-                               if (results.hasMoreElements())
-                                       searchResult = null;
+                       while (results.hasMoreElements()) {
+                               SearchResult searchResult = (SearchResult) results.nextElement();
+                               directGroups.add(toDn(searchBase, searchResult));
                        }
-                       if (searchResult == null)
-                               return null;
-                       return new LdifUser(toDn(searchBase, searchResult),
-                                       searchResult.getAttributes());
+                       return directGroups;
                } catch (Exception e) {
-                       throw new ArgeoUserAdminException("Cannot get user with " + key
-                                       + "=" + value, e);
+                       throw new UserDirectoryException("Cannot populate direct members of " + dn, e);
                }
        }
 
        @Override
-       public Authorization getAuthorization(User user) {
-               LdifUser u = (LdifUser) user;
-               populateDirectMemberOf(u);
-               return new LdifAuthorization(u);
+       protected void prepare(UserDirectoryWorkingCopy wc) {
+               try {
+                       getLdapContext().reconnect(getLdapContext().getConnectControls());
+                       // delete
+                       for (LdapName dn : wc.getDeletedUsers().keySet()) {
+                               if (!entryExists(dn))
+                                       throw new UserDirectoryException("User to delete no found " + dn);
+                       }
+                       // add
+                       for (LdapName dn : wc.getNewUsers().keySet()) {
+                               if (entryExists(dn))
+                                       throw new UserDirectoryException("User to create found " + dn);
+                       }
+                       // modify
+                       for (LdapName dn : wc.getModifiedUsers().keySet()) {
+                               if (!wc.getNewUsers().containsKey(dn) && !entryExists(dn))
+                                       throw new UserDirectoryException("User to modify not found " + dn);
+                       }
+               } catch (NamingException e) {
+                       throw new UserDirectoryException("Cannot prepare LDAP", e);
+               }
        }
 
-       private LdapName toDn(String baseDn, Binding binding)
-                       throws InvalidNameException {
-               return new LdapName(binding.isRelative() ? binding.getName() + ","
-                               + baseDn : binding.getName());
+       private boolean entryExists(LdapName dn) throws NamingException {
+               try {
+                       return getLdapContext().getAttributes(dn).size() != 0;
+               } catch (NameNotFoundException e) {
+                       return false;
+               }
        }
 
-       void populateDirectMemberOf(LdifUser user) {
-
+       @Override
+       protected void commit(UserDirectoryWorkingCopy wc) {
                try {
-                       String searchFilter = "(&(objectClass=groupOfNames)(member="
-                                       + user.getName() + "))";
-
-                       SearchControls searchControls = new SearchControls();
-                       searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
-
-                       String searchBase = "ou=node";
-                       NamingEnumeration<SearchResult> results = initialLdapContext
-                                       .search(searchBase, searchFilter, searchControls);
-
-                       // TODO synchro
-                       user.directMemberOf.clear();
-                       while (results.hasMoreElements()) {
-                               SearchResult searchResult = (SearchResult) results
-                                               .nextElement();
-                               LdifGroup group = new LdifGroup(toDn(searchBase, searchResult),
-                                               searchResult.getAttributes());
-                               populateDirectMemberOf(group);
-                               user.directMemberOf.add(group);
+                       // delete
+                       for (LdapName dn : wc.getDeletedUsers().keySet()) {
+                               getLdapContext().destroySubcontext(dn);
                        }
-               } catch (Exception e) {
-                       throw new ArgeoException("Cannot populate direct members of "
-                                       + user, e);
+                       // add
+                       for (LdapName dn : wc.getNewUsers().keySet()) {
+                               DirectoryUser user = wc.getNewUsers().get(dn);
+                               getLdapContext().createSubcontext(dn, user.getAttributes());
+                       }
+                       // modify
+                       for (LdapName dn : wc.getModifiedUsers().keySet()) {
+                               Attributes modifiedAttrs = wc.getModifiedUsers().get(dn);
+                               getLdapContext().modifyAttributes(dn, DirContext.REPLACE_ATTRIBUTE, modifiedAttrs);
+                       }
+               } catch (NamingException e) {
+                       throw new UserDirectoryException("Cannot commit LDAP", e);
                }
        }
 
+       @Override
+       protected void rollback(UserDirectoryWorkingCopy wc) {
+               // prepare not impacting
+       }
+
 }