Introduce aggregating node user admin
authorMathieu Baudier <mbaudier@argeo.org>
Fri, 4 Sep 2015 10:01:43 +0000 (10:01 +0000)
committerMathieu Baudier <mbaudier@argeo.org>
Fri, 4 Sep 2015 10:01:43 +0000 (10:01 +0000)
git-svn-id: https://svn.argeo.org/commons/trunk@8364 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc

org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelConstants.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeSecurity.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeUserAdmin.java [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/internal/kernel/dc=example,dc=com.ldif [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/internal/kernel/demo.ldif

index f273242c530543ca4d2617803f87516ed489c6a3..2663650b9187217e4f6eb9ff7aa3158e42e369b6 100644 (file)
@@ -19,6 +19,7 @@ public interface KernelConstants {
        // Node Security
        /** URI to an LDIF file used as initialization or backend */
        final static String USERADMIN_URI = "argeo.node.useradmin.uri";
+       final static String ROLES_BASEDN = "ou=system,ou=node";
 
        final static String[] DEFAULT_CNDS = { "/org/argeo/jcr/argeo.cnd",
                        "/org/argeo/cms/cms.cnd" };
index 5e9877935495ec113540de8f918ed06d0f4ddc0d..83216d0484a96a896d6c4f95e1473bcce3ce1efe 100644 (file)
@@ -1,10 +1,15 @@
 package org.argeo.cms.internal.kernel;
 
+import java.io.File;
+import java.io.IOException;
+
 import javax.jcr.RepositoryException;
 
+import org.apache.commons.io.FileUtils;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.argeo.cms.CmsException;
+import org.argeo.cms.KernelHeader;
 import org.argeo.cms.internal.useradmin.SimpleJcrSecurityModel;
 import org.argeo.cms.internal.useradmin.jackrabbit.JackrabbitUserAdminService;
 import org.argeo.osgi.useradmin.AbstractLdapUserAdmin;
@@ -17,6 +22,7 @@ import org.argeo.security.core.InternalAuthenticationProvider;
 import org.argeo.security.core.OsAuthenticationProvider;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.useradmin.Role;
 import org.osgi.service.useradmin.UserAdmin;
 import org.springframework.security.authentication.AnonymousAuthenticationProvider;
 import org.springframework.security.authentication.AnonymousAuthenticationToken;
@@ -36,7 +42,7 @@ class NodeSecurity implements AuthenticationManager {
        private final InternalAuthenticationProvider internalAuth;
        private final AnonymousAuthenticationProvider anonymousAuth;
        private final JackrabbitUserAdminService userAdminService;
-       private final AbstractLdapUserAdmin userAdmin;
+       private final NodeUserAdmin userAdmin;
 
        private ServiceRegistration<AuthenticationManager> authenticationManagerReg;
        private ServiceRegistration<UserAdminService> userAdminServiceReg;
@@ -60,15 +66,41 @@ class NodeSecurity implements AuthenticationManager {
                userAdminService.setSecurityModel(new SimpleJcrSecurityModel());
                userAdminService.init();
 
+               userAdmin = new NodeUserAdmin();
+
+               String baseDn = "dc=example,dc=com";
                String userAdminUri = KernelUtils
                                .getFrameworkProp(KernelConstants.USERADMIN_URI);
                if (userAdminUri == null)
-                       userAdminUri = getClass().getResource("demo.ldif").toString();
+                       userAdminUri = getClass().getResource(baseDn + ".ldif").toString();
 
+               AbstractLdapUserAdmin businessRoles;
                if (userAdminUri.startsWith("ldap"))
-                       userAdmin = new LdapUserAdmin(userAdminUri);
-               else
-                       userAdmin = new LdifUserAdmin(userAdminUri);
+                       businessRoles = new LdapUserAdmin(userAdminUri);
+               else {
+                       businessRoles = new LdifUserAdmin(userAdminUri);
+               }
+               businessRoles.init();
+               userAdmin.addUserAdmin(baseDn, businessRoles);
+
+               File osgiInstanceDir = KernelUtils.getOsgiInstanceDir();
+               File homeDir = new File(osgiInstanceDir, "node");
+
+               String baseNodeRoleDn = KernelConstants.ROLES_BASEDN;
+               File nodeRolesFile = new File(homeDir, baseNodeRoleDn + ".ldif");
+               try {
+                       FileUtils.copyInputStreamToFile(
+                                       getClass().getResourceAsStream("demo.ldif"), nodeRolesFile);
+               } catch (IOException e) {
+                       throw new CmsException("Cannot copy demo resource", e);
+               }
+               LdifUserAdmin nodeRoles = new LdifUserAdmin(nodeRolesFile.toURI()
+                               .toString());
+               nodeRoles.setExternalRoles(userAdmin);
+               nodeRoles.init();
+               // nodeRoles.createRole(KernelHeader.ROLE_ADMIN, Role.GROUP);
+               userAdmin.addUserAdmin(baseNodeRoleDn, nodeRoles);
+
        }
 
        public void publish() {
@@ -92,7 +124,7 @@ class NodeSecurity implements AuthenticationManager {
                userAdminServiceReg.unregister();
                authenticationManagerReg.unregister();
 
-               userAdmin.destroy();
+               // userAdmin.destroy();
                userAdminReg.unregister();
        }
 
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeUserAdmin.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeUserAdmin.java
new file mode 100644 (file)
index 0000000..d8dcf0e
--- /dev/null
@@ -0,0 +1,149 @@
+package org.argeo.cms.internal.kernel;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.naming.InvalidNameException;
+import javax.naming.ldap.LdapName;
+
+import org.argeo.osgi.useradmin.ArgeoUserAdminException;
+import org.argeo.osgi.useradmin.UserAdminAggregator;
+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 NodeUserAdmin implements UserAdmin, UserAdminAggregator {
+       final static LdapName ROLES_BASE;
+       static {
+               try {
+                       ROLES_BASE = new LdapName(KernelConstants.ROLES_BASEDN);
+               } catch (InvalidNameException e) {
+                       throw new ArgeoUserAdminException("Cannot initialize "
+                                       + NodeUserAdmin.class, e);
+               }
+       }
+
+       private UserAdmin nodeRoles = null;
+       private Map<LdapName, UserAdmin> userAdmins = new HashMap<LdapName, UserAdmin>();
+
+       @Override
+       public Role createRole(String name, int type) {
+               return findUserAdmin(name).createRole(name, type);
+       }
+
+       @Override
+       public boolean removeRole(String name) {
+               return findUserAdmin(name).removeRole(name);
+       }
+
+       @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) {
+               UserAdmin userAdmin = findUserAdmin(user.getName());
+               // FIXME clarify assumptions
+               return userAdmin.getAuthorization(user);
+               // String[] roles = auth.getRoles();
+               // // Gather system roles
+               // Set<String> systemRoles = new HashSet<String>();
+               // for(String businessRole:roles){
+               //
+               // }
+               // return null;
+       }
+
+       //
+       // USER ADMIN AGGREGATOR
+       //
+       @Override
+       public synchronized void addUserAdmin(String baseDn, UserAdmin userAdmin) {
+               if (baseDn.equals(KernelConstants.ROLES_BASEDN)) {
+                       nodeRoles = userAdmin;
+                       return;
+               }
+
+               if (userAdmins.containsKey(baseDn))
+                       throw new ArgeoUserAdminException(
+                                       "There is already a user admin for " + baseDn);
+               try {
+                       userAdmins.put(new LdapName(baseDn), userAdmin);
+               } catch (InvalidNameException e) {
+                       throw new ArgeoUserAdminException("Badly formatted base DN "
+                                       + baseDn, e);
+               }
+       }
+
+       @Override
+       public synchronized void removeUserAdmin(String baseDn) {
+               if (baseDn.equals(KernelConstants.ROLES_BASEDN))
+                       throw new ArgeoUserAdminException("Node roles cannot be removed.");
+               LdapName base;
+               try {
+                       base = new LdapName(baseDn);
+               } catch (InvalidNameException e) {
+                       throw new ArgeoUserAdminException("Badly formatted base DN "
+                                       + baseDn, e);
+               }
+               if (!userAdmins.containsKey(base))
+                       throw new ArgeoUserAdminException("There is no user admin for "
+                                       + base);
+               userAdmins.remove(base);
+       }
+
+       private UserAdmin findUserAdmin(String name) {
+               try {
+                       return findUserAdmin(new LdapName(name));
+               } catch (InvalidNameException e) {
+                       throw new ArgeoUserAdminException("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 ArgeoUserAdminException("Cannot find user admin for "
+                                       + name);
+               if (res.size() > 1)
+                       throw new ArgeoUserAdminException("Multiple user admin found for "
+                                       + name);
+               return res.get(0);
+       }
+}
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/dc=example,dc=com.ldif b/org.argeo.cms/src/org/argeo/cms/internal/kernel/dc=example,dc=com.ldif
new file mode 100644 (file)
index 0000000..fda4ead
--- /dev/null
@@ -0,0 +1,41 @@
+dn: dc=example,dc=com
+objectClass: domain
+objectClass: extensibleObject
+objectClass: top
+dc: example
+
+dn: ou=groups,dc=example,dc=com
+objectClass: organizationalUnit
+objectClass: top
+ou: groups
+
+dn: ou=users,dc=example,dc=com
+objectClass: organizationalUnit
+objectClass: top
+ou: users
+
+dn: uid=demo,ou=users,dc=example,dc=com
+objectClass: inetOrgPerson
+objectClass: organizationalPerson
+objectClass: person
+objectClass: top
+cn: Demo User
+description: Demo user
+givenname: Demo
+mail: demo@localhost
+sn: User
+uid: demo
+userpassword:: e1NIQX1pZVNWNTVRYytlUU9hWURSU2hhL0Fqek5USkU9
+
+dn: uid=root,ou=users,dc=example,dc=com
+objectClass: inetOrgPerson
+objectClass: person
+objectClass: organizationalPerson
+objectClass: top
+cn: Super User
+description: Superuser
+givenname: Super
+mail: root@localhost
+sn: User
+uid: root
+userpassword:: e1NIQX1pZVNWNTVRYytlUU9hWURSU2hhL0Fqek5USkU9
index 1f172b29c4a84515580c8daf22d8ed525cbb103d..7370ea5af275ea171cfd42e7be1562027f4342bc 100644 (file)
@@ -1,45 +1,3 @@
-dn: dc=example,dc=com
-objectClass: domain
-objectClass: extensibleObject
-objectClass: top
-dc: example
-
-dn: ou=groups,dc=example,dc=com
-objectClass: organizationalUnit
-objectClass: top
-ou: groups
-
-dn: ou=users,dc=example,dc=com
-objectClass: organizationalUnit
-objectClass: top
-ou: users
-
-dn: uid=demo,ou=users,dc=example,dc=com
-objectClass: inetOrgPerson
-objectClass: organizationalPerson
-objectClass: person
-objectClass: top
-cn: Demo User
-description: Demo user
-givenname: Demo
-mail: demo@localhost
-sn: User
-uid: demo
-userpassword:: e1NIQX1pZVNWNTVRYytlUU9hWURSU2hhL0Fqek5USkU9
-
-dn: uid=root,ou=users,dc=example,dc=com
-objectClass: inetOrgPerson
-objectClass: person
-objectClass: organizationalPerson
-objectClass: top
-cn: Super User
-description: Superuser
-givenname: Super
-mail: root@localhost
-sn: User
-uid: root
-userpassword:: e1NIQX1pZVNWNTVRYytlUU9hWURSU2hhL0Fqek5USkU9
-
 dn: ou=node
 objectClass: organizationalUnit
 objectClass: top