Skip to content

Commit

Permalink
LDAP authentication tests.
Browse files Browse the repository at this point in the history
Signed-off-by: Lukasz Soszynski <[email protected]>

Tests for roles recursive search for LDAP.

Signed-off-by: Lukasz Soszynski <[email protected]>

User impersonation tests for LDAP authentication.

Signed-off-by: Lukasz Soszynski <[email protected]>
  • Loading branch information
lukasz-soszynski-eliatra committed Nov 23, 2022
1 parent 7cad5e4 commit 20cc9de
Show file tree
Hide file tree
Showing 25 changed files with 1,875 additions and 32 deletions.
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,7 @@ dependencies {
integrationTestImplementation('org.awaitility:awaitility:4.2.0') {
exclude(group: 'org.hamcrest', module: 'hamcrest')
}
integrationTestImplementation 'com.unboundid:unboundid-ldapsdk:4.0.9'
}

jar {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*
*/
package org.opensearch.security.http;

import org.opensearch.test.framework.ldap.LdifBuilder;
import org.opensearch.test.framework.ldap.LdifData;

class DirectoryInformationTrees {

public static final String DN_PEOPLE_TEST_ORG = "ou=people,o=test.org";
public static final String DN_OPEN_SEARCH_PEOPLE_TEST_ORG = "cn=Open Search,ou=people,o=test.org";
public static final String DN_CHRISTPHER_PEOPLE_TEST_ORG = "cn=Christpher,ou=people,o=test.org";
public static final String DN_KIRK_PEOPLE_TEST_ORG = "cn=Kirk,ou=people,o=test.org";
public static final String DN_CAPTAIN_SPOCK_PEOPLE_TEST_ORG = "cn=Captain Spock,ou=people,o=test.org";
public static final String DN_LEONARD_PEOPLE_TEST_ORG = "cn=Leonard,ou=people,o=test.org";
public static final String DN_JEAN_PEOPLE_TEST_ORG = "cn=Jean,ou=people,o=test.org";
public static final String DN_GROUPS_TEST_ORG = "ou=groups,o=test.org";
public static final String DN_BRIDGE_GROUPS_TEST_ORG = "cn=bridge,ou=groups,o=test.org";

public static final String USER_KIRK = "kirk";
public static final String PASSWORD_KIRK = "kirk-secret";
public static final String USER_SPOCK = "spock";
public static final String PASSWORD_SPOCK = "spocksecret";
public static final String USER_OPENS = "opens";
public static final String PASSWORD_OPEN_SEARCH = "open_search-secret";
public static final String USER_JEAN = "jean";
public static final String PASSWORD_JEAN = "jeansecret";
public static final String USER_LEONARD = "leonard";
public static final String PASSWORD_LEONARD = "Leonard-secret";
public static final String PASSWORD_CHRISTPHER = "christpher_secret";

public static final String CN_GROUP_ADMIN = "admin";
public static final String CN_GROUP_CREW = "crew";
public static final String CN_GROUP_BRIDGE = "bridge";

public static final String USER_SEARCH = "(uid={0})";
public static final String USERNAME_ATTRIBUTE = "uid";

static final LdifData LDIF_DATA = new LdifBuilder()
.root("o=test.org")
.dc("TEST")
.classes("top", "domain")
.newRecord(DN_PEOPLE_TEST_ORG)
.ou("people")
.classes("organizationalUnit", "top")
.newRecord(DN_OPEN_SEARCH_PEOPLE_TEST_ORG)
.classes("inetOrgPerson")
.cn("Open Search")
.sn("Search")
.uid(USER_OPENS)
.userPassword(PASSWORD_OPEN_SEARCH)
.mail("[email protected]")
.ou("Human Resources")
.newRecord(DN_CAPTAIN_SPOCK_PEOPLE_TEST_ORG)
.classes("inetOrgPerson")
.cn("Captain Spock")
.sn(USER_SPOCK)
.uid(USER_SPOCK)
.userPassword(PASSWORD_SPOCK)
.mail("[email protected]")
.ou("Human Resources")
.newRecord(DN_KIRK_PEOPLE_TEST_ORG)
.classes("inetOrgPerson")
.cn("Kirk")
.sn("Kirk")
.uid(USER_KIRK)
.userPassword(PASSWORD_KIRK)
.mail("[email protected]")
.ou("Human Resources")
.newRecord(DN_CHRISTPHER_PEOPLE_TEST_ORG)
.classes("inetOrgPerson")
.cn("Christpher")
.sn("Christpher")
.uid("christpher")
.userPassword(PASSWORD_CHRISTPHER)
.mail("[email protected]")
.ou("Human Resources")
.newRecord(DN_LEONARD_PEOPLE_TEST_ORG)
.classes("inetOrgPerson")
.cn("Leonard")
.sn("Leonard")
.uid(USER_LEONARD)
.userPassword(PASSWORD_LEONARD)
.mail("[email protected]")
.ou("Human Resources")
.newRecord(DN_JEAN_PEOPLE_TEST_ORG)
.classes("inetOrgPerson")
.cn("Jean")
.sn("Jean")
.uid(USER_JEAN)
.userPassword(PASSWORD_JEAN)
.mail("[email protected]")
.ou("Human Resources")
.newRecord(DN_GROUPS_TEST_ORG)
.ou("groups")
.cn("groupsRoot")
.classes("groupofuniquenames", "top")
.newRecord("cn=admin,ou=groups,o=test.org")
.ou("groups")
.cn(CN_GROUP_ADMIN)
.uniqueMember(DN_KIRK_PEOPLE_TEST_ORG)
.classes("groupofuniquenames", "top")
.newRecord("cn=crew,ou=groups,o=test.org")
.ou("groups")
.cn(CN_GROUP_CREW)
.uniqueMember(DN_CAPTAIN_SPOCK_PEOPLE_TEST_ORG)
.uniqueMember(DN_CHRISTPHER_PEOPLE_TEST_ORG)
.uniqueMember(DN_BRIDGE_GROUPS_TEST_ORG)
.classes("groupofuniquenames", "top")
.newRecord(DN_BRIDGE_GROUPS_TEST_ORG)
.ou("groups")
.cn(CN_GROUP_BRIDGE)
.uniqueMember(DN_JEAN_PEOPLE_TEST_ORG)
.classes("groupofuniquenames", "top")
.buildRecord()
.buildLdif();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*
*/
package org.opensearch.security.http;

import java.util.List;

import com.carrotsearch.randomizedtesting.annotations.ThreadLeakScope;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.RuleChain;
import org.junit.runner.RunWith;

import org.opensearch.test.framework.LdapAuthenticationConfigBuilder;
import org.opensearch.test.framework.TestSecurityConfig;
import org.opensearch.test.framework.TestSecurityConfig.AuthcDomain;
import org.opensearch.test.framework.TestSecurityConfig.AuthcDomain.AuthenticationBackend;
import org.opensearch.test.framework.TestSecurityConfig.AuthcDomain.HttpAuthenticator;
import org.opensearch.test.framework.certificate.TestCertificates;
import org.opensearch.test.framework.cluster.ClusterManager;
import org.opensearch.test.framework.cluster.LocalCluster;
import org.opensearch.test.framework.cluster.TestRestClient;
import org.opensearch.test.framework.ldap.EmbeddedLDAPServer;
import org.opensearch.test.framework.log.LogsRule;

import static org.opensearch.security.http.DirectoryInformationTrees.DN_CAPTAIN_SPOCK_PEOPLE_TEST_ORG;
import static org.opensearch.security.http.DirectoryInformationTrees.DN_OPEN_SEARCH_PEOPLE_TEST_ORG;
import static org.opensearch.security.http.DirectoryInformationTrees.DN_PEOPLE_TEST_ORG;
import static org.opensearch.security.http.DirectoryInformationTrees.LDIF_DATA;
import static org.opensearch.security.http.DirectoryInformationTrees.PASSWORD_OPEN_SEARCH;
import static org.opensearch.security.http.DirectoryInformationTrees.PASSWORD_SPOCK;
import static org.opensearch.security.http.DirectoryInformationTrees.USERNAME_ATTRIBUTE;
import static org.opensearch.security.http.DirectoryInformationTrees.USER_SEARCH;
import static org.opensearch.security.http.DirectoryInformationTrees.USER_SPOCK;
import static org.opensearch.test.framework.TestSecurityConfig.AuthcDomain.AUTHC_HTTPBASIC_INTERNAL;
import static org.opensearch.test.framework.TestSecurityConfig.AuthcDomain.BASIC_AUTH_DOMAIN_ORDER;
import static org.opensearch.test.framework.TestSecurityConfig.Role.ALL_ACCESS;

/**
* Test uses plain (non TLS) connection between OpenSearch and LDAP server.
*/
@RunWith(com.carrotsearch.randomizedtesting.RandomizedRunner.class)
@ThreadLeakScope(ThreadLeakScope.Scope.NONE)
public class LdapAuthenticationTest {

private static final Logger log = LogManager.getLogger(LdapAuthenticationTest.class);

private static final TestSecurityConfig.User ADMIN_USER = new TestSecurityConfig.User("admin").roles(ALL_ACCESS);

private static final TestCertificates TEST_CERTIFICATES = new TestCertificates();

public static final EmbeddedLDAPServer embeddedLDAPServer = new EmbeddedLDAPServer(TEST_CERTIFICATES.getRootCertificateData(),
TEST_CERTIFICATES.getLdapCertificateData(), LDIF_DATA);

public static LocalCluster cluster = new LocalCluster.Builder()
.testCertificates(TEST_CERTIFICATES)
.clusterManager(ClusterManager.SINGLENODE).anonymousAuth(false)
.authc(new AuthcDomain("ldap", BASIC_AUTH_DOMAIN_ORDER + 1, true)
.httpAuthenticator(new HttpAuthenticator("basic").challenge(false))
.backend(new AuthenticationBackend("ldap")
.config(() -> LdapAuthenticationConfigBuilder.config()
// this port is available when embeddedLDAPServer is already started, therefore Supplier interface is used to postpone
// execution of the code in this block.
.enableSsl(false)
.enableStartTls(false)
.hosts(List.of("localhost:" + embeddedLDAPServer.getLdapNonTlsPort()))
.bindDn(DN_OPEN_SEARCH_PEOPLE_TEST_ORG)
.password(PASSWORD_OPEN_SEARCH)
.userBase(DN_PEOPLE_TEST_ORG)
.userSearch(USER_SEARCH)
.usernameAttribute(USERNAME_ATTRIBUTE)
.build())))
.authc(AUTHC_HTTPBASIC_INTERNAL)
.users(ADMIN_USER)
.build();

@ClassRule
public static RuleChain ruleChain = RuleChain.outerRule(embeddedLDAPServer).around(cluster);

@Rule
public LogsRule logsRule = new LogsRule("com.amazon.dlic.auth.ldap.backend.LDAPAuthenticationBackend");

@Test
public void shouldAuthenticateUserWithLdap_positive() {
try (TestRestClient client = cluster.getRestClient(USER_SPOCK, PASSWORD_SPOCK)) {
TestRestClient.HttpResponse response = client.getAuthInfo();

response.assertStatusCode(200);
}
}

@Test
public void shouldAuthenticateUserWithLdap_negativeWhenIncorrectPassword() {
try (TestRestClient client = cluster.getRestClient(USER_SPOCK, "incorrect password")) {
TestRestClient.HttpResponse response = client.getAuthInfo();

response.assertStatusCode(401);
String expectedStackTraceFragment = "Unable to bind as user '".concat(DN_CAPTAIN_SPOCK_PEOPLE_TEST_ORG)
.concat("' because the provided password was incorrect.");
logsRule.assertThatStackTraceContain(expectedStackTraceFragment);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*
*/
package org.opensearch.security.http;

import java.util.List;

import com.carrotsearch.randomizedtesting.annotations.ThreadLeakScope;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.RuleChain;
import org.junit.runner.RunWith;

import org.opensearch.test.framework.LdapAuthenticationConfigBuilder;
import org.opensearch.test.framework.TestSecurityConfig;
import org.opensearch.test.framework.TestSecurityConfig.AuthcDomain;
import org.opensearch.test.framework.TestSecurityConfig.AuthcDomain.AuthenticationBackend;
import org.opensearch.test.framework.TestSecurityConfig.AuthcDomain.HttpAuthenticator;
import org.opensearch.test.framework.certificate.TestCertificates;
import org.opensearch.test.framework.cluster.ClusterManager;
import org.opensearch.test.framework.cluster.LocalCluster;
import org.opensearch.test.framework.cluster.TestRestClient;
import org.opensearch.test.framework.ldap.EmbeddedLDAPServer;
import org.opensearch.test.framework.log.LogsRule;

import static org.opensearch.security.http.DirectoryInformationTrees.DN_CAPTAIN_SPOCK_PEOPLE_TEST_ORG;
import static org.opensearch.security.http.DirectoryInformationTrees.DN_OPEN_SEARCH_PEOPLE_TEST_ORG;
import static org.opensearch.security.http.DirectoryInformationTrees.DN_PEOPLE_TEST_ORG;
import static org.opensearch.security.http.DirectoryInformationTrees.LDIF_DATA;
import static org.opensearch.security.http.DirectoryInformationTrees.PASSWORD_OPEN_SEARCH;
import static org.opensearch.security.http.DirectoryInformationTrees.PASSWORD_SPOCK;
import static org.opensearch.security.http.DirectoryInformationTrees.USERNAME_ATTRIBUTE;
import static org.opensearch.security.http.DirectoryInformationTrees.USER_SEARCH;
import static org.opensearch.security.http.DirectoryInformationTrees.USER_SPOCK;
import static org.opensearch.test.framework.TestSecurityConfig.AuthcDomain.AUTHC_HTTPBASIC_INTERNAL;
import static org.opensearch.test.framework.TestSecurityConfig.AuthcDomain.BASIC_AUTH_DOMAIN_ORDER;
import static org.opensearch.test.framework.TestSecurityConfig.Role.ALL_ACCESS;

/**
* Test initiates plain (non-TLS) connection between OpenSearch and LDAP server and then in the course of the test connection is upgraded
* to TLS.
*/
@RunWith(com.carrotsearch.randomizedtesting.RandomizedRunner.class)
@ThreadLeakScope(ThreadLeakScope.Scope.NONE)
public class LdapStartTlsAuthenticationTest {

private static final TestSecurityConfig.User ADMIN_USER = new TestSecurityConfig.User("admin").roles(ALL_ACCESS);

private static final TestCertificates TEST_CERTIFICATES = new TestCertificates();

public static final EmbeddedLDAPServer embeddedLDAPServer = new EmbeddedLDAPServer(TEST_CERTIFICATES.getRootCertificateData(),
TEST_CERTIFICATES.getLdapCertificateData(), LDIF_DATA);

public static LocalCluster cluster = new LocalCluster.Builder()
.testCertificates(TEST_CERTIFICATES)
.clusterManager(ClusterManager.SINGLENODE).anonymousAuth(false)
.authc(new AuthcDomain("ldap-config-id", BASIC_AUTH_DOMAIN_ORDER + 1, true)
.httpAuthenticator(new HttpAuthenticator("basic").challenge(false))
.backend(new AuthenticationBackend("ldap")
.config(() -> LdapAuthenticationConfigBuilder.config()
// this port is available when embeddedLDAPServer is already started, therefore Supplier interface is used
.hosts(List.of("localhost:" + embeddedLDAPServer.getLdapNonTlsPort()))
.enableSsl(false)
.enableStartTls(true)
.bindDn(DN_OPEN_SEARCH_PEOPLE_TEST_ORG)
.password(PASSWORD_OPEN_SEARCH)
.userBase(DN_PEOPLE_TEST_ORG)
.userSearch(USER_SEARCH)
.usernameAttribute(USERNAME_ATTRIBUTE)
.penTrustedCasFilePath(TEST_CERTIFICATES.getRootCertificate().getAbsolutePath())
.build())))
.authc(AUTHC_HTTPBASIC_INTERNAL)
.users(ADMIN_USER)
.build();

@ClassRule
public static RuleChain ruleChain = RuleChain.outerRule(embeddedLDAPServer).around(cluster);

@Rule
public LogsRule logsRule = new LogsRule("com.amazon.dlic.auth.ldap.backend.LDAPAuthenticationBackend");

@Test
public void shouldAuthenticateUserWithLdap_positive() {
try (TestRestClient client = cluster.getRestClient(USER_SPOCK, PASSWORD_SPOCK)) {
TestRestClient.HttpResponse response = client.getAuthInfo();

response.assertStatusCode(200);
}
}

@Test
public void shouldAuthenticateUserWithLdap_negativeWhenIncorrectPassword() {
try (TestRestClient client = cluster.getRestClient(USER_SPOCK, "incorrect password")) {
TestRestClient.HttpResponse response = client.getAuthInfo();

response.assertStatusCode(401);
String expectedStackTraceFragment = "Unable to bind as user '".concat(DN_CAPTAIN_SPOCK_PEOPLE_TEST_ORG)
.concat("' because the provided password was incorrect.");
logsRule.assertThatStackTraceContain(expectedStackTraceFragment);
}
}
}
Loading

0 comments on commit 20cc9de

Please sign in to comment.