Refactor user admin
authorMathieu Baudier <mbaudier@argeo.org>
Fri, 9 Sep 2016 09:15:24 +0000 (09:15 +0000)
committerMathieu Baudier <mbaudier@argeo.org>
Fri, 9 Sep 2016 09:15:24 +0000 (09:15 +0000)
git-svn-id: https://svn.argeo.org/commons/trunk@9106 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc

org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsDeployment.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsState.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeUserAdmin.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/UserDirectoryServiceFactory.java [deleted file]
org.argeo.security.core/ext/test/org/argeo/osgi/useradmin/LdifUserAdminTest.java
org.argeo.security.core/src/org/argeo/osgi/useradmin/AbstractUserDirectory.java
org.argeo.security.core/src/org/argeo/osgi/useradmin/AggregatingAuthorization.java [new file with mode: 0644]
org.argeo.security.core/src/org/argeo/osgi/useradmin/AggregatingUserAdmin.java [new file with mode: 0644]
org.argeo.security.core/src/org/argeo/osgi/useradmin/LdapUserAdmin.java
org.argeo.security.core/src/org/argeo/osgi/useradmin/UserDirectory.java

index a2d072e5bcc62923d945d681ccef1c7b4b9a5c95..7a4ca1bf4eeed4fec009f4c11a2203bcb9c3516f 100644 (file)
@@ -28,6 +28,7 @@ import org.argeo.node.NodeDeployment;
 import org.argeo.node.NodeState;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
 import org.osgi.framework.FrameworkUtil;
 import org.osgi.framework.ServiceReference;
 import org.osgi.framework.wiring.BundleCapability;
@@ -178,6 +179,8 @@ public class CmsDeployment implements NodeDeployment {
                Hashtable<String, Object> properties = new Hashtable<>();
                properties.put(ArgeoJcrConstants.JCR_REPOSITORY_ALIAS, name);
                properties.put(NodeConstants.CN, name);
+               if (name.equals(ArgeoJcrConstants.ALIAS_NODE))
+                       properties.put(Constants.SERVICE_RANKING, Integer.MAX_VALUE);
                bc.registerService(Repository.class, adminSession.getRepository(), properties);
                if (log.isDebugEnabled())
                        log.debug("Published data model " + name);
index 87fae05cf2af4333c97078af43c6b3c48c6a3a6c..63aeeac299a42b257ded84f05a6ec3a90e414feb 100644 (file)
@@ -10,6 +10,7 @@ import java.net.InetAddress;
 import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.Dictionary;
+import java.util.Hashtable;
 import java.util.List;
 import java.util.Locale;
 import java.util.UUID;
@@ -21,6 +22,7 @@ import javax.transaction.UserTransaction;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.argeo.cms.auth.AuthConstants;
 import org.argeo.cms.maintenance.MaintenanceUi;
 import org.argeo.node.NodeConstants;
 import org.argeo.node.NodeState;
@@ -32,7 +34,6 @@ import org.osgi.framework.Constants;
 import org.osgi.framework.FrameworkUtil;
 import org.osgi.framework.ServiceReference;
 import org.osgi.service.cm.ManagedServiceFactory;
-import org.osgi.service.useradmin.UserAdmin;
 
 import bitronix.tm.BitronixTransactionManager;
 import bitronix.tm.BitronixTransactionSynchronizationRegistry;
@@ -88,6 +89,7 @@ public class CmsState implements NodeState {
        }
 
        private void initServices() {
+               // JTA
                initTransactionManager();
 
                // JCR
@@ -100,14 +102,9 @@ public class CmsState implements NodeState {
                bc.registerService(RepositoryFactory.class, repositoryFactory, null);
 
                // Security
-//             UserDirectoryServiceFactory userDirectoryServiceFactory = new UserDirectoryServiceFactory();
-//             shutdownHooks.add(() -> userDirectoryServiceFactory.shutdown());
-//             bc.registerService(ManagedServiceFactory.class, userDirectoryServiceFactory,
-//                             LangUtils.init(Constants.SERVICE_PID, NodeConstants.NODE_USER_DIRECTORIES_FACTORY_PID));
-
-               NodeUserAdmin userAdmin = new NodeUserAdmin();
+               NodeUserAdmin userAdmin = new NodeUserAdmin(AuthConstants.ROLES_BASEDN);
                shutdownHooks.add(() -> userAdmin.destroy());
-               Dictionary<String, Object> props = userAdmin.currentState();
+               Dictionary<String, Object> props = new Hashtable<>();
                props.put(Constants.SERVICE_PID, NodeConstants.NODE_USER_ADMIN_PID);
                bc.registerService(ManagedServiceFactory.class, userAdmin, props);
 
index c04d820da418d1fd0cf6c8fa4fae813f1e1b1548..d0cf93d80c21e117cf36aae65c869c44981e1a31 100644 (file)
@@ -2,92 +2,56 @@ package org.argeo.cms.internal.kernel;
 
 import java.net.URI;
 import java.net.URISyntaxException;
-import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Dictionary;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Hashtable;
-import java.util.List;
 import java.util.Map;
-import java.util.Set;
 
-import javax.naming.InvalidNameException;
 import javax.naming.ldap.LdapName;
 import javax.transaction.TransactionManager;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.argeo.cms.CmsException;
-import org.argeo.cms.auth.AuthConstants;
 import org.argeo.node.NodeConstants;
+import org.argeo.osgi.useradmin.AbstractUserDirectory;
+import org.argeo.osgi.useradmin.AggregatingUserAdmin;
 import org.argeo.osgi.useradmin.LdapUserAdmin;
 import org.argeo.osgi.useradmin.LdifUserAdmin;
 import org.argeo.osgi.useradmin.UserAdminConf;
 import org.argeo.osgi.useradmin.UserDirectory;
-import org.argeo.osgi.useradmin.UserDirectoryException;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.Constants;
 import org.osgi.framework.FrameworkUtil;
-import org.osgi.framework.InvalidSyntaxException;
-import org.osgi.framework.ServiceReference;
 import org.osgi.framework.ServiceRegistration;
 import org.osgi.service.cm.ConfigurationException;
 import org.osgi.service.cm.ManagedServiceFactory;
-import org.osgi.service.useradmin.Authorization;
-import org.osgi.service.useradmin.Role;
-import org.osgi.service.useradmin.User;
 import org.osgi.service.useradmin.UserAdmin;
 import org.osgi.util.tracker.ServiceTracker;
 
+import bitronix.tm.BitronixTransactionManager;
 import bitronix.tm.resource.ehcache.EhCacheXAResourceProducer;
 
 /**
- * Aggregates multiple {@link UserDirectory} and integrates them with this node
- * system roles.
+ * Aggregates multiple {@link UserDirectory} and integrates them with system
+ * roles.
  */
-class NodeUserAdmin implements UserAdmin, ManagedServiceFactory, KernelConstants {
+class NodeUserAdmin extends AggregatingUserAdmin implements ManagedServiceFactory, KernelConstants {
        private final static Log log = LogFactory.getLog(NodeUserAdmin.class);
-       final static LdapName ROLES_BASE;
-       static {
-               try {
-                       ROLES_BASE = new LdapName(AuthConstants.ROLES_BASEDN);
-               } catch (InvalidNameException e) {
-                       throw new UserDirectoryException("Cannot initialize " + NodeUserAdmin.class, e);
-               }
-       }
-
        private final BundleContext bc = FrameworkUtil.getBundle(getClass()).getBundleContext();
 
-       // DAOs
-       private UserAdmin nodeRoles = null;
-       private Map<LdapName, UserAdmin> userAdmins = new HashMap<LdapName, UserAdmin>();
+       // OSGi
        private Map<String, LdapName> pidToBaseDn = new HashMap<>();
-
+       private Map<String, ServiceRegistration<UserDirectory>> pidToServiceRegs = new HashMap<>();
        private ServiceRegistration<UserAdmin> userAdminReg;
 
+       // JTA
        private final ServiceTracker<TransactionManager, TransactionManager> tmTracker;
-
-       // JCR
-       // private String homeBasePath = "/home";
-       // private String peopleBasePath = ArgeoJcrConstants.PEOPLE_BASE_PATH;
-       // private Repository repository;
-       // private Session adminSession;
-
        private final String cacheName = UserDirectory.class.getName();
 
-       public NodeUserAdmin() {
-               // DAOs
-               // File nodeBaseDir = new File(getOsgiInstanceDir(), DIR_NODE);
-               // nodeBaseDir.mkdirs();
-               // String userAdminUri = getFrameworkProp(NodeConstants.USERADMIN_URIS);
-               // initUserAdmins(userAdminUri, nodeBaseDir);
-               // String nodeRolesUri = getFrameworkProp(NodeConstants.ROLES_URI);
-               // initNodeRoles(nodeRolesUri, nodeBaseDir);
-
-               // new ServiceTracker<>(bc, TransactionManager.class, new
-               // TransactionManagerStc()).open();
-               tmTracker = new TransactionManagerStc();
+       public NodeUserAdmin(String systemRolesBaseDn) {
+               super(systemRolesBaseDn);
+               tmTracker = new ServiceTracker<>(bc, TransactionManager.class, null);
                tmTracker.open();
        }
 
@@ -100,492 +64,64 @@ class NodeUserAdmin implements UserAdmin, ManagedServiceFactory, KernelConstants
                } catch (URISyntaxException e) {
                        throw new CmsException("Badly formatted URI " + uri, e);
                }
-               UserDirectory userDirectory = u.getScheme().equals("ldap") ? new LdapUserAdmin(properties)
+
+               // Create
+               AbstractUserDirectory userDirectory = u.getScheme().equals("ldap") ? new LdapUserAdmin(properties)
                                : new LdifUserAdmin(properties);
-               LdapName baseDn;
-               try {
-                       baseDn = new LdapName(userDirectory.getBaseDn());
-               } catch (InvalidNameException e) {
-                       throw new CmsException("Badly formatted base DN " + userDirectory.getBaseDn(), e);
-               }
-               if (isRolesDnBase(baseDn)) {
-                       nodeRoles = (UserAdmin) userDirectory;
-                       userDirectory.setExternalRoles(this);
-               }
-               userDirectory.init();
-               addUserAdmin(baseDn.toString(), (UserAdmin) userDirectory);
+               addUserDirectory(userDirectory);
 
-               // publish user directory
+               // OSGi
+               LdapName baseDn = userDirectory.getBaseDn();
                Dictionary<String, Object> regProps = new Hashtable<>();
                regProps.put(Constants.SERVICE_PID, pid);
+               if(isSystemRolesBaseDn(baseDn))
+                       regProps.put(Constants.SERVICE_RANKING, Integer.MAX_VALUE);
                regProps.put(UserAdminConf.baseDn.name(), baseDn);
-               bc.registerService(UserDirectory.class, userDirectory, regProps);
+               ServiceRegistration<UserDirectory> reg = bc.registerService(UserDirectory.class, userDirectory, regProps);
                pidToBaseDn.put(pid, baseDn);
+               pidToServiceRegs.put(pid, reg);
 
-               if (log.isDebugEnabled()) {
+               if (log.isDebugEnabled())
                        log.debug("User directory " + userDirectory.getBaseDn() + " [" + u.getScheme() + "] enabled.");
-               }
 
-               if (!isRolesDnBase(baseDn)) {
+               if (!isSystemRolesBaseDn(baseDn)) {
                        if (userAdminReg != null)
                                userAdminReg.unregister();
                        // register self as main user admin
-                       userAdminReg = bc.registerService(UserAdmin.class, this, currentState());
+                       Dictionary<String, Object> userAdminregProps = currentState();
+                       userAdminregProps.put(NodeConstants.CN, NodeConstants.DEFAULT);
+                       userAdminregProps.put(Constants.SERVICE_RANKING, Integer.MAX_VALUE);
+                       userAdminReg = bc.registerService(UserAdmin.class, this, userAdminregProps);
                }
        }
 
-       private boolean isRolesDnBase(LdapName baseDn) {
-               return baseDn.equals(ROLES_BASE);
-       }
-
        @Override
        public void deleted(String pid) {
+               assert pidToServiceRegs.get(pid) != null;
+               assert pidToBaseDn.get(pid) != null;
+               pidToServiceRegs.remove(pid).unregister();
                LdapName baseDn = pidToBaseDn.remove(pid);
-               UserAdmin userAdmin = userAdmins.remove(baseDn);
-               ((UserDirectory) userAdmin).destroy();
+               removeUserDirectory(baseDn);
        }
 
        @Override
        public String getName() {
-               return "Node user admin";
-       }
-
-       private class TransactionManagerStc extends ServiceTracker<TransactionManager, TransactionManager> {
-
-               public TransactionManagerStc() {
-                       super(bc, TransactionManager.class, null);
-               }
-
-               @Override
-               public TransactionManager addingService(ServiceReference<TransactionManager> reference) {
-                       TransactionManager transactionManager = bc.getService(reference);
-                       if (nodeRoles != null)
-                               ((UserDirectory) nodeRoles).setTransactionManager(transactionManager);
-                       for (UserAdmin userAdmin : userAdmins.values()) {
-                               if (userAdmin instanceof UserDirectory)
-                                       ((UserDirectory) userAdmin).setTransactionManager(transactionManager);
-                       }
-                       if (log.isDebugEnabled())
-                               log.debug("Set transaction manager");
-                       return transactionManager;
-               }
-
-               @Override
-               public void removedService(ServiceReference<TransactionManager> reference, TransactionManager service) {
-                       ((UserDirectory) nodeRoles).setTransactionManager(null);
-                       for (UserAdmin userAdmin : userAdmins.values()) {
-                               if (userAdmin instanceof UserDirectory)
-                                       ((UserDirectory) userAdmin).setTransactionManager(null);
-                       }
-               }
-
+               return "Node User Admin";
        }
 
-       // @Deprecated
-       // public NodeUserAdmin(TransactionManager transactionManager, Repository
-       // repository) {
-       // // this.repository = repository;
-       // // try {
-       // // this.adminSession = this.repository.login();
-       // // } catch (RepositoryException e) {
-       // // throw new CmsException("Cannot log-in", e);
-       // // }
-       //
-       // // DAOs
-       // File nodeBaseDir = new File(getOsgiInstanceDir(), DIR_NODE);
-       // nodeBaseDir.mkdirs();
-       // String userAdminUri = getFrameworkProp(NodeConstants.USERADMIN_URIS);
-       // initUserAdmins(userAdminUri, nodeBaseDir);
-       // String nodeRolesUri = getFrameworkProp(NodeConstants.ROLES_URI);
-       // initNodeRoles(nodeRolesUri, nodeBaseDir);
-       //
-       // // Transaction manager
-       // ((UserDirectory) nodeRoles).setTransactionManager(transactionManager);
-       // for (UserAdmin userAdmin : userAdmins.values()) {
-       // if (userAdmin instanceof UserDirectory)
-       // ((UserDirectory) userAdmin).setTransactionManager(transactionManager);
-       // }
-       //
-       // // JCR
-       // // initJcr(adminSession);
-       // }
-
-       Dictionary<String, Object> currentState() {
-               Dictionary<String, Object> res = new Hashtable<String, Object>();
-               res.put(NodeConstants.CN, NodeConstants.DEFAULT);
-               for (LdapName name : userAdmins.keySet()) {
-                       StringBuilder buf = new StringBuilder();
-                       if (userAdmins.get(name) instanceof UserDirectory) {
-                               UserDirectory userDirectory = (UserDirectory) userAdmins.get(name);
-                               String uri = UserAdminConf.propertiesAsUri(userDirectory.getProperties()).toString();
-                               res.put(uri, "");
-                       } else {
-                               buf.append('/').append(name.toString()).append("?readOnly=true");
-                       }
-               }
-               return res;
+       protected void postAdd(AbstractUserDirectory userDirectory) {
+               // JTA
+               TransactionManager tm = tmTracker.getService();
+               if (tm == null)
+                       throw new CmsException("A JTA transaction manager must be available.");
+               userDirectory.setTransactionManager(tm);
+               if (tmTracker.getService() instanceof BitronixTransactionManager)
+                       EhCacheXAResourceProducer.registerXAResource(cacheName, userDirectory.getXaResource());
        }
 
-       public void destroy() {
-               for (LdapName name : userAdmins.keySet()) {
-                       if (userAdmins.get(name) instanceof UserDirectory) {
-                               UserDirectory userDirectory = (UserDirectory) userAdmins.get(name);
-                               try {
-                                       // FIXME Make it less bitronix dependant
-                                       EhCacheXAResourceProducer.unregisterXAResource(cacheName, userDirectory.getXaResource());
-                               } catch (Exception e) {
-                                       log.error("Cannot unregister resource from Bitronix", e);
-                               }
-                               userDirectory.destroy();
-
-                       }
-               }
-       }
-
-       @Override
-       public Role createRole(String name, int type) {
-               return findUserAdmin(name).createRole(name, type);
-       }
-
-       @Override
-       public boolean removeRole(String name) {
-               boolean actuallyDeleted = findUserAdmin(name).removeRole(name);
-               nodeRoles.removeRole(name);
-               return actuallyDeleted;
+       protected void preDestroy(UserDirectory userDirectory) {
+               if (tmTracker.getService() instanceof BitronixTransactionManager)
+                       EhCacheXAResourceProducer.unregisterXAResource(cacheName, userDirectory.getXaResource());
        }
 
-       @Override
-       public Role getRole(String name) {
-               return findUserAdmin(name).getRole(name);
-       }
-
-       @Override
-       public Role[] getRoles(String filter) throws InvalidSyntaxException {
-               List<Role> res = new ArrayList<Role>();
-               for (UserAdmin userAdmin : userAdmins.values()) {
-                       res.addAll(Arrays.asList(userAdmin.getRoles(filter)));
-               }
-               res.addAll(Arrays.asList(nodeRoles.getRoles(filter)));
-               return res.toArray(new Role[res.size()]);
-       }
-
-       @Override
-       public User getUser(String key, String value) {
-               List<User> res = new ArrayList<User>();
-               for (UserAdmin userAdmin : userAdmins.values()) {
-                       User u = userAdmin.getUser(key, value);
-                       if (u != null)
-                               res.add(u);
-               }
-               // Note: node roles cannot contain users, so it is not searched
-               return res.size() == 1 ? res.get(0) : null;
-       }
-
-       @Override
-       public Authorization getAuthorization(User user) {
-               if (user == null) {// anonymous
-                       return nodeRoles.getAuthorization(null);
-               }
-               UserAdmin userAdmin = findUserAdmin(user.getName());
-               Authorization rawAuthorization = userAdmin.getAuthorization(user);
-               // gather system roles
-               Set<String> systemRoles = new HashSet<String>();
-               for (String role : rawAuthorization.getRoles()) {
-                       Authorization auth = nodeRoles.getAuthorization((User) userAdmin.getRole(role));
-                       systemRoles.addAll(Arrays.asList(auth.getRoles()));
-               }
-               Authorization authorization = new NodeAuthorization(rawAuthorization.getName(), rawAuthorization.toString(),
-                               systemRoles, rawAuthorization.getRoles());
-               // syncJcr(adminSession, authorization);
-               return authorization;
-       }
-
-       //
-       // USER ADMIN AGGREGATOR
-       //
-       public void addUserAdmin(String baseDn, UserAdmin userAdmin) {
-               try {
-                       LdapName key = new LdapName(baseDn);
-                       if (userAdmins.containsKey(key))
-                               throw new UserDirectoryException("There is already a user admin for " + baseDn);
-                       userAdmins.put(key, userAdmin);
-               } catch (InvalidNameException e) {
-                       throw new UserDirectoryException("Badly formatted base DN " + baseDn, e);
-               }
-               if (userAdmin instanceof UserDirectory) {
-                       UserDirectory userDirectory = (UserDirectory) userAdmin;
-                       try {
-                               userDirectory.setTransactionManager(tmTracker.getService());
-                               // FIXME Make it less bitronix dependant
-                               EhCacheXAResourceProducer.registerXAResource(cacheName, ((UserDirectory) userAdmin).getXaResource());
-                       } catch (Exception e) {
-                               log.error("Cannot register resource to Bitronix", e);
-                       }
-               }
-       }
-
-       private UserAdmin findUserAdmin(String name) {
-               try {
-                       return findUserAdmin(new LdapName(name));
-               } catch (InvalidNameException e) {
-                       throw new UserDirectoryException("Badly formatted name " + name, e);
-               }
-       }
-
-       private UserAdmin findUserAdmin(LdapName name) {
-               if (name.startsWith(ROLES_BASE))
-                       return nodeRoles;
-               List<UserAdmin> res = new ArrayList<UserAdmin>(1);
-               for (LdapName baseDn : userAdmins.keySet()) {
-                       if (name.startsWith(baseDn))
-                               res.add(userAdmins.get(baseDn));
-               }
-               if (res.size() == 0)
-                       throw new UserDirectoryException("Cannot find user admin for " + name);
-               if (res.size() > 1)
-                       throw new UserDirectoryException("Multiple user admin found for " + name);
-               return res.get(0);
-       }
-
-       public void setTransactionManager(TransactionManager transactionManager) {
-               if (nodeRoles instanceof UserDirectory)
-                       ((UserDirectory) nodeRoles).setTransactionManager(transactionManager);
-               for (UserAdmin userAdmin : userAdmins.values()) {
-                       if (userAdmin instanceof UserDirectory)
-                               ((UserDirectory) userAdmin).setTransactionManager(transactionManager);
-               }
-       }
-
-       // private void initUserAdmins(String userAdminUri, File nodeBaseDir) {
-       // // if (userAdminUri == null) {
-       // // String demoBaseDn = "dc=example,dc=com";
-       // // File businessRolesFile = new File(nodeBaseDir, demoBaseDn + ".ldif");
-       // // if (!businessRolesFile.exists())
-       // // try {
-       // //
-       // FileUtils.copyInputStreamToFile(getClass().getResourceAsStream(demoBaseDn
-       // // + ".ldif"),
-       // // businessRolesFile);
-       // // } catch (IOException e) {
-       // // throw new CmsException("Cannot copy demo resource", e);
-       // // }
-       // // userAdminUri = businessRolesFile.toURI().toString();
-       // // }
-       // String[] uris = userAdminUri.split(" ");
-       // for (String uri : uris) {
-       // URI u;
-       // try {
-       // u = new URI(uri);
-       // if (u.getPath() == null)
-       // throw new CmsException("URI " + uri + " must have a path in order to
-       // determine base DN");
-       // if (u.getScheme() == null) {
-       // if (uri.startsWith("/") || uri.startsWith("./") || uri.startsWith("../"))
-       // u = new File(uri).getCanonicalFile().toURI();
-       // else if (!uri.contains("/")) {
-       // u = new URI(nodeBaseDir.toURI() + uri);
-       // // u = new File(nodeBaseDir, uri).getCanonicalFile()
-       // // .toURI();
-       // } else
-       // throw new CmsException("Cannot interpret " + uri + " as an uri");
-       // } else if (u.getScheme().equals("file")) {
-       // u = new File(u).getCanonicalFile().toURI();
-       // }
-       // } catch (Exception e) {
-       // throw new CmsException("Cannot interpret " + uri + " as an uri", e);
-       // }
-       // Dictionary<String, ?> properties =
-       // UserAdminConf.uriAsProperties(u.toString());
-       // UserDirectory businessRoles;
-       // if (u.getScheme().startsWith("ldap")) {
-       // businessRoles = new LdapUserAdmin(properties);
-       // } else {
-       // businessRoles = new LdifUserAdmin(properties);
-       // }
-       // businessRoles.init();
-       // String baseDn = businessRoles.getBaseDn();
-       // if (userAdmins.containsKey(baseDn))
-       // throw new UserDirectoryException("There is already a user admin for " +
-       // baseDn);
-       // try {
-       // userAdmins.put(new LdapName(baseDn), (UserAdmin) businessRoles);
-       // } catch (InvalidNameException e) {
-       // throw new UserDirectoryException("Badly formatted base DN " + baseDn, e);
-       // }
-       // addUserAdmin(businessRoles.getBaseDn(), (UserAdmin) businessRoles);
-       // if (log.isDebugEnabled())
-       // log.debug("User directory " + businessRoles.getBaseDn() + " [" +
-       // u.getScheme() + "] enabled.");
-       // }
-       //
-       // }
-       //
-       // private void initNodeRoles(String nodeRolesUri, File nodeBaseDir) {
-       // String baseNodeRoleDn = AuthConstants.ROLES_BASEDN;
-       // if (nodeRolesUri == null) {
-       // File nodeRolesFile = new File(nodeBaseDir, baseNodeRoleDn + ".ldif");
-       // if (!nodeRolesFile.exists())
-       // try {
-       // FileUtils.copyInputStreamToFile(getClass().getResourceAsStream(baseNodeRoleDn
-       // + ".ldif"),
-       // nodeRolesFile);
-       // } catch (IOException e) {
-       // throw new CmsException("Cannot copy demo resource", e);
-       // }
-       // nodeRolesUri = nodeRolesFile.toURI().toString();
-       // }
-       //
-       // Dictionary<String, ?> nodeRolesProperties =
-       // UserAdminConf.uriAsProperties(nodeRolesUri);
-       // if
-       // (!nodeRolesProperties.get(UserAdminConf.baseDn.name()).equals(baseNodeRoleDn))
-       // {
-       // throw new CmsException("Invalid base dn for node roles");
-       // // TODO deal with "mounted" roles with a different baseDN
-       // }
-       // if (nodeRolesUri.startsWith("ldap")) {
-       // nodeRoles = new LdapUserAdmin(nodeRolesProperties);
-       // } else {
-       // nodeRoles = new LdifUserAdmin(nodeRolesProperties);
-       // }
-       // ((UserDirectory) nodeRoles).setExternalRoles(this);
-       // ((UserDirectory) nodeRoles).init();
-       // addUserAdmin(baseNodeRoleDn, (UserAdmin) nodeRoles);
-       // if (log.isTraceEnabled())
-       // log.trace("Node roles enabled.");
-       //
-       // }
-
-       /*
-        * JCR
-        */
-       // private void initJcr(Session adminSession) {
-       // try {
-       // JcrUtils.mkdirs(adminSession, homeBasePath);
-       // JcrUtils.mkdirs(adminSession, peopleBasePath);
-       // adminSession.save();
-       //
-       // JcrUtils.addPrivilege(adminSession, homeBasePath,
-       // AuthConstants.ROLE_USER_ADMIN, Privilege.JCR_READ);
-       // JcrUtils.addPrivilege(adminSession, peopleBasePath,
-       // AuthConstants.ROLE_USER_ADMIN, Privilege.JCR_ALL);
-       // adminSession.save();
-       // } catch (RepositoryException e) {
-       // throw new CmsException("Cannot initialize node user admin", e);
-       // }
-       // }
-       //
-       // private Node syncJcr(Session session, Authorization authorization) {
-       // // TODO check user name validity (e.g. should not start by ROLE_)
-       // String username = authorization.getName();
-       // // String[] roles = authorization.getRoles();
-       // try {
-       // Node userHome = UserJcrUtils.getUserHome(session, username);
-       // if (userHome == null) {
-       // String homePath = generateUserPath(homeBasePath, username);
-       // if (session.itemExists(homePath))// duplicate user id
-       // userHome =
-       // session.getNode(homePath).getParent().addNode(JcrUtils.lastPathElement(homePath));
-       // else
-       // userHome = JcrUtils.mkdirs(session, homePath);
-       // // userHome = JcrUtils.mkfolders(session, homePath);
-       // userHome.addMixin(ArgeoTypes.ARGEO_USER_HOME);
-       // userHome.setProperty(ArgeoNames.ARGEO_USER_ID, username);
-       // session.save();
-       //
-       // JcrUtils.clearAccessControList(session, homePath, username);
-       // JcrUtils.addPrivilege(session, homePath, username, Privilege.JCR_ALL);
-       // }
-       //
-       // Node userProfile = UserJcrUtils.getUserProfile(session, username);
-       // // new user
-       // if (userProfile == null) {
-       // String personPath = generateUserPath(peopleBasePath, username);
-       // Node personBase;
-       // if (session.itemExists(personPath))// duplicate user id
-       // personBase =
-       // session.getNode(personPath).getParent().addNode(JcrUtils.lastPathElement(personPath));
-       // else
-       // personBase = JcrUtils.mkdirs(session, personPath);
-       // userProfile = personBase.addNode(ArgeoNames.ARGEO_PROFILE);
-       // userProfile.addMixin(ArgeoTypes.ARGEO_USER_PROFILE);
-       // userProfile.setProperty(ArgeoNames.ARGEO_USER_ID, username);
-       // userProfile.setProperty(ArgeoNames.ARGEO_ENABLED, true);
-       // userProfile.setProperty(ArgeoNames.ARGEO_ACCOUNT_NON_EXPIRED, true);
-       // userProfile.setProperty(ArgeoNames.ARGEO_ACCOUNT_NON_LOCKED, true);
-       // userProfile.setProperty(ArgeoNames.ARGEO_CREDENTIALS_NON_EXPIRED, true);
-       // session.save();
-       //
-       // JcrUtils.clearAccessControList(session, userProfile.getPath(), username);
-       // JcrUtils.addPrivilege(session, userProfile.getPath(), username,
-       // Privilege.JCR_READ);
-       // }
-       //
-       // // Remote roles
-       // // if (roles != null) {
-       // // writeRemoteRoles(userProfile, roles);
-       // // }
-       // if (adminSession.hasPendingChanges())
-       // adminSession.save();
-       // return userProfile;
-       // } catch (RepositoryException e) {
-       // JcrUtils.discardQuietly(session);
-       // throw new ArgeoException("Cannot sync node security model for " +
-       // username, e);
-       // }
-       // }
-       //
-       // /** Generate path for a new user home */
-       // private String generateUserPath(String base, String username) {
-       // LdapName dn;
-       // try {
-       // dn = new LdapName(username);
-       // } catch (InvalidNameException e) {
-       // throw new ArgeoException("Invalid name " + username, e);
-       // }
-       // String userId = dn.getRdn(dn.size() - 1).getValue().toString();
-       // int atIndex = userId.indexOf('@');
-       // if (atIndex > 0) {
-       // String domain = userId.substring(0, atIndex);
-       // String name = userId.substring(atIndex + 1);
-       // return base + '/' + JcrUtils.firstCharsToPath(domain, 2) + '/' + domain +
-       // '/'
-       // + JcrUtils.firstCharsToPath(name, 2) + '/' + name;
-       // } else if (atIndex == 0 || atIndex == (userId.length() - 1)) {
-       // throw new ArgeoException("Unsupported username " + userId);
-       // } else {
-       // return base + '/' + JcrUtils.firstCharsToPath(userId, 2) + '/' + userId;
-       // }
-       // }
-
-       // /** Write remote roles used by remote access in the home directory */
-       // private void writeRemoteRoles(Node userHome, String[] roles)
-       // throws RepositoryException {
-       // boolean writeRoles = false;
-       // if (userHome.hasProperty(ArgeoNames.ARGEO_REMOTE_ROLES)) {
-       // Value[] remoteRoles = userHome.getProperty(
-       // ArgeoNames.ARGEO_REMOTE_ROLES).getValues();
-       // if (remoteRoles.length != roles.length)
-       // writeRoles = true;
-       // else
-       // for (int i = 0; i < remoteRoles.length; i++)
-       // if (!remoteRoles[i].getString().equals(roles[i]))
-       // writeRoles = true;
-       // } else
-       // writeRoles = true;
-       //
-       // if (writeRoles) {
-       // userHome.getSession().getWorkspace().getVersionManager()
-       // .checkout(userHome.getPath());
-       // userHome.setProperty(ArgeoNames.ARGEO_REMOTE_ROLES, roles);
-       // JcrUtils.updateLastModified(userHome);
-       // userHome.getSession().save();
-       // userHome.getSession().getWorkspace().getVersionManager()
-       // .checkin(userHome.getPath());
-       // if (log.isDebugEnabled())
-       // log.debug("Wrote remote roles " + roles + " for "
-       // + userHome.getProperty(ArgeoNames.ARGEO_USER_ID));
-       // }
-       //
-       // }
 }
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/UserDirectoryServiceFactory.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/UserDirectoryServiceFactory.java
deleted file mode 100644 (file)
index 0726e50..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-package org.argeo.cms.internal.kernel;
-
-import java.util.Dictionary;
-import java.util.HashMap;
-import java.util.Hashtable;
-import java.util.Map;
-
-import org.argeo.osgi.useradmin.LdapUserAdmin;
-import org.argeo.osgi.useradmin.LdifUserAdmin;
-import org.argeo.osgi.useradmin.UserAdminConf;
-import org.argeo.osgi.useradmin.UserDirectory;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.Constants;
-import org.osgi.framework.FrameworkUtil;
-import org.osgi.service.cm.ConfigurationException;
-import org.osgi.service.cm.ManagedServiceFactory;
-
-class UserDirectoryServiceFactory implements ManagedServiceFactory {
-       private final BundleContext bc = FrameworkUtil.getBundle(UserDirectoryServiceFactory.class).getBundleContext();
-
-       private Map<String, UserDirectory> userDirectories = new HashMap<>();
-
-       @Override
-       public String getName() {
-               return "User Directories Factory";
-       }
-
-       @Override
-       public void updated(String pid, Dictionary<String, ?> properties) throws ConfigurationException {
-               String uri = (String) properties.get(UserAdminConf.uri.name());
-               UserDirectory userDirectory = uri.startsWith("ldap:") ? new LdapUserAdmin(properties)
-                               : new LdifUserAdmin(properties);
-               String baseDn = userDirectory.getBaseDn();
-               Dictionary<String, Object> regProps = new Hashtable<>();
-               regProps.put(Constants.SERVICE_PID, pid);
-               regProps.put(UserAdminConf.baseDn.name(), baseDn);
-//             regProps.put(UserAdminConf.uri.name(), uri);
-               bc.registerService(UserDirectory.class, userDirectory, regProps);
-               userDirectories.put(pid, userDirectory);
-       }
-
-       @Override
-       public void deleted(String pid) {
-               userDirectories.remove(pid);
-       }
-
-       void shutdown() {
-               for (UserDirectory userDirectory : userDirectories.values())
-                       userDirectory.destroy();
-       }
-}
index 9714ade9a1ee75eb51faf466cbacf1c78a1adea6..e76769d71078150dc61ec24798729a9de15a8780 100644 (file)
@@ -13,6 +13,8 @@ import java.util.Hashtable;
 import java.util.List;
 import java.util.UUID;
 
+import javax.transaction.TransactionManager;
+
 import org.apache.commons.codec.binary.Base64;
 import org.apache.commons.codec.digest.DigestUtils;
 import org.apache.commons.io.IOUtils;
@@ -27,8 +29,12 @@ import bitronix.tm.resource.ehcache.EhCacheXAResourceProducer;
 import junit.framework.TestCase;
 
 public class LdifUserAdminTest extends TestCase implements BasicTestConstants {
-       private AbstractUserDirectory userAdmin;
        private BitronixTransactionManager tm;
+       private URI uri;
+       private AbstractUserDirectory userAdmin;
+
+       public void testConcurrent() throws Exception {
+       }
 
        @SuppressWarnings("unchecked")
        public void testEdition() throws Exception {
@@ -40,11 +46,13 @@ public class LdifUserAdminTest extends TestCase implements BasicTestConstants {
                demoUser.getProperties().put("cn", newName);
                assertEquals(newName, demoUser.getProperties().get("cn"));
                tm.commit();
+               persistAndRestart();
                assertEquals(newName, demoUser.getProperties().get("cn"));
 
                tm.begin();
                userAdmin.removeRole(DEMO_USER_DN);
                tm.commit();
+               persistAndRestart();
 
                // check data
                Role[] search = userAdmin.getRoles("(objectclass=inetOrgPerson)");
@@ -126,8 +134,7 @@ public class LdifUserAdminTest extends TestCase implements BasicTestConstants {
        @Override
        protected void setUp() throws Exception {
                Path tempDir = Files.createTempDirectory(getClass().getName());
-               URI uri;
-               String uriProp = System.getProperty("argeo.useradmin.uri");
+               String uriProp = System.getProperty("argeo.userdirectory.uri");
                if (uriProp != null)
                        uri = new URI(uriProp);
                else {
@@ -139,25 +146,39 @@ public class LdifUserAdminTest extends TestCase implements BasicTestConstants {
                        uri = ldifPath.toUri();
                }
 
+               bitronix.tm.Configuration tmConf = TransactionManagerServices.getConfiguration();
+               tmConf.setServerId(UUID.randomUUID().toString());
+               tmConf.setLogPart1Filename(new File(tempDir.toFile(), "btm1.tlog").getAbsolutePath());
+               tmConf.setLogPart2Filename(new File(tempDir.toFile(), "btm2.tlog").getAbsolutePath());
+               tm = TransactionManagerServices.getTransactionManager();
+
+               userAdmin = initUserAdmin(uri, tm);
+       }
+
+       private AbstractUserDirectory initUserAdmin(URI uri, TransactionManager tm) {
                Dictionary<String, Object> props = new Hashtable<>();
                props.put(UserAdminConf.uri.name(), uri.toString());
                props.put(UserAdminConf.baseDn.name(), BASE_DN);
                props.put(UserAdminConf.userBase.name(), "ou=users");
                props.put(UserAdminConf.groupBase.name(), "ou=groups");
+               AbstractUserDirectory userAdmin;
                if (uri.getScheme().startsWith("ldap"))
                        userAdmin = new LdapUserAdmin(props);
                else
                        userAdmin = new LdifUserAdmin(props);
                userAdmin.init();
-
-               bitronix.tm.Configuration tmConf = TransactionManagerServices.getConfiguration();
-               tmConf.setServerId(UUID.randomUUID().toString());
-               tmConf.setLogPart1Filename(new File(tempDir.toFile(), "btm1.tlog").getAbsolutePath());
-               tmConf.setLogPart2Filename(new File(tempDir.toFile(), "btm2.tlog").getAbsolutePath());
-               tm = TransactionManagerServices.getTransactionManager();
+               // JTA
                EhCacheXAResourceProducer.registerXAResource(UserDirectory.class.getName(), userAdmin.getXaResource());
-
                userAdmin.setTransactionManager(tm);
+               return userAdmin;
+       }
+
+       private void persistAndRestart() {
+               EhCacheXAResourceProducer.unregisterXAResource(UserDirectory.class.getName(), userAdmin.getXaResource());
+               if (userAdmin instanceof LdifUserAdmin)
+                       ((LdifUserAdmin) userAdmin).save();
+               userAdmin.destroy();
+               userAdmin = initUserAdmin(uri, tm);
        }
 
        @Override
index 48d657c2e21d00620a65920cd7e33972c68dc5d3..3f5bf850d55a890093804df97993f27f1eb809e4 100644 (file)
@@ -38,13 +38,12 @@ import org.osgi.service.useradmin.User;
 import org.osgi.service.useradmin.UserAdmin;
 
 /** Base class for a {@link UserDirectory}. */
-abstract class AbstractUserDirectory implements UserAdmin, UserDirectory {
+public abstract class AbstractUserDirectory implements UserAdmin, UserDirectory {
        private final static Log log = LogFactory.getLog(AbstractUserDirectory.class);
 
        private final Hashtable<String, Object> properties;
-       private final String baseDn;
-       private final String userObjectClass;
-       private final String groupObjectClass;
+       private final LdapName baseDn;
+       private final String userObjectClass, userBase, groupObjectClass, groupBase;
 
        private final boolean readOnly;
        private final URI uri;
@@ -56,10 +55,11 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory {
        private String memberAttributeId = "member";
        private List<String> credentialAttributeIds = Arrays.asList(new String[] { LdifName.userPassword.name() });
 
+       // JTA
        private TransactionManager transactionManager;
        private WcXaResource xaResource = new WcXaResource(this);
 
-       AbstractUserDirectory(Dictionary<String, ?> props) {
+       public AbstractUserDirectory(Dictionary<String, ?> props) {
                properties = new Hashtable<String, Object>();
                for (Enumeration<String> keys = props.keys(); keys.hasMoreElements();) {
                        String key = keys.nextElement();
@@ -76,7 +76,11 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory {
                                throw new UserDirectoryException("Badly formatted URI " + uriStr, e);
                        }
 
-               baseDn = UserAdminConf.baseDn.getValue(properties).toString();
+               try {
+                       baseDn = new LdapName(UserAdminConf.baseDn.getValue(properties));
+               } catch (InvalidNameException e) {
+                       throw new UserDirectoryException("Badly formated base DN " + UserAdminConf.baseDn.getValue(properties), e);
+               }
                String readOnlyStr = UserAdminConf.readOnly.getValue(properties);
                if (readOnlyStr == null) {
                        readOnly = readOnlyDefault(uri);
@@ -85,7 +89,9 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory {
                        readOnly = new Boolean(readOnlyStr);
 
                userObjectClass = UserAdminConf.userObjectClass.getValue(properties);
+               userBase = UserAdminConf.userBase.getValue(properties);
                groupObjectClass = UserAdminConf.groupObjectClass.getValue(properties);
+               groupBase = UserAdminConf.groupBase.getValue(properties);
        }
 
        /** Returns the groups this user is a direct member of. */
@@ -105,7 +111,7 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory {
 
        }
 
-       boolean isEditing() {
+       protected boolean isEditing() {
                return xaResource.wc() != null;
        }
 
@@ -116,7 +122,7 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory {
                return wc;
        }
 
-       void checkEdit() {
+       protected void checkEdit() {
                Transaction transaction;
                try {
                        transaction = transactionManager.getTransaction();
@@ -135,7 +141,7 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory {
                }
        }
 
-       List<Role> getAllRoles(DirectoryUser user) {
+       protected List<Role> getAllRoles(DirectoryUser user) {
                List<Role> allRoles = new ArrayList<Role>();
                if (user != null) {
                        collectRoles(user, allRoles);
@@ -334,11 +340,11 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory {
        }
 
        // GETTERS
-       String getMemberAttributeId() {
+       protected String getMemberAttributeId() {
                return memberAttributeId;
        }
 
-       List<String> getCredentialAttributeIds() {
+       protected List<String> getCredentialAttributeIds() {
                return credentialAttributeIds;
        }
 
@@ -371,27 +377,36 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory {
                return readOnly;
        }
 
-       UserAdmin getExternalRoles() {
+       protected UserAdmin getExternalRoles() {
                return externalRoles;
        }
 
-       public String getBaseDn() {
-               return baseDn;
+       public LdapName getBaseDn() {
+               // always clone so that the property is not modified by reference
+               return (LdapName) baseDn.clone();
        }
 
        /** dn can be null, in that case a default should be returned. */
-       protected String getUserObjectClass() {
+       public String getUserObjectClass() {
                return userObjectClass;
        }
 
+       public String getUserBase() {
+               return userBase;
+       }
+
        protected String newUserObjectClass(LdapName dn) {
                return getUserObjectClass();
        }
 
-       protected String getGroupObjectClass() {
+       public String getGroupObjectClass() {
                return groupObjectClass;
        }
 
+       public String getGroupBase() {
+               return groupBase;
+       }
+
        public Dictionary<String, Object> getProperties() {
                return properties;
        }
diff --git a/org.argeo.security.core/src/org/argeo/osgi/useradmin/AggregatingAuthorization.java b/org.argeo.security.core/src/org/argeo/osgi/useradmin/AggregatingAuthorization.java
new file mode 100644 (file)
index 0000000..f270b8d
--- /dev/null
@@ -0,0 +1,73 @@
+package org.argeo.osgi.useradmin;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import javax.security.auth.x500.X500Principal;
+
+import org.osgi.service.useradmin.Authorization;
+
+class AggregatingAuthorization implements Authorization {
+       private final String name;
+       private final String displayName;
+       private final List<String> systemRoles;
+       private final List<String> roles;
+
+       public AggregatingAuthorization(String name, String displayName,
+                       Collection<String> systemRoles, String[] roles) {
+               this.name = new X500Principal(name).getName();
+               this.displayName = displayName;
+               this.systemRoles = Collections.unmodifiableList(new ArrayList<String>(
+                               systemRoles));
+               this.roles = Collections.unmodifiableList(Arrays.asList(roles));
+       }
+
+       @Override
+       public String getName() {
+               return name;
+       }
+
+       @Override
+       public boolean hasRole(String name) {
+               if (systemRoles.contains(name))
+                       return true;
+               if (roles.contains(name))
+                       return true;
+               return false;
+       }
+
+       @Override
+       public String[] getRoles() {
+               int size = systemRoles.size() + roles.size();
+               List<String> res = new ArrayList<String>(size);
+               res.addAll(systemRoles);
+               res.addAll(roles);
+               return res.toArray(new String[size]);
+       }
+
+       @Override
+       public int hashCode() {
+               if (name == null)
+                       return super.hashCode();
+               return name.hashCode();
+       }
+
+       @Override
+       public boolean equals(Object obj) {
+               if (!(obj instanceof Authorization))
+                       return false;
+               Authorization that = (Authorization) obj;
+               if (name == null)
+                       return that.getName() == null;
+               return name.equals(that.getName());
+       }
+
+       @Override
+       public String toString() {
+               return displayName;
+       }
+
+}
diff --git a/org.argeo.security.core/src/org/argeo/osgi/useradmin/AggregatingUserAdmin.java b/org.argeo.security.core/src/org/argeo/osgi/useradmin/AggregatingUserAdmin.java
new file mode 100644 (file)
index 0000000..860c5ef
--- /dev/null
@@ -0,0 +1,189 @@
+package org.argeo.osgi.useradmin;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.naming.InvalidNameException;
+import javax.naming.ldap.LdapName;
+
+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;
+
+/**
+ * Aggregates multiple {@link UserDirectory} and integrates them with system
+ * roles.
+ */
+public class AggregatingUserAdmin implements UserAdmin {
+       private final LdapName systemRolesBaseDn;
+
+       // DAOs
+       private AbstractUserDirectory systemRoles = null;
+       private Map<LdapName, AbstractUserDirectory> businessRoles = new HashMap<LdapName, AbstractUserDirectory>();
+
+       public AggregatingUserAdmin(String systemRolesBaseDn) {
+               try {
+                       this.systemRolesBaseDn = new LdapName(systemRolesBaseDn);
+               } catch (InvalidNameException e) {
+                       throw new UserDirectoryException("Cannot initialize " + AggregatingUserAdmin.class, e);
+               }
+       }
+
+       @Override
+       public Role createRole(String name, int type) {
+               return findUserAdmin(name).createRole(name, type);
+       }
+
+       @Override
+       public boolean removeRole(String name) {
+               boolean actuallyDeleted = findUserAdmin(name).removeRole(name);
+               systemRoles.removeRole(name);
+               return actuallyDeleted;
+       }
+
+       @Override
+       public Role getRole(String name) {
+               return findUserAdmin(name).getRole(name);
+       }
+
+       @Override
+       public Role[] getRoles(String filter) throws InvalidSyntaxException {
+               List<Role> res = new ArrayList<Role>();
+               for (UserAdmin userAdmin : businessRoles.values()) {
+                       res.addAll(Arrays.asList(userAdmin.getRoles(filter)));
+               }
+               res.addAll(Arrays.asList(systemRoles.getRoles(filter)));
+               return res.toArray(new Role[res.size()]);
+       }
+
+       @Override
+       public User getUser(String key, String value) {
+               List<User> res = new ArrayList<User>();
+               for (UserAdmin userAdmin : businessRoles.values()) {
+                       User u = userAdmin.getUser(key, value);
+                       if (u != null)
+                               res.add(u);
+               }
+               // Note: node roles cannot contain users, so it is not searched
+               return res.size() == 1 ? res.get(0) : null;
+       }
+
+       @Override
+       public Authorization getAuthorization(User user) {
+               if (user == null) {// anonymous
+                       return systemRoles.getAuthorization(null);
+               }
+               UserAdmin userAdmin = findUserAdmin(user.getName());
+               Authorization rawAuthorization = userAdmin.getAuthorization(user);
+               // gather system roles
+               Set<String> sysRoles = new HashSet<String>();
+               for (String role : rawAuthorization.getRoles()) {
+                       Authorization auth = systemRoles.getAuthorization((User) userAdmin.getRole(role));
+                       sysRoles.addAll(Arrays.asList(auth.getRoles()));
+               }
+               Authorization authorization = new AggregatingAuthorization(rawAuthorization.getName(),
+                               rawAuthorization.toString(), sysRoles, rawAuthorization.getRoles());
+               return authorization;
+       }
+
+       //
+       // USER ADMIN AGGREGATOR
+       //
+       protected void addUserDirectory(AbstractUserDirectory userDirectory) {
+               LdapName baseDn = userDirectory.getBaseDn();
+               if (isSystemRolesBaseDn(baseDn)) {
+                       this.systemRoles = userDirectory;
+                       systemRoles.setExternalRoles(this);
+               } else {
+                       if (businessRoles.containsKey(baseDn))
+                               throw new UserDirectoryException("There is already a user admin for " + baseDn);
+                       businessRoles.put(baseDn, userDirectory);
+               }
+               userDirectory.init();
+               postAdd(userDirectory);
+       }
+
+       /** Called after a new user directory has been added */
+       protected void postAdd(AbstractUserDirectory userDirectory) {
+       }
+
+       private UserAdmin findUserAdmin(String name) {
+               try {
+                       return findUserAdmin(new LdapName(name));
+               } catch (InvalidNameException e) {
+                       throw new UserDirectoryException("Badly formatted name " + name, e);
+               }
+       }
+
+       private UserAdmin findUserAdmin(LdapName name) {
+               if (name.startsWith(systemRolesBaseDn))
+                       return systemRoles;
+               List<UserAdmin> res = new ArrayList<UserAdmin>(1);
+               for (LdapName baseDn : businessRoles.keySet()) {
+                       if (name.startsWith(baseDn))
+                               res.add(businessRoles.get(baseDn));
+               }
+               if (res.size() == 0)
+                       throw new UserDirectoryException("Cannot find user admin for " + name);
+               if (res.size() > 1)
+                       throw new UserDirectoryException("Multiple user admin found for " + name);
+               return res.get(0);
+       }
+
+       protected boolean isSystemRolesBaseDn(LdapName baseDn) {
+               return baseDn.equals(systemRolesBaseDn);
+       }
+
+       protected Dictionary<String, Object> currentState() {
+               Dictionary<String, Object> res = new Hashtable<String, Object>();
+               // res.put(NodeConstants.CN, NodeConstants.DEFAULT);
+               for (LdapName name : businessRoles.keySet()) {
+                       AbstractUserDirectory userDirectory = businessRoles.get(name);
+                       String uri = UserAdminConf.propertiesAsUri(userDirectory.getProperties()).toString();
+                       res.put(uri, "");
+               }
+               return res;
+       }
+
+       public void destroy() {
+               for (LdapName name : businessRoles.keySet()) {
+                       AbstractUserDirectory userDirectory = businessRoles.get(name);
+                       destroy(userDirectory);
+               }
+               businessRoles.clear();
+               businessRoles = null;
+               destroy(systemRoles);
+               systemRoles = null;
+       }
+
+       private void destroy(AbstractUserDirectory userDirectory) {
+               preDestroy(userDirectory);
+               userDirectory.destroy();
+       }
+
+       protected void removeUserDirectory(LdapName baseDn) {
+               if (isSystemRolesBaseDn(baseDn))
+                       throw new UserDirectoryException("System roles cannot be removed ");
+               if (!businessRoles.containsKey(baseDn))
+                       throw new UserDirectoryException("No user directory registered for " + baseDn);
+               AbstractUserDirectory userDirectory = businessRoles.remove(baseDn);
+               destroy(userDirectory);
+       }
+
+       /**
+        * Called before each user directory is destroyed, so that additional
+        * actions can be performed.
+        */
+       protected void preDestroy(UserDirectory userDirectory) {
+       }
+
+}
index c2e26ca4cfc09b610cc9880679f9175c8da7ea83..b1360c65c5b3039a388d25383b1adf465f5d8967 100644 (file)
@@ -113,7 +113,7 @@ public class LdapUserAdmin extends AbstractUserDirectory {
                        SearchControls searchControls = new SearchControls();
                        searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
 
-                       String searchBase = getBaseDn();
+                       LdapName searchBase = getBaseDn();
                        NamingEnumeration<SearchResult> results = getLdapContext().search(searchBase, searchFilter, searchControls);
 
                        ArrayList<DirectoryUser> res = new ArrayList<DirectoryUser>();
@@ -139,7 +139,7 @@ public class LdapUserAdmin extends AbstractUserDirectory {
                }
        }
 
-       private LdapName toDn(String baseDn, Binding binding) throws InvalidNameException {
+       private LdapName toDn(LdapName baseDn, Binding binding) throws InvalidNameException {
                return new LdapName(binding.isRelative() ? binding.getName() + "," + baseDn : binding.getName());
        }
 
@@ -153,7 +153,7 @@ public class LdapUserAdmin extends AbstractUserDirectory {
                        SearchControls searchControls = new SearchControls();
                        searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
 
-                       String searchBase = getBaseDn();
+                       LdapName searchBase = getBaseDn();
                        NamingEnumeration<SearchResult> results = getLdapContext().search(searchBase, searchFilter, searchControls);
 
                        while (results.hasMoreElements()) {
index 1491721228bfeac52923b90dc9d556fa0cc26d46..e5de738363249090a1aea8f846cb4d339af6cc35 100644 (file)
@@ -1,27 +1,23 @@
 package org.argeo.osgi.useradmin;
 
-import java.util.Dictionary;
-
-import javax.transaction.TransactionManager;
+import javax.naming.ldap.LdapName;
 import javax.transaction.xa.XAResource;
 
-import org.osgi.service.useradmin.UserAdmin;
-
 /** Information about a user directory. */
 public interface UserDirectory {
-       public String getBaseDn();
+       /** The base DN of all entries in this user directory */
+       public LdapName getBaseDn();
 
-       public void setExternalRoles(UserAdmin externalRoles);
+       /** The related {@link XAResource} */
+       public XAResource getXaResource();
 
-       /** Keys listed and described in {@link UserAdminConf}. */
-       public Dictionary<String, Object> getProperties();
+       public boolean isReadOnly();
 
-       // Transitional. In the future, more will be managed in OSGi.
-       public void setTransactionManager(TransactionManager transactionManager);
+       public String getUserObjectClass();
 
-       public void init();
+       public String getUserBase();
 
-       public void destroy();
+       public String getGroupObjectClass();
 
-       public XAResource getXaResource();
+       public String getGroupBase();
 }