Start clarifying user admin.
authorMathieu Baudier <mbaudier@argeo.org>
Mon, 13 Jun 2022 05:53:47 +0000 (07:53 +0200)
committerMathieu Baudier <mbaudier@argeo.org>
Mon, 13 Jun 2022 05:53:47 +0000 (07:53 +0200)
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserAdminWrapper.java
org.argeo.cms/src/org/argeo/cms/internal/auth/CmsUserManagerImpl.java
org.argeo.cms/src/org/argeo/cms/internal/osgi/NodeUserAdmin.java
org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsUserAdmin.java
org.argeo.util/src/org/argeo/osgi/useradmin/AbstractUserDirectory.java
org.argeo.util/src/org/argeo/osgi/useradmin/AggregatingUserAdmin.java
org.argeo.util/src/org/argeo/osgi/useradmin/OsUserUtils.java
org.argeo.util/src/org/argeo/osgi/useradmin/UserDirectory.java
org.argeo.util/src/org/argeo/osgi/useradmin/UserDirectoryException.java

index 16aa78316142a1a37f2340293c7ee15b4437ff9d..465c15a17be6199487d90b7e7e95a9c5c9582ad3 100644 (file)
@@ -91,7 +91,7 @@ public class UserAdminWrapper {
                Map<String, String> dns = new HashMap<String, String>();
                for (UserDirectory userDirectory : userDirectories.keySet()) {
                        Boolean readOnly = userDirectory.isReadOnly();
-                       String baseDn = userDirectory.getBaseDn().toString();
+                       String baseDn = userDirectory.getBasePath();
 
                        if (onlyWritable && readOnly)
                                continue;
index 19136606da492a3f5d6029bc5f526a5907aa03fd..782487a9ad7b083275e3db8d65810f750d0fbdcd 100644 (file)
@@ -229,7 +229,7 @@ public class CmsUserManagerImpl implements CmsUserManager {
                Map<String, String> dns = new HashMap<String, String>();
                for (UserDirectory userDirectory : userDirectories.keySet()) {
                        Boolean readOnly = userDirectory.isReadOnly();
-                       String baseDn = userDirectory.getBaseDn().toString();
+                       String baseDn = userDirectory.getBasePath();
 
                        if (onlyWritable && readOnly)
                                continue;
index 4eda98c35c6087bb36527a98781f26ab7eb3d2fd..0746d4301d5b324ac61921a56c77d720e342c767 100644 (file)
@@ -5,9 +5,6 @@ import java.util.HashMap;
 import java.util.Hashtable;
 import java.util.Map;
 
-import javax.naming.InvalidNameException;
-import javax.naming.ldap.LdapName;
-
 import org.argeo.api.cms.CmsConstants;
 import org.argeo.api.cms.CmsLog;
 import org.argeo.cms.internal.runtime.CmsUserAdmin;
@@ -27,22 +24,17 @@ public class NodeUserAdmin extends CmsUserAdmin implements ManagedServiceFactory
        private final static CmsLog log = CmsLog.getLog(NodeUserAdmin.class);
 
        // OSGi
-       private Map<String, LdapName> pidToBaseDn = new HashMap<>();
+       private Map<String, String> pidToBaseDn = new HashMap<>();
 
        @Override
        public void updated(String pid, Dictionary<String, ?> properties) throws ConfigurationException {
 
-               LdapName baseDn;
-               try {
-                       baseDn = new LdapName((String) properties.get(UserAdminConf.baseDn.name()));
-               } catch (InvalidNameException e) {
-                       throw new IllegalArgumentException(e);
-               }
+               String basePath = (String) properties.get(UserAdminConf.baseDn.name());
 
                // FIXME make updates more robust
-               if (pidToBaseDn.containsValue(baseDn)) {
+               if (pidToBaseDn.containsValue(basePath)) {
                        if (log.isDebugEnabled())
-                               log.debug("Ignoring user directory update of " + baseDn);
+                               log.debug("Ignoring user directory update of " + basePath);
                        return;
                }
 
@@ -50,14 +42,14 @@ public class NodeUserAdmin extends CmsUserAdmin implements ManagedServiceFactory
                // OSGi
                Hashtable<String, Object> regProps = new Hashtable<>();
                regProps.put(Constants.SERVICE_PID, pid);
-               if (isSystemRolesBaseDn(baseDn))
+               if (isSystemRolesBaseDn(basePath))
                        regProps.put(Constants.SERVICE_RANKING, Integer.MAX_VALUE);
-               regProps.put(UserAdminConf.baseDn.name(), baseDn);
+               regProps.put(UserAdminConf.baseDn.name(), basePath);
 
                CmsActivator.getBundleContext().registerService(UserDirectory.class, userDirectory, regProps);
-               pidToBaseDn.put(pid, baseDn);
+               pidToBaseDn.put(pid, basePath);
 
-               if (isSystemRolesBaseDn(baseDn)) {
+               if (isSystemRolesBaseDn(basePath)) {
                        // publishes itself as user admin only when system roles are available
                        Dictionary<String, Object> userAdminregProps = new Hashtable<>();
                        userAdminregProps.put(CmsConstants.CN, CmsConstants.DEFAULT);
@@ -71,8 +63,8 @@ public class NodeUserAdmin extends CmsUserAdmin implements ManagedServiceFactory
                // assert pidToServiceRegs.get(pid) != null;
                assert pidToBaseDn.get(pid) != null;
                // pidToServiceRegs.remove(pid).unregister();
-               LdapName baseDn = pidToBaseDn.remove(pid);
-               removeUserDirectory(baseDn);
+               String basePath = pidToBaseDn.remove(pid);
+               removeUserDirectory(basePath);
        }
 
        @Override
index 9364ee4a3069bfdfcf905d41ae74ecc21172180b..391c94a3da1c3011b06846b34727b4da4001891e 100644 (file)
@@ -11,9 +11,9 @@ import java.security.PrivilegedExceptionAction;
 import java.util.ArrayList;
 import java.util.Dictionary;
 import java.util.Iterator;
+import java.util.Optional;
 import java.util.Set;
 
-import javax.naming.ldap.LdapName;
 import javax.security.auth.Subject;
 import javax.security.auth.callback.Callback;
 import javax.security.auth.callback.CallbackHandler;
@@ -35,7 +35,6 @@ import org.argeo.cms.internal.http.client.HttpCredentialProvider;
 import org.argeo.cms.internal.http.client.SpnegoAuthScheme;
 import org.argeo.osgi.transaction.WorkControl;
 import org.argeo.osgi.transaction.WorkTransaction;
-import org.argeo.osgi.useradmin.AbstractUserDirectory;
 import org.argeo.osgi.useradmin.AggregatingUserAdmin;
 import org.argeo.osgi.useradmin.LdapUserAdmin;
 import org.argeo.osgi.useradmin.LdifUserAdmin;
@@ -96,7 +95,7 @@ public class CmsUserAdmin extends AggregatingUserAdmin {
                }
 
                // Create
-               AbstractUserDirectory userDirectory;
+               UserDirectory userDirectory;
                if (realm != null || UserAdminConf.SCHEME_LDAP.equals(u.getScheme())
                                || UserAdminConf.SCHEME_LDAPS.equals(u.getScheme())) {
                        userDirectory = new LdapUserAdmin(properties);
@@ -108,14 +107,14 @@ public class CmsUserAdmin extends AggregatingUserAdmin {
                } else {
                        throw new IllegalArgumentException("Unsupported scheme " + u.getScheme());
                }
-               LdapName baseDn = userDirectory.getBaseDn();
+               String basePath = userDirectory.getBasePath();
 
                addUserDirectory(userDirectory);
-               if (isSystemRolesBaseDn(baseDn)) {
+               if (isSystemRolesBaseDn(basePath)) {
                        addStandardSystemRoles();
                }
                if (log.isDebugEnabled()) {
-                       log.debug("User directory " + userDirectory.getBaseDn() + (u != null ? " [" + u.getScheme() + "]" : "")
+                       log.debug("User directory " + userDirectory.getBasePath() + (u != null ? " [" + u.getScheme() + "]" : "")
                                        + " enabled." + (realm != null ? " " + realm + " realm." : ""));
                }
                return userDirectory;
@@ -153,13 +152,14 @@ public class CmsUserAdmin extends AggregatingUserAdmin {
                }
        }
 
-       protected void postAdd(AbstractUserDirectory userDirectory) {
+       @Override
+       protected void postAdd(UserDirectory userDirectory) {
                userDirectory.setTransactionControl(transactionManager);
 
-               Object realm = userDirectory.getProperties().get(UserAdminConf.realm.name());
-               if (realm != null) {
+               Optional<String> realm = userDirectory.getRealm();
+               if (realm.isPresent()) {
                        if (Files.exists(nodeKeyTab)) {
-                               String servicePrincipal = getKerberosServicePrincipal(realm.toString());
+                               String servicePrincipal = getKerberosServicePrincipal(realm.get());
                                if (servicePrincipal != null) {
                                        CallbackHandler callbackHandler = new CallbackHandler() {
                                                @Override
@@ -193,9 +193,10 @@ public class CmsUserAdmin extends AggregatingUserAdmin {
                }
        }
 
-       protected void preDestroy(AbstractUserDirectory userDirectory) {
-               Object realm = userDirectory.getProperties().get(UserAdminConf.realm.name());
-               if (realm != null) {
+       @Override
+       protected void preDestroy(UserDirectory userDirectory) {
+               Optional<String> realm = userDirectory.getRealm();
+               if (realm.isPresent()) {
                        if (acceptorCredentials != null) {
                                try {
                                        acceptorCredentials.dispose();
index e028e384e1a8a7af1b5656124e0a66e6f50e9c00..19cd06886ba3b83b2daa1ef42e291e88876b72a0 100644 (file)
@@ -17,10 +17,12 @@ import java.util.Enumeration;
 import java.util.Hashtable;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Optional;
 
 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.BasicAttribute;
@@ -39,7 +41,7 @@ import org.osgi.service.useradmin.User;
 import org.osgi.service.useradmin.UserAdmin;
 
 /** Base class for a {@link UserDirectory}. */
-public abstract class AbstractUserDirectory implements UserAdmin, UserDirectory {
+abstract class AbstractUserDirectory implements UserAdmin, UserDirectory {
        static final String SHARED_STATE_USERNAME = "javax.security.auth.login.name";
        static final String SHARED_STATE_PASSWORD = "javax.security.auth.login.password";
 
@@ -99,7 +101,8 @@ public abstract class AbstractUserDirectory implements UserAdmin, UserDirectory
                        userBaseDn = new LdapName(userBase + "," + baseDn);
                        groupBaseDn = new LdapName(groupBase + "," + baseDn);
                } catch (InvalidNameException e) {
-                       throw new UserDirectoryException("Badly formated base DN " + UserAdminConf.baseDn.getValue(properties), e);
+                       throw new IllegalArgumentException("Badly formated base DN " + UserAdminConf.baseDn.getValue(properties),
+                                       e);
                }
                String readOnlyStr = UserAdminConf.readOnly.getValue(properties);
                if (readOnlyStr == null) {
@@ -133,6 +136,19 @@ public abstract class AbstractUserDirectory implements UserAdmin, UserDirectory
 
        }
 
+       @Override
+       public String getBasePath() {
+               return getBaseDn().toString();
+       }
+
+       @Override
+       public Optional<String> getRealm() {
+               Object realm = getProperties().get(UserAdminConf.realm.name());
+               if (realm == null)
+                       return Optional.empty();
+               return Optional.of(realm.toString());
+       }
+
        protected boolean isEditing() {
                return xaResource.wc() != null;
        }
@@ -145,20 +161,11 @@ public abstract class AbstractUserDirectory implements UserAdmin, UserDirectory
        }
 
        protected void checkEdit() {
-//             Transaction transaction;
-//             try {
-//                     transaction = transactionManager.getTransaction();
-//             } catch (SystemException e) {
-//                     throw new UserDirectoryException("Cannot get transaction", e);
-//             }
-//             if (transaction == null)
-//                     throw new UserDirectoryException("A transaction needs to be active in order to edit");
                if (xaResource.wc() == null) {
                        try {
-//                             transaction.enlistResource(xaResource);
                                transactionControl.getWorkContext().registerXAResource(xaResource, null);
                        } catch (Exception e) {
-                               throw new UserDirectoryException("Cannot enlist " + xaResource, e);
+                               throw new IllegalStateException("Cannot enlist " + xaResource, e);
                        }
                } else {
                }
@@ -189,8 +196,8 @@ public abstract class AbstractUserDirectory implements UserAdmin, UserDirectory
                                        if (group != null)
                                                allRoles.add(group);
                                }
-                       } catch (Exception e) {
-                               throw new UserDirectoryException("Cannot get memberOf groups for " + user, e);
+                       } catch (NamingException e) {
+                               throw new IllegalStateException("Cannot get memberOf groups for " + user, e);
                        }
                } else {
                        for (LdapName groupDn : getDirectGroups(user.getDn())) {
@@ -211,7 +218,7 @@ public abstract class AbstractUserDirectory implements UserAdmin, UserDirectory
        // USER ADMIN
        @Override
        public Role getRole(String name) {
-               return doGetRole(toDn(name));
+               return doGetRole(toLdapName(name));
        }
 
        protected DirectoryUser doGetRole(LdapName dn) {
@@ -260,7 +267,7 @@ public abstract class AbstractUserDirectory implements UserAdmin, UserDirectory
                if (key != null) {
                        doGetUser(key, value, collectedUsers);
                } else {
-                       throw new UserDirectoryException("Key cannot be null");
+                       throw new IllegalArgumentException("Key cannot be null");
                }
 
                if (collectedUsers.size() == 1) {
@@ -278,7 +285,7 @@ public abstract class AbstractUserDirectory implements UserAdmin, UserDirectory
                        List<DirectoryUser> users = doGetRoles(f);
                        collectedUsers.addAll(users);
                } catch (InvalidSyntaxException e) {
-                       throw new UserDirectoryException("Cannot get user with " + key + "=" + value, e);
+                       throw new IllegalArgumentException("Cannot get user with " + key + "=" + value, e);
                }
        }
 
@@ -292,7 +299,7 @@ public abstract class AbstractUserDirectory implements UserAdmin, UserDirectory
                        try {
                                DirectoryUser directoryUser = (DirectoryUser) scopedUserAdmin.getRole(user.getName());
                                if (directoryUser == null)
-                                       throw new UserDirectoryException("No scoped user found for " + user);
+                                       throw new IllegalStateException("No scoped user found for " + user);
                                LdifAuthorization authorization = new LdifAuthorization(directoryUser,
                                                scopedUserAdmin.getAllRoles(directoryUser));
                                return authorization;
@@ -306,9 +313,9 @@ public abstract class AbstractUserDirectory implements UserAdmin, UserDirectory
        public Role createRole(String name, int type) {
                checkEdit();
                UserDirectoryWorkingCopy wc = getWorkingCopy();
-               LdapName dn = toDn(name);
+               LdapName dn = toLdapName(name);
                if ((daoHasRole(dn) && !wc.getDeletedUsers().containsKey(dn)) || wc.getNewUsers().containsKey(dn))
-                       throw new UserDirectoryException("Already a role " + name);
+                       throw new IllegalArgumentException("Already a role " + name);
                BasicAttributes attrs = new BasicAttributes(true);
                // attrs.put(LdifName.dn.name(), dn.toString());
                Rdn nameRdn = dn.getRdn(dn.size() - 1);
@@ -350,7 +357,7 @@ public abstract class AbstractUserDirectory implements UserAdmin, UserDirectory
                        attrs.put(objClass);
                        newRole = new LdifGroup(this, dn, attrs);
                } else
-                       throw new UserDirectoryException("Unsupported type " + type);
+                       throw new IllegalArgumentException("Unsupported type " + type);
                return newRole;
        }
 
@@ -358,7 +365,7 @@ public abstract class AbstractUserDirectory implements UserAdmin, UserDirectory
        public boolean removeRole(String name) {
                checkEdit();
                UserDirectoryWorkingCopy wc = getWorkingCopy();
-               LdapName dn = toDn(name);
+               LdapName dn = toLdapName(name);
                boolean actuallyDeleted;
                if (daoHasRole(dn) || wc.getNewUsers().containsKey(dn)) {
                        DirectoryUser user = (DirectoryUser) getRole(name);
@@ -387,15 +394,6 @@ public abstract class AbstractUserDirectory implements UserAdmin, UserDirectory
 
        }
 
-       // UTILITIES
-       protected LdapName toDn(String name) {
-               try {
-                       return new LdapName(name);
-               } catch (InvalidNameException e) {
-                       throw new UserDirectoryException("Badly formatted name", e);
-               }
-       }
-
        // GETTERS
        protected String getMemberAttributeId() {
                return memberAttributeId;
@@ -514,4 +512,14 @@ public abstract class AbstractUserDirectory implements UserAdmin, UserDirectory
                return scoped;
        }
 
+       /*
+        * STATIC UTILITIES
+        */
+       static LdapName toLdapName(String name) {
+               try {
+                       return new LdapName(name);
+               } catch (InvalidNameException e) {
+                       throw new IllegalArgumentException(name + " is not an LDAP name", e);
+               }
+       }
 }
index c274ed97e581c934515d11adce2bf19b82e2661a..5613c28484ac4dff444505d5b96f3e1ab6e74434 100644 (file)
@@ -1,5 +1,7 @@
 package org.argeo.osgi.useradmin;
 
+import static org.argeo.osgi.useradmin.AbstractUserDirectory.toLdapName;
+
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -75,9 +77,9 @@ public class AggregatingUserAdmin implements UserAdmin {
        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);
+                       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;
@@ -153,15 +155,19 @@ public class AggregatingUserAdmin implements UserAdmin {
        //
        // USER ADMIN AGGREGATOR
        //
-       protected void addUserDirectory(AbstractUserDirectory userDirectory) {
-               LdapName baseDn = userDirectory.getBaseDn();
-               if (isSystemRolesBaseDn(baseDn)) {
+       protected void addUserDirectory(UserDirectory ud) {
+               if (!(ud instanceof AbstractUserDirectory))
+                       throw new IllegalArgumentException("Only " + AbstractUserDirectory.class.getName() + " is supported");
+               AbstractUserDirectory userDirectory = (AbstractUserDirectory) ud;
+               String basePath = userDirectory.getBasePath();
+               if (isSystemRolesBaseDn(basePath)) {
                        this.systemRoles = userDirectory;
                        systemRoles.setExternalRoles(this);
-               } else if (isTokensBaseDn(baseDn)) {
+               } else if (isTokensBaseDn(basePath)) {
                        this.tokens = userDirectory;
                        tokens.setExternalRoles(this);
                } else {
+                       LdapName baseDn = toLdapName(basePath);
                        if (businessRoles.containsKey(baseDn))
                                throw new UserDirectoryException("There is already a user admin for " + baseDn);
                        businessRoles.put(baseDn, userDirectory);
@@ -171,20 +177,9 @@ public class AggregatingUserAdmin implements UserAdmin {
        }
 
        /** Called after a new user directory has been added */
-       protected void postAdd(AbstractUserDirectory userDirectory) {
+       protected void postAdd(UserDirectory userDirectory) {
        }
 
-//     private UserAdmin findUserAdmin(User user) {
-//             if (user == null)
-//                     throw new IllegalArgumentException("User should not be null");
-//             AbstractUserDirectory userAdmin = findUserAdmin(user.getName());
-//             if (user instanceof DirectoryUser) {
-//                     return userAdmin;
-//             } else {
-//                     return userAdmin.scope(user);
-//             }
-//     }
-
        private AbstractUserDirectory findUserAdmin(String name) {
                try {
                        return findUserAdmin(new LdapName(name));
@@ -223,12 +218,12 @@ public class AggregatingUserAdmin implements UserAdmin {
                return res.get(0);
        }
 
-       protected boolean isSystemRolesBaseDn(LdapName baseDn) {
-               return baseDn.equals(systemRolesBaseDn);
+       protected boolean isSystemRolesBaseDn(String basePath) {
+               return toLdapName(basePath).equals(systemRolesBaseDn);
        }
 
-       protected boolean isTokensBaseDn(LdapName baseDn) {
-               return tokensBaseDn != null && baseDn.equals(tokensBaseDn);
+       protected boolean isTokensBaseDn(String basePath) {
+               return tokensBaseDn != null && toLdapName(basePath).equals(tokensBaseDn);
        }
 
 //     protected Dictionary<String, Object> currentState() {
@@ -258,9 +253,10 @@ public class AggregatingUserAdmin implements UserAdmin {
                userDirectory.destroy();
        }
 
-       protected void removeUserDirectory(LdapName baseDn) {
-               if (isSystemRolesBaseDn(baseDn))
+       protected void removeUserDirectory(String basePath) {
+               if (isSystemRolesBaseDn(basePath))
                        throw new UserDirectoryException("System roles cannot be removed ");
+               LdapName baseDn = toLdapName(basePath);
                if (!businessRoles.containsKey(baseDn))
                        throw new UserDirectoryException("No user directory registered for " + baseDn);
                AbstractUserDirectory userDirectory = businessRoles.remove(baseDn);
@@ -271,7 +267,7 @@ public class AggregatingUserAdmin implements UserAdmin {
         * Called before each user directory is destroyed, so that additional actions
         * can be performed.
         */
-       protected void preDestroy(AbstractUserDirectory userDirectory) {
+       protected void preDestroy(UserDirectory userDirectory) {
        }
 
 }
index ad6bf881653aabd565e1f16abfc99b69ea621f42..5d0cbf687c55cf1f07d90a7d72d091ff048dddec 100644 (file)
@@ -10,9 +10,10 @@ import javax.security.auth.login.Configuration;
 import javax.security.auth.login.LoginContext;
 import javax.security.auth.login.LoginException;
 
+/** Log in based on JDK-provided OS integration. */
 public class OsUserUtils {
-       private static String LOGIN_CONTEXT_USER_NIX = "USER_NIX";
-       private static String LOGIN_CONTEXT_USER_NT = "USER_NT";
+       private final static String LOGIN_CONTEXT_USER_NIX = "USER_NIX";
+       private final static String LOGIN_CONTEXT_USER_NT = "USER_NT";
 
        public static String getOsUsername() {
                return System.getProperty("user.name");
index ff80c5ac8385bf4aacac7699102754004cbdb6a6..7d773a9e72283aed73fabe695314e1cd2da12a4f 100644 (file)
@@ -1,15 +1,22 @@
 package org.argeo.osgi.useradmin;
 
-import javax.naming.ldap.LdapName;
-import javax.transaction.xa.XAResource;
+import java.util.Optional;
+
+import org.argeo.osgi.transaction.WorkControl;
 
 /** Information about a user directory. */
 public interface UserDirectory {
-       /** The base DN of all entries in this user directory */
-       LdapName getBaseDn();
+       /**
+        * The base of the hierarchy defined by this directory. This could typically be
+        * an LDAP base DN.
+        */
+       String getBasePath();
+
+//     /** The base DN of all entries in this user directory */
+//     LdapName getBaseDn();
 
-       /** The related {@link XAResource} */
-       XAResource getXaResource();
+//     /** The related {@link XAResource} */
+//     XAResource getXaResource();
 
        boolean isReadOnly();
 
@@ -22,4 +29,9 @@ public interface UserDirectory {
        String getGroupObjectClass();
 
        String getGroupBase();
+
+       Optional<String> getRealm();
+
+       @Deprecated
+       void setTransactionControl(WorkControl transactionControl);
 }
index 613d0fdf0abb600776341750933162820428d466..0140a196d3db3d4b831e811bdbf7b68e682b3d53 100644 (file)
@@ -6,6 +6,7 @@ import org.osgi.service.useradmin.UserAdmin;
  * Exceptions related to Argeo's implementation of OSGi {@link UserAdmin}
  * service.
  */
+@Deprecated
 public class UserDirectoryException extends RuntimeException {
        private static final long serialVersionUID = 1419352360062048603L;