| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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.jackrabbit.oak.security.authentication.ldap; |
| |
| import static junit.framework.Assert.assertEquals; |
| import static junit.framework.Assert.assertNotNull; |
| import static junit.framework.Assert.assertNull; |
| import static junit.framework.Assert.assertTrue; |
| import static junit.framework.Assert.fail; |
| import static org.junit.Assert.assertArrayEquals; |
| import static org.junit.Assert.assertThat; |
| |
| import java.io.InputStream; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Iterator; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Map; |
| |
| import javax.jcr.SimpleCredentials; |
| import javax.security.auth.login.LoginException; |
| |
| import org.apache.directory.api.util.Strings; |
| import org.apache.directory.server.constants.ServerDNConstants; |
| import org.apache.jackrabbit.oak.security.authentication.ldap.impl.LdapIdentity; |
| import org.apache.jackrabbit.oak.security.authentication.ldap.impl.LdapIdentityProvider; |
| import org.apache.jackrabbit.oak.security.authentication.ldap.impl.LdapProviderConfig; |
| import org.apache.jackrabbit.oak.security.authentication.ldap.impl.LdapUser; |
| import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalGroup; |
| import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalIdentity; |
| import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalIdentityException; |
| import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalIdentityRef; |
| import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalUser; |
| import org.apache.jackrabbit.util.Text; |
| import org.hamcrest.Matchers; |
| import org.junit.After; |
| import org.junit.AfterClass; |
| import org.junit.Before; |
| import org.junit.BeforeClass; |
| import org.junit.Test; |
| |
| public class LdapProviderTest { |
| |
| protected static final InternalLdapServer LDAP_SERVER = new InternalLdapServer(); |
| |
| //initialize LDAP server only once (fast, but might turn out to be not sufficiently flexible in the future) |
| protected static final boolean USE_COMMON_LDAP_FIXTURE = false; |
| |
| private static final String TUTORIAL_LDIF = "apache-ds-tutorial.ldif"; |
| |
| private static final String ERRONEOUS_LDIF = "erroneous.ldif"; |
| |
| public static final String IDP_NAME = "ldap"; |
| |
| protected LdapIdentityProvider idp; |
| |
| protected LdapProviderConfig providerConfig; |
| |
| @BeforeClass |
| public static void beforeClass() throws Exception { |
| if (USE_COMMON_LDAP_FIXTURE) { |
| LDAP_SERVER.setUp(); |
| initLdapFixture(LDAP_SERVER); |
| } |
| } |
| |
| @AfterClass |
| public static void afterClass() throws Exception { |
| if (USE_COMMON_LDAP_FIXTURE) { |
| LDAP_SERVER.tearDown(); |
| } |
| } |
| |
| @Before |
| public void before() throws Exception { |
| if (!USE_COMMON_LDAP_FIXTURE) { |
| LDAP_SERVER.setUp(); |
| initLdapFixture(LDAP_SERVER); |
| } |
| idp = createIDP(); |
| } |
| |
| @After |
| public void after() throws Exception { |
| if (!USE_COMMON_LDAP_FIXTURE) { |
| LDAP_SERVER.tearDown(); |
| } |
| if (idp != null) { |
| idp.close(); |
| idp = null; |
| } |
| } |
| |
| protected LdapIdentityProvider createIDP() { |
| //The attribute "mail" is excluded deliberately |
| return createIDP(new String[] { "objectclass", "uid", "givenname", "description", "sn"}); |
| } |
| |
| protected LdapIdentityProvider createIDP(String[] userProperties) { |
| providerConfig = new LdapProviderConfig() |
| .setName(IDP_NAME) |
| .setHostname("127.0.0.1") |
| .setPort(LDAP_SERVER.getPort()) |
| .setBindDN(ServerDNConstants.ADMIN_SYSTEM_DN) |
| .setBindPassword(InternalLdapServer.ADMIN_PW) |
| .setGroupMemberAttribute("uniquemember") |
| .setCustomAttributes(userProperties); |
| |
| providerConfig.getUserConfig() |
| .setBaseDN(ServerDNConstants.USERS_SYSTEM_DN) |
| .setObjectClasses("inetOrgPerson"); |
| providerConfig.getGroupConfig() |
| .setBaseDN(ServerDNConstants.GROUPS_SYSTEM_DN) |
| .setObjectClasses("groupOfUniqueNames"); |
| |
| providerConfig.getAdminPoolConfig().setMaxActive(0); |
| providerConfig.getUserPoolConfig().setMaxActive(0); |
| return new LdapIdentityProvider(providerConfig); |
| } |
| |
| protected static void initLdapFixture(InternalLdapServer server) throws Exception { |
| InputStream tutorialLDIF = LdapProviderTest.class.getResourceAsStream(TUTORIAL_LDIF); |
| server.loadLdif(tutorialLDIF); |
| } |
| |
| public static final String TEST_USER0_DN = "cn=Rat Ratterson,ou=users,ou=system"; |
| public static final String TEST_USER0_UID = "ratty"; |
| |
| public static final String TEST_USER1_DN = "cn=Horatio Hornblower,ou=users,ou=system"; |
| public static final String TEST_USER1_UID = "hhornblo"; |
| public static final String TEST_USER1_PATH = "cn=Horatio Hornblower/ou=users/ou=system"; |
| |
| public static final String TEST_USER2_DN = "cn=William Bush,ou=users,ou=system"; |
| public static final String TEST_USER3_DN = "cn=Thomas Quist,ou=users,ou=system"; |
| public static final String TEST_USER4_DN = "cn=Moultrie Crystal,ou=users,ou=system"; |
| |
| public static final String TEST_USER5_UID = "=007="; |
| public static final String TEST_USER5_DN = "cn=Special\\, Agent [007],ou=users,ou=system"; |
| public static final String TEST_USER5_PATH = "cn=Special\\, Agent %5B007%5D/ou=users/ou=system"; |
| |
| public static final String TEST_GROUP1_DN = "cn=HMS Lydia,ou=crews,ou=groups,ou=system"; |
| public static final String TEST_GROUP1_NAME = "HMS Lydia"; |
| public static final String[] TEST_GROUP1_MEMBERS = { |
| TEST_USER0_DN, TEST_USER1_DN, TEST_USER2_DN, TEST_USER3_DN, TEST_USER4_DN |
| }; |
| |
| public static final String TEST_GROUP2_DN = "cn=HMS Victory,ou=crews,ou=groups,ou=system"; |
| public static final String TEST_GROUP2_NAME = "HMS Victory"; |
| |
| public static final String TEST_GROUP3_DN = "cn=HMS Bounty,ou=crews,ou=groups,ou=system"; |
| public static final String TEST_GROUP3_NAME = "HMS Bounty"; |
| |
| public static final String[] TEST_USER0_GROUPS = {TEST_GROUP1_DN, TEST_GROUP2_DN, TEST_GROUP3_DN}; |
| public static final String[] TEST_USER1_GROUPS = {TEST_GROUP1_DN}; |
| |
| @Test |
| public void testGetUserByRef() throws Exception { |
| ExternalIdentityRef ref = new ExternalIdentityRef(TEST_USER1_DN, IDP_NAME); |
| ExternalIdentity id = idp.getIdentity(ref); |
| assertTrue("User instance", id instanceof ExternalUser); |
| assertEquals("User ID", TEST_USER1_UID, id.getId()); |
| } |
| |
| /** |
| * Test case to reproduce OAK-3396 where an ldap user entry |
| * without a uid caused a NullpointerException in LdapIdentityProvider.createUser |
| */ |
| @Test |
| public void testListUsersWithMissingUid() throws Exception { |
| // the ERRONEOUS_LDIF contains an entry without uid |
| InputStream erroneousDIF = LdapProviderTest.class.getResourceAsStream(ERRONEOUS_LDIF); |
| LDAP_SERVER.loadLdif(erroneousDIF); |
| Iterator<ExternalUser> users = idp.listUsers(); |
| // without the LdapInvalidAttributeValueException a NPE would result here: |
| while(users.hasNext()) { |
| ExternalUser user = users.next(); |
| // the 'Faulty Entry' of the ERRONEOUS_LDIF should be filtered out |
| // (by LdapIdentityProvider.listUsers.getNext()) |
| assertTrue(!user.getPrincipalName().startsWith("cn=Faulty Entry")); |
| } |
| } |
| |
| @Test |
| public void testGetUserByUserId() throws Exception { |
| ExternalUser user = idp.getUser(TEST_USER1_UID); |
| assertNotNull("User 1 must exist", user); |
| assertEquals("User Ref", TEST_USER1_DN, ((LdapUser)user).getEntry().getDn().getName()); |
| } |
| |
| @Test |
| public void testGetUserProperties() throws Exception { |
| ExternalUser user = idp.getUser(TEST_USER1_UID); |
| assertNotNull("User 1 must exist", user); |
| |
| Map<String, ?> properties = user.getProperties(); |
| assertThat((Map<String, Collection<String>>) properties, |
| Matchers.<String, Collection<String>>hasEntry( |
| Matchers.equalTo("objectclass"), |
| Matchers.containsInAnyOrder( "inetOrgPerson", "top", "person", "organizationalPerson"))); |
| assertThat(properties, Matchers.<String, Object>hasEntry("uid", "hhornblo")); |
| assertThat(properties, Matchers.<String, Object>hasEntry("givenname", "Horatio")); |
| assertThat(properties, Matchers.<String, Object>hasEntry("description", "Capt. Horatio Hornblower, R.N")); |
| assertThat(properties, Matchers.<String, Object>hasEntry("sn", "Hornblower")); |
| |
| assertThat(properties, Matchers.not(Matchers.<String, Object>hasEntry("mail", "hhornblo@royalnavy.mod.uk"))); |
| } |
| |
| private void authenticateInternal(LdapIdentityProvider idp, String id) throws Exception { |
| SimpleCredentials creds = new SimpleCredentials(TEST_USER1_UID, "pass".toCharArray()); |
| ExternalUser user = idp.authenticate(creds); |
| assertNotNull("User 1 must authenticate", user); |
| assertEquals("User Ref", TEST_USER1_DN, ((LdapUser)user).getEntry().getDn().getName()); |
| assertEquals("User Ref", id, user.getExternalId().getId()); |
| } |
| |
| @Test |
| public void testAuthenticate() throws Exception { |
| authenticateInternal(idp, TEST_USER1_DN); |
| |
| providerConfig.setUseUidForExtId(true); |
| idp.close(); |
| idp = new LdapIdentityProvider(providerConfig); |
| authenticateInternal(idp, TEST_USER1_UID); |
| } |
| |
| private void authenticateValidateInternal(LdapIdentityProvider idp, String id) throws Exception { |
| SimpleCredentials creds = new SimpleCredentials(TEST_USER1_UID, "pass".toCharArray()); |
| for (int i=0; i<8; i++) { |
| ExternalUser user = this.idp.authenticate(creds); |
| assertNotNull("User 1 must authenticate (i=" + i + ")", user); |
| assertEquals("User Ref", TEST_USER1_DN, ((LdapUser)user).getEntry().getDn().getName()); |
| assertEquals("User Ref", id, user.getExternalId().getId()); |
| } |
| } |
| |
| @Test |
| public void testAuthenticateValidateFalseFalse() throws Exception { |
| providerConfig.getAdminPoolConfig() |
| .setMaxActive(2) |
| .setLookupOnValidate(false); |
| providerConfig.getUserPoolConfig() |
| .setMaxActive(2) |
| .setLookupOnValidate(false); |
| idp.close(); |
| idp = new LdapIdentityProvider(providerConfig); |
| authenticateValidateInternal(idp, TEST_USER1_DN); |
| |
| providerConfig.setUseUidForExtId(true); |
| idp.close(); |
| idp = new LdapIdentityProvider(providerConfig); |
| authenticateValidateInternal(idp, TEST_USER1_UID); |
| } |
| |
| @Test |
| public void testAuthenticateValidateFalseTrue() throws Exception { |
| providerConfig.getAdminPoolConfig() |
| .setMaxActive(2) |
| .setLookupOnValidate(false); |
| providerConfig.getUserPoolConfig() |
| .setMaxActive(2) |
| .setLookupOnValidate(true); |
| idp.close(); |
| idp = new LdapIdentityProvider(providerConfig); |
| authenticateValidateInternal(idp, TEST_USER1_DN); |
| |
| providerConfig.setUseUidForExtId(true); |
| idp.close(); |
| idp = new LdapIdentityProvider(providerConfig); |
| authenticateValidateInternal(idp, TEST_USER1_UID); |
| } |
| |
| @Test |
| public void testAuthenticateValidateTrueFalse() throws Exception { |
| providerConfig.getAdminPoolConfig() |
| .setMaxActive(2) |
| .setLookupOnValidate(true); |
| providerConfig.getUserPoolConfig() |
| .setMaxActive(2) |
| .setLookupOnValidate(false); |
| idp.close(); |
| idp = new LdapIdentityProvider(providerConfig); |
| authenticateValidateInternal(idp, TEST_USER1_DN); |
| |
| providerConfig.setUseUidForExtId(true); |
| idp.close(); |
| idp = new LdapIdentityProvider(providerConfig); |
| authenticateValidateInternal(idp, TEST_USER1_UID); |
| } |
| |
| @Test |
| public void testAuthenticateValidateTrueTrue() throws Exception { |
| providerConfig.getAdminPoolConfig() |
| .setMaxActive(2) |
| .setLookupOnValidate(true); |
| providerConfig.getUserPoolConfig() |
| .setMaxActive(2) |
| .setLookupOnValidate(true); |
| idp.close(); |
| idp = new LdapIdentityProvider(providerConfig); |
| authenticateValidateInternal(idp, TEST_USER1_DN); |
| |
| providerConfig.setUseUidForExtId(true); |
| idp.close(); |
| idp = new LdapIdentityProvider(providerConfig); |
| authenticateValidateInternal(idp, TEST_USER1_UID); |
| } |
| |
| @Test |
| public void testAuthenticateCaseInsensitive() throws Exception { |
| SimpleCredentials creds = new SimpleCredentials(TEST_USER1_UID.toUpperCase(), "pass".toCharArray()); |
| ExternalUser user = idp.authenticate(creds); |
| assertNotNull("User 1 must authenticate", user); |
| assertEquals("User Ref", TEST_USER1_DN, ((LdapUser)user).getEntry().getDn().getName()); |
| assertEquals("User Ref", TEST_USER1_DN, user.getExternalId().getId()); |
| |
| providerConfig.setUseUidForExtId(true); |
| idp.close(); |
| idp = new LdapIdentityProvider(providerConfig); |
| user = idp.authenticate(creds); |
| assertNotNull("User 1 must authenticate", user); |
| assertEquals("User Ref", TEST_USER1_DN, ((LdapUser)user).getEntry().getDn().getName()); |
| assertEquals("User Ref", TEST_USER1_UID.toUpperCase(), user.getExternalId().getId()); |
| } |
| |
| @Test |
| public void testAuthenticateFail() throws Exception { |
| SimpleCredentials creds = new SimpleCredentials(TEST_USER1_UID, "foobar".toCharArray()); |
| try { |
| idp.authenticate(creds); |
| fail("Authenticate must fail with LoginException for wrong password"); |
| } catch (LoginException e) { |
| // ok |
| } |
| } |
| |
| @Test |
| public void testAuthenticateMissing() throws Exception { |
| SimpleCredentials creds = new SimpleCredentials("foobar" + TEST_USER1_UID, "pass".toCharArray()); |
| ExternalUser user = idp.authenticate(creds); |
| assertNull("Authenticate must return NULL for unknown user", user); |
| } |
| |
| @Test |
| public void testGetUserByForeignRef() throws Exception { |
| ExternalIdentityRef ref = new ExternalIdentityRef(TEST_USER1_DN, "foobar"); |
| ExternalIdentity id = idp.getIdentity(ref); |
| assertNull("Foreign ref must be null", id); |
| } |
| |
| @Test |
| public void testGetUnknownUserByRef() throws Exception { |
| ExternalIdentityRef ref = new ExternalIdentityRef("bla=foo," + TEST_USER1_DN, IDP_NAME); |
| ExternalIdentity id = idp.getIdentity(ref); |
| assertNull("Unknown user must return null", id); |
| } |
| |
| @Test |
| public void testGetGroupByRef() throws Exception { |
| ExternalIdentityRef ref = new ExternalIdentityRef(TEST_GROUP1_DN, IDP_NAME); |
| ExternalIdentity id = idp.getIdentity(ref); |
| assertTrue("Group instance", id instanceof ExternalGroup); |
| assertEquals("Group Name", TEST_GROUP1_NAME, id.getId()); |
| } |
| |
| @Test |
| public void testGetGroupByName() throws Exception { |
| ExternalGroup group = idp.getGroup(TEST_GROUP1_NAME); |
| assertNotNull("Group 1 must exist", group); |
| assertEquals("Group Ref", TEST_GROUP1_DN, ((LdapIdentity)group).getEntry().getDn().getName()); |
| } |
| |
| @Test |
| public void testGetMembers() throws Exception { |
| ExternalIdentityRef ref = new ExternalIdentityRef(TEST_GROUP1_DN, IDP_NAME); |
| ExternalIdentity id = idp.getIdentity(ref); |
| assertTrue("Group instance", id instanceof ExternalGroup); |
| |
| ExternalGroup grp = (ExternalGroup) id; |
| assertIfEquals("Group members", TEST_GROUP1_MEMBERS, grp.getDeclaredMembers()); |
| } |
| |
| @Test |
| public void testGetGroups() throws Exception { |
| ExternalIdentityRef ref = new ExternalIdentityRef(TEST_USER1_DN, IDP_NAME); |
| ExternalIdentity id = idp.getIdentity(ref); |
| assertTrue("User instance", id instanceof ExternalUser); |
| assertIfEquals("Groups", TEST_USER1_GROUPS, id.getDeclaredGroups()); |
| } |
| |
| @Test |
| public void testGetGroups2() throws Exception { |
| ExternalIdentityRef ref = new ExternalIdentityRef(TEST_USER0_DN, IDP_NAME); |
| ExternalIdentity id = idp.getIdentity(ref); |
| assertTrue("User instance", id instanceof ExternalUser); |
| assertIfEquals("Groups", TEST_USER0_GROUPS, id.getDeclaredGroups()); |
| } |
| |
| @Test |
| public void testNullIntermediatePath() throws Exception { |
| providerConfig.getUserConfig().setMakeDnPath(false); |
| ExternalUser user = idp.getUser(TEST_USER1_UID); |
| assertNotNull("User 1 must exist", user); |
| assertNull("Intermediate path must be null", user.getIntermediatePath()); |
| } |
| |
| @Test |
| public void testSplitDNIntermediatePath() throws Exception { |
| providerConfig.getUserConfig().setMakeDnPath(true); |
| ExternalUser user = idp.getUser(TEST_USER1_UID); |
| assertNotNull("User 1 must exist", user); |
| assertEquals("Intermediate path must be the split dn", TEST_USER1_PATH, user.getIntermediatePath()); |
| } |
| |
| @Test |
| public void testSplitDNIntermediatePath2() throws Exception { |
| providerConfig.getUserConfig().setMakeDnPath(true); |
| ExternalUser user = idp.getUser(TEST_USER5_UID); |
| assertNotNull("User 5 must exist", user); |
| assertEquals("Intermediate path must be the split dn", TEST_USER5_PATH, user.getIntermediatePath()); |
| } |
| |
| @Test |
| public void testRemoveEmptyString() throws Exception { |
| providerConfig.setCustomAttributes(new String[] {"a", Strings.EMPTY_STRING, "b" }); |
| assertArrayEquals("Array must not contain empty strings", new String[] {"a", "b" }, providerConfig.getCustomAttributes()); |
| } |
| |
| @Test |
| public void testResolvePrincipalNameUser() throws ExternalIdentityException { |
| ExternalUser user = idp.getUser(TEST_USER5_UID); |
| assertNotNull(user); |
| assertEquals(user.getPrincipalName(), idp.fromExternalIdentityRef(user.getExternalId())); |
| } |
| |
| @Test |
| public void testResolvePrincipalNameGroup() throws ExternalIdentityException { |
| ExternalGroup gr = idp.getGroup(TEST_GROUP1_NAME); |
| assertNotNull(gr); |
| |
| assertEquals(gr.getPrincipalName(), idp.fromExternalIdentityRef(gr.getExternalId())); |
| } |
| |
| @Test(expected = ExternalIdentityException.class) |
| public void testResolvePrincipalNameForeignExtId() throws Exception { |
| idp.fromExternalIdentityRef(new ExternalIdentityRef("anyId", "anotherProviderName")); |
| } |
| |
| public static void assertIfEquals(String message, String[] expected, Iterable<ExternalIdentityRef> result) { |
| List<String> dns = new LinkedList<String>(); |
| for (ExternalIdentityRef ref: result) { |
| dns.add(ref.getId()); |
| } |
| Collections.sort(dns); |
| Arrays.sort(expected); |
| String exp = Text.implode(expected, ",\n"); |
| String res = Text.implode(dns.toArray(new String[dns.size()]), ",\n"); |
| assertEquals(message, exp, res); |
| } |
| |
| } |