/** * This file is part of lavagna. * * lavagna is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * lavagna is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with lavagna. If not, see <http://www.gnu.org/licenses/>. */ package io.lavagna.service; import io.lavagna.model.Key; import io.lavagna.service.LdapConnection.InitialDirContextCloseable; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; import javax.naming.NamingEnumeration; import javax.naming.NamingException; import javax.naming.directory.SearchControls; import javax.naming.directory.SearchResult; import java.util.EnumMap; import java.util.EnumSet; import java.util.Map; import static org.mockito.Matchers.any; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.*; @RunWith(MockitoJUnitRunner.class) public class LdapTest { private static final String MANAGER_PWD = "secret"; private static final String MANAGER_DN = "uid=admin,ou=system"; private static final String PROVIDER_URL = "ldap://localhost:10389"; @Mock private ConfigurationRepository configurationRepository; @Mock private LdapConnection ldapConnection; private Ldap ldap; @Before public void prepare() { ldap = new Ldap(configurationRepository, ldapConnection); Map<Key, String> conf = new EnumMap<>(Key.class); conf.put(Key.LDAP_SERVER_URL, PROVIDER_URL); conf.put(Key.LDAP_MANAGER_DN, MANAGER_DN); conf.put(Key.LDAP_MANAGER_PASSWORD, MANAGER_PWD); conf.put(Key.LDAP_USER_SEARCH_BASE, "ou=system"); conf.put(Key.LDAP_USER_SEARCH_FILTER, "uid={0}"); when( configurationRepository.findConfigurationFor(EnumSet.of(Key.LDAP_SERVER_URL, Key.LDAP_MANAGER_DN, Key.LDAP_MANAGER_PASSWORD, Key.LDAP_USER_SEARCH_BASE, Key.LDAP_USER_SEARCH_FILTER))) .thenReturn(conf); } @Test public void failOnFirstOpen() throws NamingException { Throwable throwable = new NamingException("unit test :D"); when(ldapConnection.context(PROVIDER_URL, MANAGER_DN, MANAGER_PWD)).thenThrow(throwable); Assert.assertFalse(ldap.authenticate("user", "password")); verify(ldapConnection).context(PROVIDER_URL, MANAGER_DN, MANAGER_PWD); } @SuppressWarnings("unchecked") @Test public void nothingFound() throws NamingException { InitialDirContextCloseable ctx = mock(InitialDirContextCloseable.class); when(ldapConnection.context(PROVIDER_URL, MANAGER_DN, MANAGER_PWD)).thenReturn(ctx); NamingEnumeration<SearchResult> searchRes = mock(NamingEnumeration.class); when(ctx.search(eq("ou=system"), eq("uid=user"), any(SearchControls.class))).thenReturn(searchRes); when(searchRes.hasMore()).thenReturn(false); Assert.assertFalse(ldap.authenticate("user", "password")); // first call verify(ldapConnection).context(PROVIDER_URL, MANAGER_DN, MANAGER_PWD); } @SuppressWarnings("unchecked") @Test public void wrongPassword() throws NamingException { InitialDirContextCloseable ctx = mock(InitialDirContextCloseable.class); when(ldapConnection.context(PROVIDER_URL, MANAGER_DN, MANAGER_PWD)).thenReturn(ctx); NamingEnumeration<SearchResult> searchRes = mock(NamingEnumeration.class); when(ctx.search(eq("ou=system"), eq("uid=user\\5c\\2a\\28\\29\\00"), any(SearchControls.class))).thenReturn( searchRes); when(searchRes.hasMore()).thenReturn(true, false); SearchResult sr = mock(SearchResult.class); when(sr.getNameInNamespace()).thenReturn("HOLOYOLO"); when(searchRes.next()).thenReturn(sr); Throwable throwable = new NamingException("unit test :D"); when(ldapConnection.context(PROVIDER_URL, "HOLOYOLO", "password")).thenThrow(throwable); // we check the escape too.. Assert.assertFalse(ldap.authenticate("user\\*()\u0000", "password")); verify(ctx).search(eq("ou=system"), eq("uid=user\\5c\\2a\\28\\29\\00"), any(SearchControls.class)); verify(sr).getNameInNamespace(); // first call verify(ldapConnection).context(PROVIDER_URL, MANAGER_DN, MANAGER_PWD); // second call with the user dn verify(ldapConnection).context(PROVIDER_URL, "HOLOYOLO", "password"); } @SuppressWarnings("unchecked") @Test public void authenticate() throws NamingException { InitialDirContextCloseable ctx = mock(InitialDirContextCloseable.class); when(ldapConnection.context(PROVIDER_URL, MANAGER_DN, MANAGER_PWD)).thenReturn(ctx); NamingEnumeration<SearchResult> searchRes = mock(NamingEnumeration.class); when(ctx.search(eq("ou=system"), eq("uid=user"), any(SearchControls.class))).thenReturn(searchRes); when(searchRes.hasMore()).thenReturn(true, false); SearchResult sr = mock(SearchResult.class); when(sr.getNameInNamespace()).thenReturn("HOLOYOLO"); when(searchRes.next()).thenReturn(sr); Assert.assertTrue(ldap.authenticate("user", "password")); verify(sr).getNameInNamespace(); // first call verify(ldapConnection).context(PROVIDER_URL, MANAGER_DN, MANAGER_PWD); // second call with the user dn verify(ldapConnection).context(PROVIDER_URL, "HOLOYOLO", "password"); } }