From: Mathieu Baudier Date: Wed, 16 Sep 2015 08:38:39 +0000 (+0000) Subject: Improve properties X-Git-Tag: argeo-commons-2.1.30~145 X-Git-Url: https://git.argeo.org/?a=commitdiff_plain;h=48a1e034607afb28d84480463b57f74a80a29929;p=lgpl%2Fargeo-commons.git Improve properties git-svn-id: https://svn.argeo.org/commons/trunk@8408 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc --- diff --git a/demo/argeo_node_rap.properties b/demo/argeo_node_rap.properties index 2c9d5d842..52eea6a30 100644 --- a/demo/argeo_node_rap.properties +++ b/demo/argeo_node_rap.properties @@ -12,7 +12,12 @@ org.eclipse.gemini.blueprint.extender argeo.osgi.start.4.workbench=\ org.eclipse.equinox.http.registry,\ -#argeo.node.useradmin.uri="\ +org.osgi.framework.security=osgi +java.security.policy=file:../../all.policy + +argeo.node.useradmin.uris=ldap://uid=admin,ou=system:secret@localhost:10389\ +/dc=example,dc=com?userBase=ou=users&groupBase=ou=groups +#argeo.node.useradmin.uris="\ #ldap://uid=admin,ou=system:secret\ #@localhost:10389\ #/dc=example,dc=com\ @@ -20,9 +25,6 @@ org.eclipse.equinox.http.registry,\ #&userObjectClass=inetOrgPerson \ #dc=example,dc=org.ldif" -org.osgi.framework.security=osgi -java.security.policy=file:../../all.policy - # HTTP org.osgi.service.http.port=7070 org.eclipse.equinox.http.jetty.log.stderr.threshold=info 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 e0d237da1..663c2b937 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 @@ -19,7 +19,7 @@ public interface KernelConstants { // Node Security final static String ROLES_URI = "argeo.node.roles.uri"; /** URI to an LDIF file or LDAP server used as initialization or backend */ - final static String USERADMIN_URI = "argeo.node.useradmin.uri"; + final static String USERADMIN_URIS = "argeo.node.useradmin.uris"; final static String[] DEFAULT_CNDS = { "/org/argeo/jcr/argeo.cnd", "/org/argeo/cms/cms.cnd" }; diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeSecurity.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeSecurity.java index aed824fbd..0b6ce9a85 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeSecurity.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeSecurity.java @@ -101,13 +101,11 @@ class NodeSecurity implements AuthenticationManager { } public void publish() { + userAdminReg = bundleContext.registerService(UserAdmin.class, + userAdmin, userAdmin.currentState()); + // dummy auth manager, in order to smooth transition from Argeo 1 authenticationManagerReg = bundleContext.registerService( AuthenticationManager.class, this, null); - Hashtable properties = new Hashtable(); - // properties.put(KernelConstants.USERADMIN_URI, - // userAdmin.asConfigUris()); - userAdminReg = bundleContext.registerService(UserAdmin.class, - userAdmin, properties); } void destroy() { 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 9227eaeb9..7ead08151 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 @@ -9,6 +9,7 @@ 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; @@ -22,8 +23,8 @@ 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.osgi.useradmin.AbstractUserDirectory; -import org.argeo.osgi.useradmin.LdapProperties; +import org.argeo.osgi.useradmin.UserDirectory; +import org.argeo.osgi.useradmin.UserAdminProps; import org.argeo.osgi.useradmin.LdapUserAdmin; import org.argeo.osgi.useradmin.LdifUserAdmin; import org.argeo.osgi.useradmin.UserDirectoryException; @@ -54,7 +55,7 @@ public class NodeUserAdmin implements UserAdmin { nodeBaseDir.mkdirs(); String userAdminUri = KernelUtils - .getFrameworkProp(KernelConstants.USERADMIN_URI); + .getFrameworkProp(KernelConstants.USERADMIN_URIS); if (userAdminUri == null) { String demoBaseDn = "dc=example,dc=com"; File businessRolesFile = new File(nodeBaseDir, demoBaseDn + ".ldif"); @@ -88,16 +89,16 @@ public class NodeUserAdmin implements UserAdmin { throw new CmsException( "Cannot interpret " + uri + " as an uri", e); } - Dictionary properties = LdapProperties.uriAsProperties(u + Dictionary properties = UserAdminProps.uriAsProperties(u .toString()); - AbstractUserDirectory businessRoles; + UserDirectory businessRoles; if (u.getScheme().startsWith("ldap")) { businessRoles = new LdapUserAdmin(properties); } else { businessRoles = new LdifUserAdmin(properties); } businessRoles.init(); - addUserAdmin(businessRoles.getBaseDn(), businessRoles); + addUserAdmin(businessRoles.getBaseDn(), (UserAdmin) businessRoles); if (log.isDebugEnabled()) log.debug("User directory " + businessRoles.getBaseDn() + " [" + u.getScheme() + "] enabled."); @@ -119,14 +120,14 @@ public class NodeUserAdmin implements UserAdmin { nodeRolesUri = nodeRolesFile.toURI().toString(); } - Dictionary nodeRolesProperties = LdapProperties + Dictionary nodeRolesProperties = UserAdminProps .uriAsProperties(nodeRolesUri); - if (!nodeRolesProperties.get(LdapProperties.baseDn.getFullName()) + if (!nodeRolesProperties.get(UserAdminProps.baseDn.getFullName()) .equals(baseNodeRoleDn)) { throw new CmsException("Invalid base dn for node roles"); // TODO deal with "mounted" roles with a different baseDN } - AbstractUserDirectory nodeRoles; + UserDirectory nodeRoles; if (nodeRolesUri.startsWith("ldap")) { nodeRoles = new LdapUserAdmin(nodeRolesProperties); } else { @@ -134,31 +135,33 @@ public class NodeUserAdmin implements UserAdmin { } nodeRoles.setExternalRoles(this); nodeRoles.init(); - addUserAdmin(baseNodeRoleDn, nodeRoles); + addUserAdmin(baseNodeRoleDn, (UserAdmin)nodeRoles); if (log.isTraceEnabled()) log.trace("Node roles enabled."); } - String asConfigUris() { - StringBuilder buf = new StringBuilder(); + Dictionary currentState() { + Dictionary res = new Hashtable(); for (LdapName name : userAdmins.keySet()) { - buf.append('/').append(name.toString()); - if (userAdmins.get(name) instanceof AbstractUserDirectory) { - AbstractUserDirectory userDirectory = (AbstractUserDirectory) userAdmins + StringBuilder buf = new StringBuilder(); + if (userAdmins.get(name) instanceof UserDirectory) { + UserDirectory userDirectory = (UserDirectory) userAdmins .get(name); - if (userDirectory.isReadOnly()) - buf.append('?').append(LdapProperties.readOnly.name()) - .append("=true"); + String uri = UserAdminProps.propertiesAsUri( + userDirectory.getProperties()).toString(); + res.put(uri, ""); + } else { + buf.append('/').append(name.toString()) + .append("?readOnly=true"); } - buf.append(' '); } - return buf.toString(); + return res; } public void destroy() { for (LdapName name : userAdmins.keySet()) { - if (userAdmins.get(name) instanceof AbstractUserDirectory) { - AbstractUserDirectory userDirectory = (AbstractUserDirectory) userAdmins + if (userAdmins.get(name) instanceof UserDirectory) { + UserDirectory userDirectory = (UserDirectory) userAdmins .get(name); userDirectory.destroy(); } @@ -283,12 +286,12 @@ public class NodeUserAdmin implements UserAdmin { } public void setTransactionManager(TransactionManager transactionManager) { - if (nodeRoles instanceof AbstractUserDirectory) - ((AbstractUserDirectory) nodeRoles) + if (nodeRoles instanceof UserDirectory) + ((UserDirectory) nodeRoles) .setTransactionManager(transactionManager); for (UserAdmin userAdmin : userAdmins.values()) { - if (userAdmin instanceof AbstractUserDirectory) - ((AbstractUserDirectory) userAdmin) + if (userAdmin instanceof UserDirectory) + ((UserDirectory) userAdmin) .setTransactionManager(transactionManager); } } diff --git a/org.argeo.security.core/src/org/argeo/osgi/useradmin/AbstractUserDirectory.java b/org.argeo.security.core/src/org/argeo/osgi/useradmin/AbstractUserDirectory.java index f68940002..18cb5ece2 100644 --- a/org.argeo.security.core/src/org/argeo/osgi/useradmin/AbstractUserDirectory.java +++ b/org.argeo.security.core/src/org/argeo/osgi/useradmin/AbstractUserDirectory.java @@ -41,7 +41,7 @@ import org.osgi.service.useradmin.Role; import org.osgi.service.useradmin.User; import org.osgi.service.useradmin.UserAdmin; -public abstract class AbstractUserDirectory implements UserAdmin { +abstract class AbstractUserDirectory implements UserAdmin, UserDirectory { private final static Log log = LogFactory .getLog(AbstractUserDirectory.class); @@ -72,7 +72,7 @@ public abstract class AbstractUserDirectory implements UserAdmin { // TODO make a copy? this.properties = properties; - String uriStr = LdapProperties.uri.getValue(properties); + String uriStr = UserAdminProps.uri.getValue(properties); if (uriStr == null) uri = null; else @@ -82,16 +82,16 @@ public abstract class AbstractUserDirectory implements UserAdmin { throw new UserDirectoryException("Badly formatted URI", e); } - baseDn = LdapProperties.baseDn.getValue(properties).toString(); - String isReadOnly = LdapProperties.readOnly.getValue(properties); + baseDn = UserAdminProps.baseDn.getValue(properties).toString(); + String isReadOnly = UserAdminProps.readOnly.getValue(properties); if (isReadOnly == null) this.isReadOnly = readOnlyDefault(uri); else this.isReadOnly = new Boolean(isReadOnly); - this.userObjectClass = LdapProperties.userObjectClass + this.userObjectClass = UserAdminProps.userObjectClass .getValue(properties); - this.groupObjectClass = LdapProperties.groupObjectClass + this.groupObjectClass = UserAdminProps.groupObjectClass .getValue(properties); } @@ -411,7 +411,7 @@ public abstract class AbstractUserDirectory implements UserAdmin { return groupObjectClass; } - protected Dictionary getProperties() { + public Dictionary getProperties() { return properties; } diff --git a/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdapProperties.java b/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdapProperties.java deleted file mode 100644 index 749c8fe84..000000000 --- a/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdapProperties.java +++ /dev/null @@ -1,148 +0,0 @@ -package org.argeo.osgi.useradmin; - -import java.io.UnsupportedEncodingException; -import java.net.URI; -import java.net.URLDecoder; -import java.util.Dictionary; -import java.util.Hashtable; -import java.util.LinkedHashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - -import javax.naming.Context; - -public enum LdapProperties { - /** Base DN */ - baseDn("dc=example,dc=com"), - - /** URI of the underlying resource */ - uri("ldap://localhost:10389"), - - /** User objectClass */ - userObjectClass("inetOrgPerson"), - - /** Groups objectClass */ - groupObjectClass("groupOfNames"), - - /** Read-only source */ - readOnly(null); - - private static String PREFIX = "argeo.ldap."; - - /** The default value. */ - private Object def; - - LdapProperties(Object def) { - this.def = def; - } - - public Object getDefault() { - return def; - } - - public String getFullName() { - return getPrefix() + name(); - } - - public String getPrefix() { - return PREFIX; - } - - public String getValue(Dictionary properties) { - Object res = getRawValue(properties); - if (res == null) - return null; - return res.toString(); - } - - @SuppressWarnings("unchecked") - public T getRawValue(Dictionary properties) { - Object res = properties.get(getFullName()); - if (res == null) - res = getDefault(); - return (T) res; - } - - public static Dictionary uriAsProperties(String uriStr) { - try { - Hashtable res = new Hashtable(); - URI u = new URI(uriStr); - String scheme = u.getScheme(); - String path = u.getPath(); - String bDn = path.substring(path.lastIndexOf('/') + 1, - path.length()); - String principal = null; - String credentials = null; - if (scheme != null) - if (scheme.equals("ldap") || scheme.equals("ldaps")) { - // TODO additional checks - String[] userInfo = u.getUserInfo().split(":"); - principal = userInfo.length > 0 ? userInfo[0] : null; - credentials = userInfo.length > 1 ? userInfo[1] : null; - } else if (scheme.equals("file")) { - if (bDn.endsWith(".ldif")) { - bDn = bDn.substring(0, bDn.length() - ".ldif".length()); - } - } else - throw new UserDirectoryException("Unsupported scheme " - + scheme); - Map> query = splitQuery(u.getQuery()); - for (String key : query.keySet()) { - LdapProperties ldapProp = LdapProperties.valueOf(key); - List values = query.get(key); - if (values.size() == 1) { - res.put(ldapProp.getFullName(), values.get(0)); - } else { - throw new UserDirectoryException( - "Only single values are supported"); - } - } - res.put(baseDn.getFullName(), bDn); - if (principal != null) - res.put(Context.SECURITY_PRINCIPAL, principal); - if (credentials != null) - res.put(Context.SECURITY_CREDENTIALS, credentials); - if (scheme != null) { - URI bareUri = new URI(scheme, null, u.getHost(), u.getPort(), - scheme.equals("file") ? u.getPath() : null, null, null); - res.put(uri.getFullName(), bareUri.toString()); - } - return res; - } catch (Exception e) { - throw new UserDirectoryException("Cannot convert " + uri - + " to properties", e); - } - } - - public static Map> splitQuery(String query) - throws UnsupportedEncodingException { - final Map> query_pairs = new LinkedHashMap>(); - if (query == null) - return query_pairs; - final String[] pairs = query.split("&"); - for (String pair : pairs) { - final int idx = pair.indexOf("="); - final String key = idx > 0 ? URLDecoder.decode( - pair.substring(0, idx), "UTF-8") : pair; - if (!query_pairs.containsKey(key)) { - query_pairs.put(key, new LinkedList()); - } - final String value = idx > 0 && pair.length() > idx + 1 ? URLDecoder - .decode(pair.substring(idx + 1), "UTF-8") : null; - query_pairs.get(key).add(value); - } - return query_pairs; - } - - public static void main(String[] args) { - System.out.println(uriAsProperties("ldap://" - + "uid=admin,ou=system:secret@localhost:10389" - + "/dc=example,dc=com" - + "?readOnly=false&userObjectClass=person")); - System.out - .println(uriAsProperties("file://some/dir/dc=example,dc=com.ldif")); - System.out - .println(uriAsProperties("/dc=example,dc=com.ldif?readOnly=true")); - } -} diff --git a/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifUserAdmin.java b/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifUserAdmin.java index 750d6a82a..830488589 100644 --- a/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifUserAdmin.java +++ b/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifUserAdmin.java @@ -48,8 +48,8 @@ public class LdifUserAdmin extends AbstractUserDirectory { private static Dictionary fromUri(String uri, String baseDn) { Hashtable res = new Hashtable(); - res.put(LdapProperties.uri.getFullName(), uri); - res.put(LdapProperties.baseDn.getFullName(), baseDn); + res.put(UserAdminProps.uri.getFullName(), uri); + res.put(UserAdminProps.baseDn.getFullName(), baseDn); return res; } diff --git a/org.argeo.security.core/src/org/argeo/osgi/useradmin/UserAdminProps.java b/org.argeo.security.core/src/org/argeo/osgi/useradmin/UserAdminProps.java new file mode 100644 index 000000000..580110d72 --- /dev/null +++ b/org.argeo.security.core/src/org/argeo/osgi/useradmin/UserAdminProps.java @@ -0,0 +1,191 @@ +package org.argeo.osgi.useradmin; + +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URLDecoder; +import java.util.Dictionary; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import javax.naming.Context; + +public enum UserAdminProps { + /** Base DN */ + baseDn("dc=example,dc=com"), + + /** URI of the underlying resource */ + uri("ldap://localhost:10389"), + + /** User objectClass */ + userObjectClass("inetOrgPerson"), + + /** Relative base DN for users */ + userBase("ou=users"), + + /** Groups objectClass */ + groupObjectClass("groupOfNames"), + + /** Relative base DN for users */ + groupBase("ou=groups"), + + /** Read-only source */ + readOnly(null); + + private static String PREFIX = "argeo.useradmin"; + + /** The default value. */ + private Object def; + + UserAdminProps(Object def) { + this.def = def; + } + + public Object getDefault() { + return def; + } + + public String getFullName() { + return getPrefix() + name(); + } + + public String getPrefix() { + return PREFIX; + } + + public String getValue(Dictionary properties) { + Object res = getRawValue(properties); + if (res == null) + return null; + return res.toString(); + } + + @SuppressWarnings("unchecked") + public T getRawValue(Dictionary properties) { + Object res = properties.get(getFullName()); + if (res == null) + res = getDefault(); + return (T) res; + } + + /** Hides host and credentials. */ + public static URI propertiesAsUri(Dictionary properties) { + StringBuilder query = new StringBuilder(); + + boolean first = true; + for (Enumeration keys = properties.keys(); keys + .hasMoreElements();) { + String key = keys.nextElement(); + if (key.startsWith(PREFIX) && !key.equals(baseDn.getFullName()) + && !key.equals(uri.getFullName())) { + if (first) + first = false; + else + query.append('&'); + query.append(key.substring(PREFIX.length())); + query.append('=').append(properties.get(key).toString()); + } + } + + String bDn = (String) properties.get(baseDn.getFullName()); + try { + return new URI(null, null, bDn != null ? '/' + bDn : null, + query.length() != 0 ? query.toString() : null, null); + } catch (URISyntaxException e) { + throw new UserDirectoryException( + "Cannot create URI from properties", e); + } + } + + public static Dictionary uriAsProperties(String uriStr) { + try { + Hashtable res = new Hashtable(); + URI u = new URI(uriStr); + String scheme = u.getScheme(); + String path = u.getPath(); + String bDn = path.substring(path.lastIndexOf('/') + 1, + path.length()); + if (bDn.endsWith(".ldif")) + bDn = bDn.substring(0, bDn.length() - ".ldif".length()); + + String principal = null; + String credentials = null; + if (scheme != null) + if (scheme.equals("ldap") || scheme.equals("ldaps")) { + // TODO additional checks + String[] userInfo = u.getUserInfo().split(":"); + principal = userInfo.length > 0 ? userInfo[0] : null; + credentials = userInfo.length > 1 ? userInfo[1] : null; + } else if (scheme.equals("file")) { + } else + throw new UserDirectoryException("Unsupported scheme " + + scheme); + Map> query = splitQuery(u.getQuery()); + for (String key : query.keySet()) { + UserAdminProps ldapProp = UserAdminProps.valueOf(key); + List values = query.get(key); + if (values.size() == 1) { + res.put(ldapProp.getFullName(), values.get(0)); + } else { + throw new UserDirectoryException( + "Only single values are supported"); + } + } + res.put(baseDn.getFullName(), bDn); + if (principal != null) + res.put(Context.SECURITY_PRINCIPAL, principal); + if (credentials != null) + res.put(Context.SECURITY_CREDENTIALS, credentials); + if (scheme != null) { + URI bareUri = new URI(scheme, null, u.getHost(), u.getPort(), + scheme.equals("file") ? u.getPath() : null, null, null); + res.put(uri.getFullName(), bareUri.toString()); + } + return res; + } catch (Exception e) { + throw new UserDirectoryException("Cannot convert " + uri + + " to properties", e); + } + } + + private static Map> splitQuery(String query) + throws UnsupportedEncodingException { + final Map> query_pairs = new LinkedHashMap>(); + if (query == null) + return query_pairs; + final String[] pairs = query.split("&"); + for (String pair : pairs) { + final int idx = pair.indexOf("="); + final String key = idx > 0 ? URLDecoder.decode( + pair.substring(0, idx), "UTF-8") : pair; + if (!query_pairs.containsKey(key)) { + query_pairs.put(key, new LinkedList()); + } + final String value = idx > 0 && pair.length() > idx + 1 ? URLDecoder + .decode(pair.substring(idx + 1), "UTF-8") : null; + query_pairs.get(key).add(value); + } + return query_pairs; + } + + public static void main(String[] args) { + Dictionary props = uriAsProperties("ldap://" + + "uid=admin,ou=system:secret@localhost:10389" + + "/dc=example,dc=com" + + "?readOnly=false&userObjectClass=person"); + System.out.println(props); + System.out.println(propertiesAsUri(props)); + + System.out + .println(uriAsProperties("file://some/dir/dc=example,dc=com.ldif")); + + props = uriAsProperties("/dc=example,dc=com.ldif?readOnly=true" + + "&userBase=ou=CoWorkers,ou=People&groupBase=ou=Roles"); + System.out.println(props); + System.out.println(propertiesAsUri(props)); + } +} diff --git a/org.argeo.security.core/src/org/argeo/osgi/useradmin/UserDirectory.java b/org.argeo.security.core/src/org/argeo/osgi/useradmin/UserDirectory.java new file mode 100644 index 000000000..b02acd666 --- /dev/null +++ b/org.argeo.security.core/src/org/argeo/osgi/useradmin/UserDirectory.java @@ -0,0 +1,23 @@ +package org.argeo.osgi.useradmin; + +import java.util.Dictionary; + +import javax.transaction.TransactionManager; + +import org.osgi.service.useradmin.UserAdmin; + +/** Information about a user directory. */ +public interface UserDirectory { + public String getBaseDn(); + + public void setExternalRoles(UserAdmin externalRoles); + + public Dictionary getProperties(); + + // Transitional. In the future, more will be managed in OSGi. + public void setTransactionManager(TransactionManager transactionManager); + + public void init(); + + public void destroy(); +}