Improve user management
[lgpl/argeo-commons.git] / org.argeo.util / src / org / argeo / osgi / useradmin / DirectoryUserAdmin.java
index 9f6d62d7a64600e85224c717786c0379335868fe..8ed23ad2ec92d747c66b3ba395ddb80844b525fa 100644 (file)
@@ -8,9 +8,10 @@ import static org.argeo.util.naming.LdapObjs.person;
 import static org.argeo.util.naming.LdapObjs.top;
 
 import java.net.URI;
-import java.nio.channels.UnsupportedAddressTypeException;
+import java.security.PrivilegedAction;
 import java.util.ArrayList;
 import java.util.Dictionary;
+import java.util.Hashtable;
 import java.util.Iterator;
 import java.util.List;
 
@@ -21,16 +22,19 @@ import javax.naming.directory.BasicAttribute;
 import javax.naming.directory.BasicAttributes;
 import javax.naming.ldap.LdapName;
 import javax.naming.ldap.Rdn;
+import javax.security.auth.Subject;
+import javax.security.auth.kerberos.KerberosTicket;
 
+import org.argeo.util.CurrentSubject;
 import org.argeo.util.directory.DirectoryConf;
 import org.argeo.util.directory.DirectoryDigestUtils;
 import org.argeo.util.directory.HierarchyUnit;
 import org.argeo.util.directory.ldap.AbstractLdapDirectory;
+import org.argeo.util.directory.ldap.LdapDao;
 import org.argeo.util.directory.ldap.LdapEntry;
 import org.argeo.util.directory.ldap.LdapEntryWorkingCopy;
 import org.argeo.util.directory.ldap.LdapNameUtils;
 import org.argeo.util.directory.ldap.LdifDao;
-import org.argeo.util.naming.LdapObjs;
 import org.osgi.framework.Filter;
 import org.osgi.framework.FrameworkUtil;
 import org.osgi.framework.InvalidSyntaxException;
@@ -66,7 +70,13 @@ public class DirectoryUserAdmin extends AbstractLdapDirectory implements UserAdm
         */
 
        protected AbstractLdapDirectory scope(User user) {
-               throw new UnsupportedAddressTypeException();
+               if (getDirectoryDao() instanceof LdapDao) {
+                       return scopeLdap(user);
+               } else if (getDirectoryDao() instanceof LdifDao) {
+                       return scopeLdif(user);
+               } else {
+                       throw new IllegalStateException("Unsupported DAO " + getDirectoryDao().getClass());
+               }
        }
 
        protected DirectoryUserAdmin scopeLdap(User user) {
@@ -74,7 +84,7 @@ public class DirectoryUserAdmin extends AbstractLdapDirectory implements UserAdm
                String username = (String) credentials.get(SHARED_STATE_USERNAME);
                if (username == null)
                        username = user.getName();
-               Dictionary<String, Object> properties = cloneProperties();
+               Dictionary<String, Object> properties = cloneConfigProperties();
                properties.put(Context.SECURITY_PRINCIPAL, username.toString());
                Object pwdCred = credentials.get(SHARED_STATE_PASSWORD);
                byte[] pwd = (byte[]) pwdCred;
@@ -84,7 +94,9 @@ public class DirectoryUserAdmin extends AbstractLdapDirectory implements UserAdm
                } else {
                        properties.put(Context.SECURITY_AUTHENTICATION, "GSSAPI");
                }
-               return new DirectoryUserAdmin(null, properties, true);
+               DirectoryUserAdmin scopedDirectory = new DirectoryUserAdmin(null, properties, true);
+               scopedDirectory.init();
+               return scopedDirectory;
        }
 
        protected DirectoryUserAdmin scopeLdif(User user) {
@@ -102,13 +114,14 @@ public class DirectoryUserAdmin extends AbstractLdapDirectory implements UserAdm
                } else {
                        throw new IllegalStateException("Password is required");
                }
-               Dictionary<String, Object> properties = cloneProperties();
+               Dictionary<String, Object> properties = cloneConfigProperties();
                properties.put(DirectoryConf.readOnly.name(), "true");
                DirectoryUserAdmin scopedUserAdmin = new DirectoryUserAdmin(null, properties, true);
 //             scopedUserAdmin.groups = Collections.unmodifiableNavigableMap(groups);
 //             scopedUserAdmin.users = Collections.unmodifiableNavigableMap(users);
                // FIXME do it better
                ((LdifDao) getDirectoryDao()).scope((LdifDao) scopedUserAdmin.getDirectoryDao());
+               scopedUserAdmin.init();
                return scopedUserAdmin;
        }
 
@@ -126,25 +139,31 @@ public class DirectoryUserAdmin extends AbstractLdapDirectory implements UserAdm
 
        @Override
        public Role getRoleByPath(String path) {
-               return (Role) doGetRole(pathToName(path));
+               LdapEntry entry = doGetRole(pathToName(path));
+               if (!(entry instanceof Role)) {
+                       throw new IllegalStateException("Path must be a UserAdmin Role.");
+               } else {
+                       return (Role) entry;
+               }
        }
 
        protected List<Role> getAllRoles(DirectoryUser user) {
                List<Role> allRoles = new ArrayList<Role>();
                if (user != null) {
-                       collectRoles(user, allRoles);
+                       collectRoles((LdapEntry) user, allRoles);
                        allRoles.add(user);
                } else
                        collectAnonymousRoles(allRoles);
                return allRoles;
        }
 
-       private void collectRoles(DirectoryUser user, List<Role> allRoles) {
+       private void collectRoles(LdapEntry user, List<Role> allRoles) {
                List<LdapEntry> allEntries = new ArrayList<>();
-               LdapEntry entry = (LdapEntry) user;
+               LdapEntry entry = user;
                collectGroups(entry, allEntries);
                for (LdapEntry e : allEntries) {
-                       allRoles.add((Role) e);
+                       if (e instanceof Role)
+                               allRoles.add((Role) e);
                }
 //             Attributes attrs = user.getAttributes();
 //             // TODO centralize attribute name
@@ -260,21 +279,51 @@ public class DirectoryUserAdmin extends AbstractLdapDirectory implements UserAdm
 
        @Override
        public Authorization getAuthorization(User user) {
-               if (user == null || user instanceof DirectoryUser) {
-                       return new LdifAuthorization(user, getAllRoles((DirectoryUser) user));
+               if (user == null) {// anonymous
+                       return new LdifAuthorization(user, getAllRoles(null));
+               }
+               LdapName userName = toLdapName(user.getName());
+               if (isExternal(userName) && user instanceof LdapEntry) {
+                       List<Role> allRoles = new ArrayList<Role>();
+                       collectRoles((LdapEntry) user, allRoles);
+                       return new LdifAuthorization(user, allRoles);
                } else {
-                       // bind
-                       DirectoryUserAdmin scopedUserAdmin = (DirectoryUserAdmin) scope(user);
-                       try {
-                               DirectoryUser directoryUser = (DirectoryUser) scopedUserAdmin.getRole(user.getName());
-                               if (directoryUser == null)
-                                       throw new IllegalStateException("No scoped user found for " + user);
-                               LdifAuthorization authorization = new LdifAuthorization(directoryUser,
-                                               scopedUserAdmin.getAllRoles(directoryUser));
-                               return authorization;
-                       } finally {
-                               scopedUserAdmin.destroy();
+
+                       Subject currentSubject = CurrentSubject.current();
+                       if (currentSubject != null //
+                                       && getRealm().isPresent() //
+                                       && !currentSubject.getPrivateCredentials(Authorization.class).isEmpty() //
+                                       && !currentSubject.getPrivateCredentials(KerberosTicket.class).isEmpty()) //
+                       {
+                               // TODO not only Kerberos but also bind scope with kept password ?
+                               Authorization auth = currentSubject.getPrivateCredentials(Authorization.class).iterator().next();
+                               // bind with authenticating user
+                               DirectoryUserAdmin scopedUserAdmin = Subject.doAs(currentSubject,
+                                               (PrivilegedAction<DirectoryUserAdmin>) () -> (DirectoryUserAdmin) scope(
+                                                               new AuthenticatingUser(auth.getName(), new Hashtable<>())));
+                               return getAuthorizationFromScoped(scopedUserAdmin, user);
                        }
+
+                       if (user instanceof DirectoryUser) {
+                               return new LdifAuthorization(user, getAllRoles((DirectoryUser) user));
+                       } else {
+                               // bind with authenticating user
+                               DirectoryUserAdmin scopedUserAdmin = (DirectoryUserAdmin) scope(user);
+                               return getAuthorizationFromScoped(scopedUserAdmin, user);
+                       }
+               }
+       }
+
+       private Authorization getAuthorizationFromScoped(DirectoryUserAdmin scopedUserAdmin, User user) {
+               try {
+                       DirectoryUser directoryUser = (DirectoryUser) scopedUserAdmin.getRole(user.getName());
+                       if (directoryUser == null)
+                               throw new IllegalStateException("No scoped user found for " + user);
+                       LdifAuthorization authorization = new LdifAuthorization(directoryUser,
+                                       scopedUserAdmin.getAllRoles(directoryUser));
+                       return authorization;
+               } finally {
+                       scopedUserAdmin.destroy();
                }
        }
 
@@ -283,7 +332,7 @@ public class DirectoryUserAdmin extends AbstractLdapDirectory implements UserAdm
                checkEdit();
                LdapEntryWorkingCopy wc = getWorkingCopy();
                LdapName dn = toLdapName(name);
-               if ((getDirectoryDao().daoHasEntry(dn) && !wc.getDeletedData().containsKey(dn))
+               if ((getDirectoryDao().entryExists(dn) && !wc.getDeletedData().containsKey(dn))
                                || wc.getNewData().containsKey(dn))
                        throw new IllegalArgumentException("Already a role " + name);
                BasicAttributes attrs = new BasicAttributes(true);
@@ -297,13 +346,13 @@ public class DirectoryUserAdmin extends AbstractLdapDirectory implements UserAdm
                        return getRole(name);
                } else {
                        wc.getModifiedData().put(dn, attrs);
-                       LdapEntry newRole = newRole(dn, type, attrs);
+                       LdapEntry newRole = doCreateRole(dn, type, attrs);
                        wc.getNewData().put(dn, newRole);
                        return (Role) newRole;
                }
        }
 
-       protected LdapEntry newRole(LdapName dn, int type, Attributes attrs) {
+       private LdapEntry doCreateRole(LdapName dn, int type, Attributes attrs) {
                LdapEntry newRole;
                BasicAttribute objClass = new BasicAttribute(objectClass.name());
                if (type == Role.USER) {
@@ -318,14 +367,14 @@ public class DirectoryUserAdmin extends AbstractLdapDirectory implements UserAdm
                        objClass.add(top.name());
                        objClass.add(extensibleObject.name());
                        attrs.put(objClass);
-                       newRole = newUser(dn, attrs);
+                       newRole = newUser(dn);
                } else if (type == Role.GROUP) {
                        String groupObjClass = getGroupObjectClass();
                        objClass.add(groupObjClass);
                        // objClass.add(LdifName.extensibleObject.name());
                        objClass.add(top.name());
                        attrs.put(objClass);
-                       newRole = newGroup(dn, attrs);
+                       newRole = newGroup(dn);
                } else
                        throw new IllegalArgumentException("Unsupported type " + type);
                return newRole;
@@ -367,7 +416,7 @@ public class DirectoryUserAdmin extends AbstractLdapDirectory implements UserAdm
 
        @Override
        public Iterable<? extends Role> getHierarchyUnitRoles(HierarchyUnit hierarchyUnit, String filter, boolean deep) {
-               LdapName dn = LdapNameUtils.toLdapName(hierarchyUnit.getContext());
+               LdapName dn = LdapNameUtils.toLdapName(hierarchyUnit.getBase());
                try {
                        return getRoles(dn, filter, deep);
                } catch (InvalidSyntaxException e) {
@@ -378,19 +427,13 @@ public class DirectoryUserAdmin extends AbstractLdapDirectory implements UserAdm
        /*
         * ROLES CREATION
         */
-       protected LdapEntry newUser(LdapName name, Attributes attrs) {
+       protected LdapEntry newUser(LdapName name) {
                // TODO support devices, applications, etc.
-               return new LdifUser.LdifPerson(this, name, attrs);
+               return new LdifUser(this, name);
        }
 
-       protected LdapEntry newGroup(LdapName name, Attributes attrs) {
-               if (LdapNameUtils.getParentRdn(name).equals(getSystemRoleBaseRdn()))
-                       return new LdifGroup.LdifSystemPermissions(this, name, attrs);
-
-               if (hasObjectClass(attrs, LdapObjs.organization))
-                       return new LdifGroup.LdifOrganization(this, name, attrs);
-               else
-                       return new LdifGroup.LdifFunctionalGroup(this, name, attrs);
+       protected LdapEntry newGroup(LdapName name) {
+               return new LdifGroup(this, name);
 
        }