From 2b6597a096ac21375aa83f86d1f750061b20387c Mon Sep 17 00:00:00 2001 From: Mathieu Baudier Date: Thu, 11 Jun 2020 11:14:37 +0200 Subject: [PATCH] Simplify node user admin APIs. --- .../OSGI-INF/userAdminWrapper.xml | 1 + .../argeo/cms/e4/users/UserAdminWrapper.java | 49 ++++++++++++++----- org.argeo.cms/bnd.bnd | 3 +- .../http/client/SpnegoAuthScheme.java | 4 +- .../cms/internal/kernel/KernelConstants.java | 3 ++ .../argeo/cms/internal/kernel/NodeHttp.java | 2 - .../cms/internal/kernel/NodeUserAdmin.java | 38 ++++++++------ .../osgi/useradmin/AggregatingUserAdmin.java | 22 ++++----- .../argeo/osgi/useradmin/UserAdminConf.java | 47 ++++++++++++------ 9 files changed, 108 insertions(+), 61 deletions(-) diff --git a/org.argeo.cms.e4/OSGI-INF/userAdminWrapper.xml b/org.argeo.cms.e4/OSGI-INF/userAdminWrapper.xml index 4b9d021ea..6fa13d402 100644 --- a/org.argeo.cms.e4/OSGI-INF/userAdminWrapper.xml +++ b/org.argeo.cms.e4/OSGI-INF/userAdminWrapper.xml @@ -6,4 +6,5 @@ + diff --git a/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserAdminWrapper.java b/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserAdminWrapper.java index 951dd2415..60d232aba 100644 --- a/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserAdminWrapper.java +++ b/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserAdminWrapper.java @@ -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 userAdminServiceReference; - private Set uris; +// private Set uris; + private Map> 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 getKnownBaseDns(boolean onlyWritable) { Map dns = new HashMap(); - for (String uri : uris) { - if (!uri.startsWith("/")) - continue; - Dictionary 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 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 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 properties) { + userDirectories.put(userDirectory, new Hashtable<>(properties)); + } + + public void removeUserDirectory(UserDirectory userDirectory, Map properties) { + userDirectories.remove(userDirectory); + } + // public void setUserAdminServiceReference( // ServiceReference userAdminServiceReference) { // this.userAdminServiceReference = userAdminServiceReference; diff --git a/org.argeo.cms/bnd.bnd b/org.argeo.cms/bnd.bnd index c17664785..d513be326 100644 --- a/org.argeo.cms/bnd.bnd +++ b/org.argeo.cms/bnd.bnd @@ -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" diff --git a/org.argeo.cms/src/org/argeo/cms/internal/http/client/SpnegoAuthScheme.java b/org.argeo.cms/src/org/argeo/cms/internal/http/client/SpnegoAuthScheme.java index 7a8071f4a..334e43c85 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/http/client/SpnegoAuthScheme.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/http/client/SpnegoAuthScheme.java @@ -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 diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelConstants.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelConstants.java index db5ac27b3..3efb41591 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelConstants.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelConstants.java @@ -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"; diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeHttp.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeHttp.java index ded4ae10e..353a6c9ce 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeHttp.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeHttp.java @@ -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 repositories; 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 index cb80f7c7f..69affd25a 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeUserAdmin.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeUserAdmin.java @@ -72,7 +72,7 @@ class NodeUserAdmin extends AggregatingUserAdmin implements ManagedServiceFactor // OSGi private Map pidToBaseDn = new HashMap<>(); private Map> pidToServiceRegs = new HashMap<>(); - private ServiceRegistration userAdminReg; +// private ServiceRegistration userAdminReg; // JTA private final ServiceTracker 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 userAdminregProps = currentState(); + if (isSystemRolesBaseDn(baseDn)) { + // publishes only when system roles are available + Dictionary 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 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) { diff --git a/org.argeo.enterprise/src/org/argeo/osgi/useradmin/AggregatingUserAdmin.java b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/AggregatingUserAdmin.java index 75ca9ae88..6f353f155 100644 --- a/org.argeo.enterprise/src/org/argeo/osgi/useradmin/AggregatingUserAdmin.java +++ b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/AggregatingUserAdmin.java @@ -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 currentState() { - Dictionary res = new Hashtable(); - // 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 currentState() { +// Dictionary res = new Hashtable(); +// // 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()) { diff --git a/org.argeo.enterprise/src/org/argeo/osgi/useradmin/UserAdminConf.java b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/UserAdminConf.java index bb7345f0b..28b3e9581 100644 --- a/org.argeo.enterprise/src/org/argeo/osgi/useradmin/UserAdminConf.java +++ b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/UserAdminConf.java @@ -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 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 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); -- 2.30.2