Simplify node user admin APIs.
authorMathieu Baudier <mbaudier@argeo.org>
Thu, 11 Jun 2020 09:14:37 +0000 (11:14 +0200)
committerMathieu Baudier <mbaudier@argeo.org>
Thu, 11 Jun 2020 09:14:37 +0000 (11:14 +0200)
org.argeo.cms.e4/OSGI-INF/userAdminWrapper.xml
org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserAdminWrapper.java
org.argeo.cms/bnd.bnd
org.argeo.cms/src/org/argeo/cms/internal/http/client/SpnegoAuthScheme.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelConstants.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeHttp.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeUserAdmin.java
org.argeo.enterprise/src/org/argeo/osgi/useradmin/AggregatingUserAdmin.java
org.argeo.enterprise/src/org/argeo/osgi/useradmin/UserAdminConf.java

index 4b9d021eab5b688a8b62dd29ee97761443dfb189..6fa13d402e069bdaa71aa7e5275a211689fe522c 100644 (file)
@@ -6,4 +6,5 @@
    <service>
       <provide interface="org.argeo.cms.e4.users.UserAdminWrapper"/>
    </service>
+   <reference bind="addUserDirectory" cardinality="0..n" interface="org.argeo.osgi.useradmin.UserDirectory" name="UserDirectory" policy="static" unbind="removeUserDirectory"/>
 </scr:component>
index 951dd2415892dc92539bf188b50e2b4f1fd8fb4b..60d232abac0a690f095753bb2b3900ad0343cc46 100644 (file)
@@ -2,12 +2,11 @@ package org.argeo.cms.e4.users;
 
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.Dictionary;
 import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
-import java.util.TreeSet;
 
 import javax.transaction.Status;
 import javax.transaction.UserTransaction;
@@ -15,6 +14,7 @@ import javax.transaction.UserTransaction;
 import org.argeo.api.NodeConstants;
 import org.argeo.cms.CmsException;
 import org.argeo.osgi.useradmin.UserAdminConf;
+import org.argeo.osgi.useradmin.UserDirectory;
 import org.osgi.service.useradmin.UserAdmin;
 import org.osgi.service.useradmin.UserAdminEvent;
 import org.osgi.service.useradmin.UserAdminListener;
@@ -24,7 +24,9 @@ public class UserAdminWrapper {
 
        private UserAdmin userAdmin;
        // private ServiceReference<UserAdmin> userAdminServiceReference;
-       private Set<String> uris;
+//     private Set<String> uris;
+       private Map<UserDirectory, Hashtable<String, String>> userDirectories = Collections
+                       .synchronizedMap(new LinkedHashMap<>());
        private UserTransaction userTransaction;
 
        // First effort to simplify UX while managing users and groups
@@ -89,21 +91,34 @@ public class UserAdminWrapper {
 
        public Map<String, String> getKnownBaseDns(boolean onlyWritable) {
                Map<String, String> dns = new HashMap<String, String>();
-               for (String uri : uris) {
-                       if (!uri.startsWith("/"))
-                               continue;
-                       Dictionary<String, ?> props = UserAdminConf.uriAsProperties(uri);
-                       String readOnly = UserAdminConf.readOnly.getValue(props);
-                       String baseDn = UserAdminConf.baseDn.getValue(props);
+               for (UserDirectory userDirectory : userDirectories.keySet()) {
+                       Boolean readOnly = userDirectory.isReadOnly();
+                       String baseDn = userDirectory.getBaseDn().toString();
 
-                       if (onlyWritable && "true".equals(readOnly))
+                       if (onlyWritable && readOnly)
                                continue;
                        if (baseDn.equalsIgnoreCase(NodeConstants.ROLES_BASEDN))
                                continue;
                        if (baseDn.equalsIgnoreCase(NodeConstants.TOKENS_BASEDN))
                                continue;
-                       dns.put(baseDn, uri);
+                       dns.put(baseDn, UserAdminConf.propertiesAsUri(userDirectories.get(userDirectory)).toString());
+
                }
+//             for (String uri : uris) {
+//                     if (!uri.startsWith("/"))
+//                             continue;
+//                     Dictionary<String, ?> props = UserAdminConf.uriAsProperties(uri);
+//                     String readOnly = UserAdminConf.readOnly.getValue(props);
+//                     String baseDn = UserAdminConf.baseDn.getValue(props);
+//
+//                     if (onlyWritable && "true".equals(readOnly))
+//                             continue;
+//                     if (baseDn.equalsIgnoreCase(NodeConstants.ROLES_BASEDN))
+//                             continue;
+//                     if (baseDn.equalsIgnoreCase(NodeConstants.TOKENS_BASEDN))
+//                             continue;
+//                     dns.put(baseDn, uri);
+//             }
                return dns;
        }
 
@@ -118,13 +133,21 @@ public class UserAdminWrapper {
        /* DEPENDENCY INJECTION */
        public void setUserAdmin(UserAdmin userAdmin, Map<String, String> properties) {
                this.userAdmin = userAdmin;
-               this.uris = Collections.unmodifiableSortedSet(new TreeSet<>(properties.keySet()));
+//             this.uris = Collections.unmodifiableSortedSet(new TreeSet<>(properties.keySet()));
        }
 
        public void setUserTransaction(UserTransaction userTransaction) {
                this.userTransaction = userTransaction;
        }
 
+       public void addUserDirectory(UserDirectory userDirectory, Map<String, String> properties) {
+               userDirectories.put(userDirectory, new Hashtable<>(properties));
+       }
+
+       public void removeUserDirectory(UserDirectory userDirectory, Map<String, String> properties) {
+               userDirectories.remove(userDirectory);
+       }
+
        // public void setUserAdminServiceReference(
        // ServiceReference<UserAdmin> userAdminServiceReference) {
        // this.userAdminServiceReference = userAdminServiceReference;
index c176647859c2e03a71db2c718b3a404e5f1a624f..d513be326173a70b6f8781727d6b5578f397e4a0 100644 (file)
@@ -16,4 +16,5 @@ OSGI-INF/dataServletContext.xml,\
 OSGI-INF/filesServletContext.xml,\
 OSGI-INF/filesServlet.xml
 
-Provide-Capability: cms.datamodel;name=argeo;cnd=/org/argeo/cms/argeo.cnd;abstract=true
+Provide-Capability: cms.datamodel;name=argeo;cnd=/org/argeo/cms/argeo.cnd;abstract=true,\
+osgi.service;objectClass="javax.jcr.Repository"
index 7a8071f4a9eb41c073be8d973e8e0983a25e4273..334e43c85a1672e9cd737bd6f9096a3ec78329d3 100644 (file)
@@ -20,7 +20,7 @@ import org.apache.commons.httpclient.auth.MalformedChallengeException;
 import org.apache.commons.httpclient.methods.GetMethod;
 import org.apache.commons.httpclient.params.DefaultHttpParams;
 import org.apache.commons.httpclient.params.HttpParams;
-import org.argeo.cms.internal.kernel.NodeHttp;
+import org.argeo.cms.internal.kernel.KernelConstants;
 import org.ietf.jgss.GSSContext;
 import org.ietf.jgss.GSSException;
 import org.ietf.jgss.GSSManager;
@@ -100,7 +100,7 @@ public class SpnegoAuthScheme implements AuthScheme {
                } catch (URIException e1) {
                        throw new IllegalStateException("Cannot authenticate", e1);
                }
-               String serverPrinc = NodeHttp.DEFAULT_SERVICE + "@" + hostname;
+               String serverPrinc = KernelConstants.DEFAULT_KERBEROS_SERVICE + "@" + hostname;
 
                try {
                        // Get service's principal name
index db5ac27b320ff9e46d6b6d2e265cb5d8d9a5f98f..3efb41591b7e7c749a5e2f029d92a3b8b8dbd00b 100644 (file)
@@ -26,6 +26,9 @@ public interface KernelConstants {
        String DEFAULT_HOME_BASE_PATH = "/home";
        String DEFAULT_USERS_BASE_PATH = "/users";
        String DEFAULT_GROUPS_BASE_PATH = "/groups";
+       
+       // KERBEROS
+       String DEFAULT_KERBEROS_SERVICE = "HTTP";
 
        // HTTP client
        String COOKIE_POLICY_BROWSER_COMPATIBILITY = "compatibility";
index ded4ae10e9cb8b60437e240c2b4e74ff0372fb6a..353a6c9cebc94da979e3f7c8d223f0e4c3994455 100644 (file)
@@ -38,8 +38,6 @@ import org.osgi.util.tracker.ServiceTracker;
 public class NodeHttp implements KernelConstants {
        private final static Log log = LogFactory.getLog(NodeHttp.class);
 
-       public final static String DEFAULT_SERVICE = "HTTP";
-
        private final BundleContext bc = FrameworkUtil.getBundle(getClass()).getBundleContext();
 
        private ServiceTracker<Repository, Repository> repositories;
index cb80f7c7f9f446794604a0b0ecc6b9d5f63cdac4..69affd25a2b160d8c1f154e883a4e11fb6fcc547 100644 (file)
@@ -72,7 +72,7 @@ class NodeUserAdmin extends AggregatingUserAdmin implements ManagedServiceFactor
        // OSGi
        private Map<String, LdapName> pidToBaseDn = new HashMap<>();
        private Map<String, ServiceRegistration<UserDirectory>> pidToServiceRegs = new HashMap<>();
-       private ServiceRegistration<UserAdmin> userAdminReg;
+//     private ServiceRegistration<UserAdmin> userAdminReg;
 
        // JTA
        private final ServiceTracker<TransactionManager, TransactionManager> tmTracker;
@@ -83,7 +83,7 @@ class NodeUserAdmin extends AggregatingUserAdmin implements ManagedServiceFactor
        private GSSCredential acceptorCredentials;
 
        private boolean singleUser = false;
-       private boolean systemRolesAvailable = false;
+//     private boolean systemRolesAvailable = false;
 
        public NodeUserAdmin(String systemRolesBaseDn, String tokensBaseDn) {
                super(systemRolesBaseDn, tokensBaseDn);
@@ -135,21 +135,29 @@ class NodeUserAdmin extends AggregatingUserAdmin implements ManagedServiceFactor
                        log.debug("User directory " + userDirectory.getBaseDn() + " [" + u.getScheme() + "] enabled."
                                        + (realm != null ? " " + realm + " realm." : ""));
 
-               if (isSystemRolesBaseDn(baseDn))
-                       systemRolesAvailable = true;
-
-               // start publishing only when system roles are available
-               if (systemRolesAvailable) {
-                       // The list of baseDns is published as properties
-                       // TODO clients should rather reference USerDirectory services
-                       if (userAdminReg != null)
-                               userAdminReg.unregister();
-                       // register self as main user admin
-                       Dictionary<String, Object> userAdminregProps = currentState();
+               if (isSystemRolesBaseDn(baseDn)) {
+                       // publishes only when system roles are available
+                       Dictionary<String, Object> userAdminregProps = new Hashtable<>();
                        userAdminregProps.put(NodeConstants.CN, NodeConstants.DEFAULT);
                        userAdminregProps.put(Constants.SERVICE_RANKING, Integer.MAX_VALUE);
-                       userAdminReg = bc.registerService(UserAdmin.class, this, userAdminregProps);
+                       bc.registerService(UserAdmin.class, this, userAdminregProps);
                }
+
+//             if (isSystemRolesBaseDn(baseDn))
+//                     systemRolesAvailable = true;
+//
+//             // start publishing only when system roles are available
+//             if (systemRolesAvailable) {
+//                     // The list of baseDns is published as properties
+//                     // TODO clients should rather reference USerDirectory services
+//                     if (userAdminReg != null)
+//                             userAdminReg.unregister();
+//                     // register self as main user admin
+//                     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);
+//             }
        }
 
        @Override
@@ -248,7 +256,7 @@ class NodeUserAdmin extends AggregatingUserAdmin implements ManagedServiceFactor
                        boolean consistentIp = localhost.getHostAddress().equals(ipfromDns);
                        String kerberosDomain = dnsBrowser.getRecord("_kerberos." + dnsZone, "TXT");
                        if (consistentIp && kerberosDomain != null && kerberosDomain.equals(realm) && Files.exists(nodeKeyTab)) {
-                               return NodeHttp.DEFAULT_SERVICE + "/" + hostname + "@" + kerberosDomain;
+                               return KernelConstants.DEFAULT_KERBEROS_SERVICE + "/" + hostname + "@" + kerberosDomain;
                        } else
                                return null;
                } catch (Exception e) {
index 75ca9ae884605a81ac07a219455030e5f5990172..6f353f155c5c620e9c0e24de4e260fb24165c797 100644 (file)
@@ -2,10 +2,8 @@ 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;
@@ -189,16 +187,16 @@ public class AggregatingUserAdmin implements UserAdmin {
                return tokensBaseDn != null && baseDn.equals(tokensBaseDn);
        }
 
-       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;
-       }
+//     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()) {
index bb7345f0b1ca51854434db0aad51348915763fab..28b3e95816d24de925b52b48d85f448cee387e39 100644 (file)
@@ -6,7 +6,6 @@ import java.net.URI;
 import java.net.URISyntaxException;
 import java.net.UnknownHostException;
 import java.util.Dictionary;
-import java.util.Enumeration;
 import java.util.Hashtable;
 import java.util.List;
 import java.util.Map;
@@ -17,7 +16,6 @@ import javax.naming.ldap.LdapName;
 
 import org.argeo.naming.DnsBrowser;
 import org.argeo.naming.NamingUtils;
-import org.osgi.framework.Constants;
 
 /** Properties used to configure user admins. */
 public enum UserAdminConf {
@@ -102,22 +100,39 @@ public enum UserAdminConf {
                StringBuilder query = new StringBuilder();
 
                boolean first = true;
-               for (Enumeration<String> keys = properties.keys(); keys.hasMoreElements();) {
-                       String key = keys.nextElement();
-                       // TODO clarify which keys are relevant (list only the enum?)
-                       if (!key.equals("service.factoryPid") && !key.equals("cn") && !key.equals("dn")
-                                       && !key.equals(Constants.SERVICE_PID) && !key.startsWith("java") && !key.equals(baseDn.name())
-                                       && !key.equals(uri.name())) {
-                               if (first)
-                                       first = false;
-                               else
-                                       query.append('&');
-                               query.append(valueOf(key).name());
-                               query.append('=').append(properties.get(key).toString());
-                       }
+//             for (Enumeration<String> keys = properties.keys(); keys.hasMoreElements();) {
+//                     String key = keys.nextElement();
+//                     // TODO clarify which keys are relevant (list only the enum?)
+//                     if (!key.equals("service.factoryPid") && !key.equals("cn") && !key.equals("dn")
+//                                     && !key.equals(Constants.SERVICE_PID) && !key.startsWith("java") && !key.equals(baseDn.name())
+//                                     && !key.equals(uri.name()) && !key.equals(Constants.OBJECTCLASS)
+//                                     && !key.equals(Constants.SERVICE_ID) && !key.equals("bundle.id")) {
+//                             if (first)
+//                                     first = false;
+//                             else
+//                                     query.append('&');
+//                             query.append(valueOf(key).name());
+//                             query.append('=').append(properties.get(key).toString());
+//                     }
+//             }
+
+               keys: for (UserAdminConf key : UserAdminConf.values()) {
+                       if (key.equals(baseDn))
+                               continue keys;
+                       Object value = properties.get(key.name());
+                       if (value == null)
+                               continue keys;
+                       if (first)
+                               first = false;
+                       else
+                               query.append('&');
+                       query.append(key.name());
+                       query.append('=').append(value.toString());
+
                }
 
-               String bDn = (String) properties.get(baseDn.name());
+               Object bDnObj = properties.get(baseDn.name());
+               String bDn = bDnObj != null ? bDnObj.toString() : null;
                try {
                        return new URI(null, null, bDn != null ? '/' + bDn : null, query.length() != 0 ? query.toString() : null,
                                        null);