diff --git a/activemq-core/src/main/java/org/apache/activemq/security/LDAPAuthorizationMap.java b/activemq-core/src/main/java/org/apache/activemq/security/LDAPAuthorizationMap.java index 21be7a275e..011c2c1033 100755 --- a/activemq-core/src/main/java/org/apache/activemq/security/LDAPAuthorizationMap.java +++ b/activemq-core/src/main/java/org/apache/activemq/security/LDAPAuthorizationMap.java @@ -32,6 +32,8 @@ import javax.naming.directory.DirContext; import javax.naming.directory.InitialDirContext; import javax.naming.directory.SearchControls; import javax.naming.directory.SearchResult; +import javax.naming.ldap.LdapName; +import javax.naming.ldap.Rdn; import org.apache.activemq.advisory.AdvisorySupport; import org.apache.activemq.command.ActiveMQDestination; @@ -388,7 +390,7 @@ public class LDAPAuthorizationMap implements AuthorizationMap { String destinationBase = ""; SearchControls constraints = new SearchControls(); if (AdvisorySupport.isAdvisoryTopic(destination) && useAdvisorySearchBase) { - destinationBase = advisorySearchBase; + destinationBase = advisorySearchBase; } else { if ((destination.getDestinationType() & ActiveMQDestination.QUEUE_TYPE) == ActiveMQDestination.QUEUE_TYPE) { destinationBase = queueSearchMatchingFormat.format(new String[]{destination.getPhysicalName()}); @@ -428,8 +430,10 @@ public class LDAPAuthorizationMap implements AuthorizationMap { } for (Iterator iter = acls.iterator(); iter.hasNext();) { String roleName = iter.next(); - String[] components = roleName.split("=", 2); - roles.add(new GroupPrincipal(components[components.length - 1])); + LdapName ldapname = new LdapName(roleName); + Rdn rdn = ldapname.getRdn(ldapname.size() - 1); + LOG.debug("Found role: [" + rdn.getValue().toString() + "]"); + roles.add(new GroupPrincipal(rdn.getValue().toString())); } return roles; } catch (NamingException e) { @@ -483,4 +487,4 @@ public class LDAPAuthorizationMap implements AuthorizationMap { return context; } -} +} \ No newline at end of file diff --git a/activemq-jaas/src/main/java/org/apache/activemq/jaas/LDAPLoginModule.java b/activemq-jaas/src/main/java/org/apache/activemq/jaas/LDAPLoginModule.java index eedabe562e..4bced65780 100644 --- a/activemq-jaas/src/main/java/org/apache/activemq/jaas/LDAPLoginModule.java +++ b/activemq-jaas/src/main/java/org/apache/activemq/jaas/LDAPLoginModule.java @@ -19,20 +19,9 @@ package org.apache.activemq.jaas; import java.io.IOException; import java.security.Principal; import java.text.MessageFormat; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.Hashtable; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; -import javax.naming.AuthenticationException; -import javax.naming.CommunicationException; -import javax.naming.Context; -import javax.naming.Name; -import javax.naming.NameParser; -import javax.naming.NamingEnumeration; -import javax.naming.NamingException; +import javax.naming.*; import javax.naming.directory.Attribute; import javax.naming.directory.Attributes; import javax.naming.directory.DirContext; @@ -71,6 +60,8 @@ public class LDAPLoginModule implements LoginModule { private static final String ROLE_SEARCH_MATCHING = "roleSearchMatching"; private static final String ROLE_SEARCH_SUBTREE = "roleSearchSubtree"; private static final String USER_ROLE_NAME = "userRoleName"; + private static final String EXPAND_ROLES = "expandRoles"; + private static final String EXPAND_ROLES_MATCHING = "expandRolesMatching"; private static Logger log = LoggerFactory.getLogger(LDAPLoginModule.class); @@ -102,7 +93,10 @@ public class LDAPLoginModule implements LoginModule { new LDAPLoginProperty (ROLE_SEARCH_MATCHING, (String)options.get(ROLE_SEARCH_MATCHING)), new LDAPLoginProperty (ROLE_SEARCH_SUBTREE, (String)options.get(ROLE_SEARCH_SUBTREE)), new LDAPLoginProperty (USER_ROLE_NAME, (String)options.get(USER_ROLE_NAME)), - }; + new LDAPLoginProperty (EXPAND_ROLES, (String) options.get(EXPAND_ROLES)), + new LDAPLoginProperty (EXPAND_ROLES_MATCHING, (String) options.get(EXPAND_ROLES_MATCHING)), + + }; } @Override @@ -281,8 +275,10 @@ public class LDAPLoginModule implements LoginModule { List list = currentRoles; MessageFormat roleSearchMatchingFormat; boolean roleSearchSubtreeBool; + boolean expandRolesBool; roleSearchMatchingFormat = new MessageFormat(getLDAPPropertyValue(ROLE_SEARCH_MATCHING)); roleSearchSubtreeBool = Boolean.valueOf(getLDAPPropertyValue(ROLE_SEARCH_SUBTREE)).booleanValue(); + expandRolesBool = Boolean.valueOf(getLDAPPropertyValue(EXPAND_ROLES)).booleanValue(); if (list == null) { list = new ArrayList(); @@ -306,17 +302,40 @@ public class LDAPLoginModule implements LoginModule { log.debug(" base DN: " + getLDAPPropertyValue(ROLE_BASE)); log.debug(" filter: " + filter); } + HashSet haveSeenNames = new HashSet(); + Queue pendingNameExpansion = new LinkedList(); NamingEnumeration results = context.search(getLDAPPropertyValue(ROLE_BASE), filter, constraints); while (results.hasMore()) { SearchResult result = results.next(); Attributes attrs = result.getAttributes(); + if (expandRolesBool) { + haveSeenNames.add(result.getNameInNamespace()); + pendingNameExpansion.add(result.getNameInNamespace()); + } if (attrs == null) { continue; } list = addAttributeValues(getLDAPPropertyValue(ROLE_NAME), attrs, list); } + if (expandRolesBool) { + MessageFormat expandRolesMatchingFormat = new MessageFormat(getLDAPPropertyValue(EXPAND_ROLES_MATCHING)); + while (!pendingNameExpansion.isEmpty()) { + String name = pendingNameExpansion.remove(); + filter = expandRolesMatchingFormat.format(new String[]{name}); + results = context.search(getLDAPPropertyValue(ROLE_BASE), filter, constraints); + while (results.hasMore()) { + SearchResult result = results.next(); + name = result.getNameInNamespace(); + if (!haveSeenNames.contains(name)) { + Attributes attrs = result.getAttributes(); + list = addAttributeValues(getLDAPPropertyValue(ROLE_NAME), attrs, list); + haveSeenNames.add(name); + pendingNameExpansion.add(name); + } + } + } + } return list; - } protected String doRFC2254Encoding(String inputString) { diff --git a/activemq-jaas/src/test/java/org/apache/activemq/jaas/LDAPModuleRoleExpansionTest.java b/activemq-jaas/src/test/java/org/apache/activemq/jaas/LDAPModuleRoleExpansionTest.java new file mode 100644 index 0000000000..b89d3085b8 --- /dev/null +++ b/activemq-jaas/src/test/java/org/apache/activemq/jaas/LDAPModuleRoleExpansionTest.java @@ -0,0 +1,135 @@ +/* + * Copyright 2012 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.activemq.jaas; + +import org.apache.directory.server.core.integ.AbstractLdapTestUnit; +import org.apache.directory.server.core.integ.FrameworkRunner; +import org.apache.directory.server.integ.ServerIntegrationUtils; +import org.apache.directory.server.ldap.LdapServer; +import org.apache.directory.server.annotations.CreateLdapServer; +import org.apache.directory.server.annotations.CreateTransport; +import org.apache.directory.server.core.annotations.ApplyLdifFiles; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import javax.naming.Context; +import javax.naming.NameClassPair; +import javax.naming.NamingEnumeration; +import javax.naming.directory.DirContext; +import javax.naming.directory.InitialDirContext; +import javax.security.auth.callback.*; +import javax.security.auth.login.LoginContext; +import javax.security.auth.login.LoginException; +import java.io.IOException; +import java.net.URL; +import java.security.Principal; +import java.util.HashSet; +import java.util.Hashtable; +import javax.security.auth.Subject; + +import static org.junit.Assert.assertTrue; + +@RunWith(FrameworkRunner.class) +@CreateLdapServer(transports = { + @CreateTransport(protocol = "LDAP", port = 1024)}) +@ApplyLdifFiles("test.ldif") +public class LDAPModuleRoleExpansionTest extends AbstractLdapTestUnit { + + public static LdapServer ldapServer; + private static final String PRINCIPAL = "uid=admin,ou=system"; + private static final String CREDENTIALS = "secret"; + private final String loginConfigSysPropName = "java.security.auth.login.config"; + private String oldLoginConfig; + + @Before + public void setLoginConfigSysProperty() { + oldLoginConfig = System.getProperty(loginConfigSysPropName, null); + System.setProperty(loginConfigSysPropName, "src/test/resources/login.config"); + } + + @After + public void resetLoginConfigSysProperty() { + if (oldLoginConfig != null) { + System.setProperty(loginConfigSysPropName, oldLoginConfig); + } + } + + @SuppressWarnings("unchecked") + @Test + public void testRunning() throws Exception { + + Hashtable env = new Hashtable(); + env.put(Context.PROVIDER_URL, "ldap://localhost:1024"); + env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); + env.put(Context.SECURITY_AUTHENTICATION, "simple"); + env.put(Context.SECURITY_PRINCIPAL, PRINCIPAL); + env.put(Context.SECURITY_CREDENTIALS, CREDENTIALS); + DirContext ctx = new InitialDirContext(env); + + HashSet set = new HashSet(); + + NamingEnumeration list = ctx.list("ou=system"); + + while (list.hasMore()) { + NameClassPair ncp = (NameClassPair) list.next(); + set.add(ncp.getName()); + } + + assertTrue(set.contains("uid=admin")); + assertTrue(set.contains("ou=users")); + assertTrue(set.contains("ou=groups")); + assertTrue(set.contains("ou=configuration")); + assertTrue(set.contains("prefNodeName=sysPrefRoot")); + + } + + @Test + public void testRoleExpansion() throws LoginException { + LoginContext context = new LoginContext("ExpandedLDAPLogin", new CallbackHandler() { + public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { + for (int i = 0; i < callbacks.length; i++) { + if (callbacks[i] instanceof NameCallback) { + ((NameCallback) callbacks[i]).setName("first"); + } else if (callbacks[i] instanceof PasswordCallback) { + ((PasswordCallback) callbacks[i]).setPassword("secret".toCharArray()); + } else { + throw new UnsupportedCallbackException(callbacks[i]); + } + } + } + }); + context.login(); + Subject subject = context.getSubject(); + boolean isAdmin = false; + boolean isUser = false; + for(Principal principal : subject.getPrincipals()) + { + if (principal instanceof GroupPrincipal) + { + GroupPrincipal groupPrincipal = (GroupPrincipal)principal; + if (groupPrincipal.getName().equalsIgnoreCase("admins")) + isAdmin = true; + if (groupPrincipal.getName().equalsIgnoreCase("users")) + isUser = true; + } + } + // Should be in users by virtue of being in admins + assertTrue(isAdmin && isUser); + context.logout(); + } +} diff --git a/activemq-jaas/src/test/resources/login.config b/activemq-jaas/src/test/resources/login.config index b588c25bed..6835c148cc 100644 --- a/activemq-jaas/src/test/resources/login.config +++ b/activemq-jaas/src/test/resources/login.config @@ -40,6 +40,27 @@ LDAPLogin { ; }; +ExpandedLDAPLogin { + org.apache.activemq.jaas.LDAPLoginModule required + debug=true + initialContextFactory=com.sun.jndi.ldap.LdapCtxFactory + connectionURL="ldap://localhost:1024" + connectionUsername="uid=admin,ou=system" + connectionPassword=secret + connectionProtocol=s + authentication=simple + userBase="ou=system" + userSearchMatching="(uid={0})" + userSearchSubtree=false + roleBase="ou=system" + roleName=cn + roleSearchMatching="(uid={1})" + roleSearchSubtree=false + expandRoles=true + expandRolesMatching="(member={0})" + ; +}; + GuestLogin { org.apache.activemq.jaas.GuestLoginModule required debug=true @@ -73,6 +94,6 @@ OpenLdapConfiguration { roleBase="ou=Group,ou=ActiveMQ,ou=system,dc=fusesource,dc=com" roleName=cn roleSearchMatching="(member:=uid={1})" - roleSearchSubtree=tru + roleSearchSubtree=true ; }; diff --git a/activemq-jaas/src/test/resources/test.ldif b/activemq-jaas/src/test/resources/test.ldif index 5aa1596e14..6d6bd588ce 100644 --- a/activemq-jaas/src/test/resources/test.ldif +++ b/activemq-jaas/src/test/resources/test.ldif @@ -20,4 +20,20 @@ uid: first userPassword: secret objectClass: account objectClass: simpleSecurityObject +objectClass: top + +################### +## Define groups ## +################### + +dn: cn=admins,ou=system +cn: admins +member: uid=first,ou=system +objectClass: groupOfNames +objectClass: top + +dn: cn=users,ou=system +cn: users +member: cn=admins,ou=system +objectClass: groupOfNames objectClass: top \ No newline at end of file