From 40c3800ea57d5de136137e3fb0ff07cf54f2df48 Mon Sep 17 00:00:00 2001 From: Mathieu Baudier Date: Wed, 2 Sep 2015 17:48:21 +0000 Subject: [PATCH] - Start factorizing LDIF and LDAP - Add filter for role search git-svn-id: https://svn.argeo.org/commons/trunk@8361 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc --- demo/argeo_node_rap.properties | 2 + demo/log4j.properties | 2 +- .../cms/internal/kernel/NodeSecurity.java | 13 +++-- .../org/argeo/cms/internal/kernel/demo.ldif | 4 +- .../osgi/useradmin/LdifUserAdminTest.java | 12 ++++- .../test/org/argeo/osgi/useradmin/basic.ldif | 6 +-- .../osgi/useradmin/AbstractLdapUserAdmin.java | 52 +++++++++++++++++++ .../argeo/osgi/useradmin/LdapUserAdmin.java | 44 ++++++++++++---- .../argeo/osgi/useradmin/LdifUserAdmin.java | 52 +++++++++---------- 9 files changed, 138 insertions(+), 49 deletions(-) create mode 100644 org.argeo.security.core/src/org/argeo/osgi/useradmin/AbstractLdapUserAdmin.java diff --git a/demo/argeo_node_rap.properties b/demo/argeo_node_rap.properties index 520c5baf4..b6ced4385 100644 --- a/demo/argeo_node_rap.properties +++ b/demo/argeo_node_rap.properties @@ -12,6 +12,8 @@ org.eclipse.gemini.blueprint.extender argeo.osgi.start.4.workbench=\ org.eclipse.equinox.http.registry,\ +#argeo.node.useradmin.uri=ldap://localhost:10389/ + # HTTP org.osgi.service.http.port=7070 org.eclipse.equinox.http.jetty.log.stderr.threshold=info diff --git a/demo/log4j.properties b/demo/log4j.properties index 1238aeebe..da63069e9 100644 --- a/demo/log4j.properties +++ b/demo/log4j.properties @@ -1,7 +1,7 @@ log4j.rootLogger=WARN, development log4j.logger.org.argeo=DEBUG -log4j.logger.org.apache.jackrabbit.core.RepositoryImpl=DEBUG +#log4j.logger.org.apache.jackrabbit.core.RepositoryImpl=DEBUG #log4j.logger.argeo.stats=DEBUG ## Appenders 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 3b5d78d89..5e9877935 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 @@ -1,15 +1,14 @@ package org.argeo.cms.internal.kernel; -import java.net.URL; - import javax.jcr.RepositoryException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.argeo.cms.CmsException; -import org.argeo.cms.internal.useradmin.JcrUserAdmin; import org.argeo.cms.internal.useradmin.SimpleJcrSecurityModel; import org.argeo.cms.internal.useradmin.jackrabbit.JackrabbitUserAdminService; +import org.argeo.osgi.useradmin.AbstractLdapUserAdmin; +import org.argeo.osgi.useradmin.LdapUserAdmin; import org.argeo.osgi.useradmin.LdifUserAdmin; import org.argeo.security.OsAuthenticationToken; import org.argeo.security.UserAdminService; @@ -37,7 +36,7 @@ class NodeSecurity implements AuthenticationManager { private final InternalAuthenticationProvider internalAuth; private final AnonymousAuthenticationProvider anonymousAuth; private final JackrabbitUserAdminService userAdminService; - private final LdifUserAdmin userAdmin; + private final AbstractLdapUserAdmin userAdmin; private ServiceRegistration authenticationManagerReg; private ServiceRegistration userAdminServiceReg; @@ -65,7 +64,11 @@ class NodeSecurity implements AuthenticationManager { .getFrameworkProp(KernelConstants.USERADMIN_URI); if (userAdminUri == null) userAdminUri = getClass().getResource("demo.ldif").toString(); - userAdmin = new LdifUserAdmin(userAdminUri); + + if (userAdminUri.startsWith("ldap")) + userAdmin = new LdapUserAdmin(userAdminUri); + else + userAdmin = new LdifUserAdmin(userAdminUri); } public void publish() { diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/demo.ldif b/org.argeo.cms/src/org/argeo/cms/internal/kernel/demo.ldif index 015ab2dd2..1f172b29c 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/demo.ldif +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/demo.ldif @@ -15,9 +15,9 @@ objectClass: top ou: users dn: uid=demo,ou=users,dc=example,dc=com +objectClass: inetOrgPerson objectClass: organizationalPerson objectClass: person -objectClass: inetOrgPerson objectClass: top cn: Demo User description: Demo user @@ -28,8 +28,8 @@ uid: demo userpassword:: e1NIQX1pZVNWNTVRYytlUU9hWURSU2hhL0Fqek5USkU9 dn: uid=root,ou=users,dc=example,dc=com -objectClass: person objectClass: inetOrgPerson +objectClass: person objectClass: organizationalPerson objectClass: top cn: Super User diff --git a/org.argeo.security.core/ext/test/org/argeo/osgi/useradmin/LdifUserAdminTest.java b/org.argeo.security.core/ext/test/org/argeo/osgi/useradmin/LdifUserAdminTest.java index d2a6c94c1..52682bf03 100644 --- a/org.argeo.security.core/ext/test/org/argeo/osgi/useradmin/LdifUserAdminTest.java +++ b/org.argeo.security.core/ext/test/org/argeo/osgi/useradmin/LdifUserAdminTest.java @@ -14,7 +14,7 @@ import org.osgi.service.useradmin.User; public class LdifUserAdminTest extends TestCase implements BasicTestConstants { - public void testBasicUserAdmin() { + public void testBasicUserAdmin() throws Exception { LdifUserAdmin userAdmin = new LdifUserAdmin(getClass() .getResourceAsStream("basic.ldif")); @@ -54,5 +54,15 @@ public class LdifUserAdminTest extends TestCase implements BasicTestConstants { .getBytes(); assertTrue(rootUser.hasCredential("userpassword", hashedPassword)); assertTrue(demoUser.hasCredential("userpassword", hashedPassword)); + + // search + Role[] search = userAdmin.getRoles(null); + assertEquals(4, search.length); + search = userAdmin.getRoles("(objectClass=groupOfNames)"); + assertEquals(2, search.length); + search = userAdmin.getRoles("(objectclass=inetOrgPerson)"); + assertEquals(2, search.length); + search = userAdmin.getRoles("(&(objectclass=inetOrgPerson)(uid=demo))"); + assertEquals(1, search.length); } } diff --git a/org.argeo.security.core/ext/test/org/argeo/osgi/useradmin/basic.ldif b/org.argeo.security.core/ext/test/org/argeo/osgi/useradmin/basic.ldif index 56c7499b2..5c6565e8a 100644 --- a/org.argeo.security.core/ext/test/org/argeo/osgi/useradmin/basic.ldif +++ b/org.argeo.security.core/ext/test/org/argeo/osgi/useradmin/basic.ldif @@ -15,9 +15,9 @@ objectClass: top ou: People dn: uid=demo,ou=People,dc=demo,dc=example,dc=org +objectClass: inetOrgPerson objectClass: organizationalPerson objectClass: person -objectClass: inetOrgPerson objectClass: top cn: demo User description: Demo user @@ -28,11 +28,11 @@ uid: demo userpassword:: e1NIQX1pZVNWNTVRYytlUU9hWURSU2hhL0Fqek5USkU9 dn: uid=root+cn=Super Admin,ou=People,dc=demo,dc=example,dc=org -objectClass: person objectClass: inetOrgPerson +objectClass: person objectClass: organizationalPerson objectClass: top -cn: demo User +cn: Super Admin description: Superuser givenname: Root mail: root@localhost diff --git a/org.argeo.security.core/src/org/argeo/osgi/useradmin/AbstractLdapUserAdmin.java b/org.argeo.security.core/src/org/argeo/osgi/useradmin/AbstractLdapUserAdmin.java new file mode 100644 index 000000000..df2ad4e8a --- /dev/null +++ b/org.argeo.security.core/src/org/argeo/osgi/useradmin/AbstractLdapUserAdmin.java @@ -0,0 +1,52 @@ +package org.argeo.osgi.useradmin; + +import java.net.URI; +import java.util.Arrays; +import java.util.List; + +import org.osgi.service.useradmin.UserAdmin; + +public abstract class AbstractLdapUserAdmin implements UserAdmin { + private boolean isReadOnly; + private URI uri; + + public AbstractLdapUserAdmin() { + } + + public AbstractLdapUserAdmin(URI uri, boolean isReadOnly) { + this.uri = uri; + this.isReadOnly = isReadOnly; + } + + private List indexedUserProperties = Arrays.asList(new String[] { + "uid", "mail", "cn" }); + + protected URI getUri() { + return uri; + } + + protected void setUri(URI uri) { + this.uri = uri; + } + + protected List getIndexedUserProperties() { + return indexedUserProperties; + } + + protected void setIndexedUserProperties(List indexedUserProperties) { + this.indexedUserProperties = indexedUserProperties; + } + + protected void setReadOnly(boolean isReadOnly) { + this.isReadOnly = isReadOnly; + } + + public boolean isReadOnly() { + return isReadOnly; + } + + public void destroy() { + + } + +} diff --git a/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdapUserAdmin.java b/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdapUserAdmin.java index 0173addbb..dcb639d96 100644 --- a/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdapUserAdmin.java +++ b/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdapUserAdmin.java @@ -1,7 +1,7 @@ package org.argeo.osgi.useradmin; +import java.net.URI; import java.util.ArrayList; -import java.util.Arrays; import java.util.Hashtable; import java.util.List; @@ -24,23 +24,20 @@ import org.osgi.framework.InvalidSyntaxException; import org.osgi.service.useradmin.Authorization; import org.osgi.service.useradmin.Role; import org.osgi.service.useradmin.User; -import org.osgi.service.useradmin.UserAdmin; -public class LdapUserAdmin implements UserAdmin { +public class LdapUserAdmin extends AbstractLdapUserAdmin { private final static Log log = LogFactory.getLog(LdapUserAdmin.class); - private List indexedUserProperties = Arrays.asList(new String[] { - "uid", "mail", "cn" }); - private String baseDn = "dc=example,dc=com"; private InitialLdapContext initialLdapContext = null; public LdapUserAdmin(String uri) { try { + setUri(new URI(uri)); Hashtable connEnv = new Hashtable(); connEnv.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); - connEnv.put(Context.PROVIDER_URL, "ldap://localhost:10389/"); + connEnv.put(Context.PROVIDER_URL, getUri().toString()); connEnv.put("java.naming.ldap.attributes.binary", "userPassword"); // connEnv.put(Context.SECURITY_AUTHENTICATION, "simple"); // connEnv.put(Context.SECURITY_PRINCIPAL, "uid=admin,ou=system"); @@ -106,15 +103,42 @@ public class LdapUserAdmin implements UserAdmin { @Override public Role[] getRoles(String filter) throws InvalidSyntaxException { - // TODO Auto-generated method stub - return null; + try { + String searchFilter = filter; + SearchControls searchControls = new SearchControls(); + searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE); + + String searchBase = baseDn; + NamingEnumeration results = initialLdapContext + .search(searchBase, searchFilter, searchControls); + + ArrayList res = new ArrayList(); + while (results.hasMoreElements()) { + SearchResult searchResult = results.next(); + Attributes attrs = searchResult.getAttributes(); + String name = searchResult.getName(); + LdifUser role; + if (attrs.get("objectClass").contains("groupOfNames")) + role = new LdifGroup(new LdapName(name), attrs); + else if (attrs.get("objectClass").contains("inetOrgPerson")) + role = new LdifUser(new LdapName(name), attrs); + else + throw new ArgeoUserAdminException( + "Unsupported LDAP type for " + name); + res.add(role); + } + return res.toArray(new Role[res.size()]); + } catch (Exception e) { + throw new ArgeoUserAdminException("Cannot get roles for filter " + + filter, e); + } } @Override public User getUser(String key, String value) { if (key == null) { List users = new ArrayList(); - for (String prop : indexedUserProperties) { + for (String prop : getIndexedUserProperties()) { User user = getUser(prop, value); if (user != null) users.add(user); 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 acbf1112f..b1e9ceb49 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 @@ -4,7 +4,6 @@ import java.io.InputStream; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; -import java.util.Arrays; import java.util.Dictionary; import java.util.LinkedHashMap; import java.util.List; @@ -17,22 +16,18 @@ import javax.naming.NamingEnumeration; import javax.naming.directory.Attributes; import javax.naming.ldap.LdapName; +import org.osgi.framework.Filter; +import org.osgi.framework.FrameworkUtil; import org.osgi.framework.InvalidSyntaxException; import org.osgi.service.useradmin.Authorization; import org.osgi.service.useradmin.Role; import org.osgi.service.useradmin.User; -import org.osgi.service.useradmin.UserAdmin; /** User admin implementation using LDIF file(s) as backend. */ -public class LdifUserAdmin implements UserAdmin { +public class LdifUserAdmin extends AbstractLdapUserAdmin { SortedMap users = new TreeMap(); SortedMap groups = new TreeMap(); - private final boolean isReadOnly; - private final URI uri; - - private List indexedUserProperties = Arrays.asList(new String[] { - "uid", "mail", "cn" }); private Map> userIndexes = new LinkedHashMap>(); public LdifUserAdmin(String uri) { @@ -40,28 +35,28 @@ public class LdifUserAdmin implements UserAdmin { } public LdifUserAdmin(String uri, boolean isReadOnly) { - this.isReadOnly = isReadOnly; + setReadOnly(isReadOnly); try { - this.uri = new URI(uri); + setUri(new URI(uri)); } catch (URISyntaxException e) { throw new ArgeoUserAdminException("Invalid URI " + uri, e); } - if (!isReadOnly && !this.uri.getScheme().equals("file:")) - throw new UnsupportedOperationException(this.uri.getScheme() + if (!isReadOnly && !getUri().getScheme().equals("file:")) + throw new UnsupportedOperationException(getUri().getScheme() + "not supported read-write."); try { - load(this.uri.toURL().openStream()); + load(getUri().toURL().openStream()); } catch (Exception e) { - throw new ArgeoUserAdminException("Cannot open URL " + this.uri, e); + throw new ArgeoUserAdminException("Cannot open URL " + getUri(), e); } } public LdifUserAdmin(InputStream in) { load(in); - isReadOnly = true; - this.uri = null; + setReadOnly(true); + setUri(null); } protected void load(InputStream in) { @@ -89,12 +84,12 @@ public class LdifUserAdmin implements UserAdmin { group.loadMembers(this); // indexes - for (String attr : indexedUserProperties) + for (String attr : getIndexedUserProperties()) userIndexes.put(attr, new TreeMap()); for (LdifUser user : users.values()) { Dictionary properties = user.getProperties(); - for (String attr : indexedUserProperties) { + for (String attr : getIndexedUserProperties()) { Object value = properties.get(attr); if (value != null) { LdifUser otherUser = userIndexes.get(attr).put( @@ -102,7 +97,7 @@ public class LdifUserAdmin implements UserAdmin { if (otherUser != null) throw new ArgeoUserAdminException("User " + user + " and user " + otherUser - + " both habe property " + attr + + " both have property " + attr + " set to " + value); } } @@ -155,13 +150,20 @@ public class LdifUserAdmin implements UserAdmin { @Override public Role[] getRoles(String filter) throws InvalidSyntaxException { + ArrayList res = new ArrayList(); if (filter == null) { - ArrayList res = new ArrayList(); res.addAll(users.values()); res.addAll(groups.values()); - return res.toArray(new Role[res.size()]); + } else { + Filter f = FrameworkUtil.createFilter(filter); + for (LdifUser user : users.values()) + if (f.match(user.getProperties())) + res.add(user); + for (LdifUser group : groups.values()) + if (f.match(group.getProperties())) + res.add(group); } - throw new UnsupportedOperationException(); + return res.toArray(new Role[res.size()]); } @Override @@ -175,7 +177,7 @@ public class LdifUserAdmin implements UserAdmin { // Try all indexes List collectedUsers = new ArrayList( - indexedUserProperties.size()); + getIndexedUserProperties().size()); // try dn LdifUser user = null; try { @@ -197,8 +199,4 @@ public class LdifUserAdmin implements UserAdmin { // throw new UnsupportedOperationException(); } - public boolean getIsReadOnly() { - return isReadOnly; - } - } -- 2.30.2