blob: 976bfc82327b35cfdc95288bd948e56653f07358 [file] [log] [blame]
/**
* 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.hadoop.security;
import org.apache.hadoop.conf.Configuration;
import org.junit.Test;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import javax.naming.CommunicationException;
import javax.naming.directory.SearchControls;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.atomic.AtomicInteger;
import static org.apache.hadoop.security.LdapGroupsMapping.LDAP_NUM_ATTEMPTS_BEFORE_FAILOVER_KEY;
import static org.apache.hadoop.security.LdapGroupsMapping.LDAP_NUM_ATTEMPTS_KEY;
import static org.apache.hadoop.security.LdapGroupsMapping.LDAP_URL_KEY;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
* Test failover functionality for switching to different
* LDAP server URLs upon failures.
*/
public class TestLdapGroupsMappingWithFailover
extends TestLdapGroupsMappingBase {
private static final String TEST_USER_NAME = "some_user";
/**
* Test that when disabled, we will retry the configured number
* of times using the same LDAP server.
*/
@Test
public void testDoesNotFailoverWhenDisabled() throws Exception {
final int numAttempts = 3;
Configuration conf = getBaseConf();
conf.setStrings(LDAP_URL_KEY, "ldap://test", "ldap://test1",
"ldap://test2");
DummyLdapCtxFactory.setExpectedLdapUrl("ldap://test");
conf.setInt(LDAP_NUM_ATTEMPTS_KEY, numAttempts);
conf.setInt(LDAP_NUM_ATTEMPTS_BEFORE_FAILOVER_KEY, numAttempts);
when(getContext().search(anyString(), anyString(), any(Object[].class),
any(SearchControls.class))).thenThrow(new CommunicationException());
LdapGroupsMapping groupsMapping = getGroupsMapping();
groupsMapping.setConf(conf);
List<String> groups = groupsMapping.getGroups(TEST_USER_NAME);
assertTrue(groups.isEmpty());
// Test that we made 3 attempts using the same server
verify(getContext(), times(numAttempts)).search(anyString(), anyString(),
any(Object[].class), any(SearchControls.class));
}
/**
* Test that when configured, we will make the specified amount of
* attempts using one ldap url before failing over to the next one.
*
* This also tests that we wrap back to the first server
* if we've tried them all.
*/
@Test
public void testFailover() throws Exception {
final Queue<String> ldapUrls = new LinkedList<>();
ldapUrls.add("ldap://test");
ldapUrls.add("ldap://test1");
ldapUrls.add("ldap://test2");
final int numAttempts = 12;
final int numAttemptsBeforeFailover = 2;
Configuration conf = getBaseConf();
conf.setStrings(LDAP_URL_KEY, "ldap://test", "ldap://test1",
"ldap://test2");
conf.setInt(LDAP_NUM_ATTEMPTS_KEY, numAttempts);
conf.setInt(LDAP_NUM_ATTEMPTS_BEFORE_FAILOVER_KEY,
numAttemptsBeforeFailover);
// Set the first expected url and add it back to the queue
String nextLdapUrl = ldapUrls.remove();
DummyLdapCtxFactory.setExpectedLdapUrl(nextLdapUrl);
ldapUrls.add(nextLdapUrl);
// Number of attempts using a single ldap server url
final AtomicInteger serverAttempts = new AtomicInteger(
numAttemptsBeforeFailover);
when(getContext().search(anyString(), anyString(), any(Object[].class),
any(SearchControls.class))).thenAnswer(new Answer<Object>() {
@Override
public Object answer(InvocationOnMock invocationOnMock)
throws Throwable {
if (serverAttempts.get() == 1) {
// Take the head of the queue and re-queue it to the back
String nextLdapUrl = ldapUrls.remove();
ldapUrls.add(nextLdapUrl);
DummyLdapCtxFactory.setExpectedLdapUrl(nextLdapUrl);
serverAttempts.set(numAttemptsBeforeFailover);
} else {
serverAttempts.decrementAndGet();
}
throw new CommunicationException();
}
});
LdapGroupsMapping groupsMapping = getGroupsMapping();
groupsMapping.setConf(conf);
List<String> groups = groupsMapping.getGroups(TEST_USER_NAME);
assertTrue(groups.isEmpty());
// Test that we made 6 attempts overall
verify(getContext(), times(numAttempts)).search(anyString(),
anyString(), any(Object[].class), any(SearchControls.class));
}
}