/**
* Copyright 2016 StreamSets Inc.
*
* Licensed under 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 com.streamsets.datacollector.http;
import com.streamsets.datacollector.main.RuntimeInfo;
import com.streamsets.datacollector.main.RuntimeModule;
import org.apache.directory.ldap.client.api.LdapConnection;
import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature;
import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.shaded.com.google.common.collect.ImmutableList;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.core.Response;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Arrays;
@Ignore
@RunWith(Parameterized.class)
public class LDAPAuthenticationFallbackIT extends LdapAuthenticationBaseIT {
/**
* This IT uses two docker containers to mimic two different LDAP servers.
* When authentication failed on the 1st server, SDC will fallback to the 2nd server
* and proceed authentication.
*/
// Connection to Ldap Server 1, authentication will fail on most of the tests
private static LdapConnection connection1;
// Connection to Ldap Server 2, authentication will success on most of the tests
private static LdapConnection connection2;
@ClassRule
public static GenericContainer server1 = new GenericContainer("osixia/openldap:1.1.6").withExposedPorts(LDAP_PORT);
@ClassRule
public static GenericContainer server2 = new GenericContainer("osixia/openldap:1.1.6").withExposedPorts(LDAP_PORT);
@Parameterized.Parameters
public static Collection<Object[]> data() throws Exception {
return Arrays.asList(new Object[][]{
// {username, password, authentication success?, expected sdc role to be found}
{"user1", "user1", true, ImmutableList.of("creator")}, // wrong password in server1, but correct in server2
{"user2", "user2", true, ImmutableList.of("admin")}, // user not found in server1, but found in server2
{"user3", "user3", true, ImmutableList.of("admin")}, // group is not mapped in server1
{"user4", "user4", true, ImmutableList.of("admin")}, // no group in server1, but found in server2
{"user3", "dummy", false, ImmutableList.of()}, // password is wrong. Auth failed on both servers
{"user5", "user5", true, ImmutableList.of("manager", "admin")} // user found in both servers but roles are different
});
}
private String username;
private String password;
private boolean result;
private List<String> role;
@BeforeClass
public static void setUpClass() throws Exception {
// create conf dir
new File(confDir).mkdirs();
connection1 = setupLdapServer(server1, "ldif/ldap-server1-entries.ldif");
connection2 = setupLdapServer(server2, "ldif/ldap-server2-entries.ldif");
String multipleLdapConf = "ldap {\n" + // information for server 1
" com.streamsets.datacollector.http.LdapLoginModule required\n" +
" debug=\"false\"\n" +
" useLdaps=\"false\"\n" +
" contextFactory=\"com.sun.jndi.ldap.LdapCtxFactory\"\n" +
" hostname=\"" + server1.getContainerIpAddress()+ "\"\n" +
" port=\"" + server1.getMappedPort(LDAP_PORT) + "\"\n" +
" bindDn=\"" + BIND_DN + "\"\n" +
" bindPassword=\"" + BIND_PWD + "\"\n" +
" authenticationMethod=\"simple\"\n" +
" forceBindingLogin=\"false\"\n" +
" userBaseDn=\"ou=users,dc=example,dc=org\"\n" +
" userRdnAttribute=\"uid\"\n" +
" userIdAttribute=\"uid\"\n" +
" userPasswordAttribute=\"userPassword\"\n" +
" userObjectClass=\"inetOrgPerson\"\n" +
" roleBaseDn=\"ou=groups,dc=example,dc=org\"\n" +
" roleNameAttribute=\"cn\"\n" +
" roleMemberAttribute=\"member\"\n" +
" roleObjectClass=\"groupOfNames\";\n" +
" \n" + // information for sever2
" com.streamsets.datacollector.http.LdapLoginModule required\n" +
" debug=\"false\"\n" +
" useLdaps=\"false\"\n" +
" contextFactory=\"com.sun.jndi.ldap.LdapCtxFactory\"\n" +
" hostname=\"" + server2.getContainerIpAddress()+ "\"\n" +
" port=\"" + server2.getMappedPort(LDAP_PORT) + "\"\n" +
" bindDn=\"" + BIND_DN + "\"\n" +
" bindPassword=\"" + BIND_PWD + "\"\n" +
" authenticationMethod=\"simple\"\n" +
" forceBindingLogin=\"false\"\n" +
" userBaseDn=\"ou=employees,dc=example,dc=org\"\n" +
" userRdnAttribute=\"uid\"\n" +
" userIdAttribute=\"uid\"\n" +
" userPasswordAttribute=\"userPassword\"\n" +
" userObjectClass=\"inetOrgPerson\"\n" +
" roleBaseDn=\"ou=departments,dc=example,dc=org\"\n" +
" roleNameAttribute=\"cn\"\n" +
" roleMemberAttribute=\"member\"\n" +
" roleObjectClass=\"groupOfNames\";\n" +
"};";
startSDCServer(multipleLdapConf);
}
@AfterClass
public static void cleanUpClass() throws IOException {
connection1.close();
connection2.close();
server1.stop();
server2.stop();
System.getProperties().remove(RuntimeModule.SDC_PROPERTY_PREFIX + RuntimeInfo.CONFIG_DIR);
stopSDCServer();
}
public LDAPAuthenticationFallbackIT(String username, String password, boolean result, List<String> role)
throws Exception {
this.username = username;
this.password = password;
this.result = result;
this.role = role;
}
@Test
public void testLdapAuthentication(){
String userInfoURI = sdcURL + "/rest/v1/system/info/currentUser";
HttpAuthenticationFeature feature = HttpAuthenticationFeature.basic(username, password);
Response response = ClientBuilder
.newClient()
.register(feature)
.target(userInfoURI)
.request()
.get();
if (!result) {
Assert.assertEquals(401, response.getStatus());
} else{
Assert.assertEquals(200, response.getStatus());
Map userInfo = response.readEntity(Map.class);
Assert.assertTrue(userInfo.containsKey("user"));
Assert.assertEquals(username, userInfo.get("user"));
Assert.assertTrue(userInfo.containsKey("roles"));
List<String> roles = (List<String>) userInfo.get("roles");
Assert.assertEquals(role.size(), roles.size());
for(int i = 0; i < roles.size(); i++) {
Assert.assertEquals(role.get(i), roles.get(i));
}
}
}
}