/* * The contents of this file are subject to the terms of the Common Development and * Distribution License (the License). You may not use this file except in compliance with the * License. * * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the * specific language governing permission and limitations under the License. * * When distributing Covered Software, include this CDDL Header Notice in each file and include * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL * Header, with the fields enclosed by brackets [] replaced by your own identifying * information: "Portions copyright [year] [name of copyright owner]". * * Copyright 2016 ForgeRock AS. * Portions Copyright 2026 3A Systems, LLC. */ package org.forgerock.opendj.rest2ldap.authz; import static org.fest.assertions.Assertions.assertThat; import static org.forgerock.opendj.rest2ldap.authz.CredentialExtractors.HTTP_BASIC_AUTH_HEADER; import static org.forgerock.opendj.rest2ldap.authz.CredentialExtractors.httpBasicExtractor; import static org.forgerock.opendj.rest2ldap.authz.CredentialExtractors.newCustomHeaderExtractor; import org.forgerock.http.protocol.Headers; import org.forgerock.testng.ForgeRockTestCase; import org.forgerock.util.Pair; import org.forgerock.util.encode.Base64; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @Test public class CredentialExtractorsTest extends ForgeRockTestCase { @Test public void testBasicCanExtractValidCredentials() { final Headers headers = new Headers(); headers.put(HTTP_BASIC_AUTH_HEADER, "basic " + Base64.encode("foo:bar".getBytes())); assertThat(httpBasicExtractor().apply(headers)).isEqualTo(Pair.of("foo", "bar")); } @DataProvider public Object[][] validBasicCredentials() { // @formatter:off return new Object[][] { // RFC 7617 section 2 forbids a colon only in the user-id: the password is everything after the first one. { "bjensen:se:cret", "bjensen", "se:cret" }, { "bjensen::", "bjensen", ":" }, { "uid=bjensen,ou=People,dc=example,dc=com:secret", "uid=bjensen,ou=People,dc=example,dc=com", "secret" }, // An empty password is a well-formed credential; the filter, not the parser, refuses it. { "bjensen:", "bjensen", "" }, }; // @formatter:on } @Test(dataProvider = "validBasicCredentials") public void testBasicSplitsAtTheFirstColon(final String credentials, final String username, final String password) { final Headers headers = new Headers(); headers.put(HTTP_BASIC_AUTH_HEADER, "Basic " + Base64.encode(credentials.getBytes())); assertThat(httpBasicExtractor().apply(headers)).isEqualTo(Pair.of(username, password)); } @DataProvider public Object[][] invalidBasicHeaders() { // @formatter:off return new Object[][] { { "*invalid*" }, { "Basic " + Base64.encode("bjensen".getBytes()) }, { "Basic !!!" }, // Base64 which is not a multiple of 4 characters long does not decode at all. { "Basic abc" }, { "Basic " + Base64.encode("foo:bar".getBytes()).replace("=", "") }, { "Basic" }, }; // @formatter:on } @Test(dataProvider = "invalidBasicHeaders") public void testBasicReturnNullOnInvalidCredentials(final String header) { final Headers headers = new Headers(); headers.put(HTTP_BASIC_AUTH_HEADER, header); assertThat(httpBasicExtractor().apply(headers)).isNull(); } @Test public void testBasicReturnNullOnMissingCredentials() { assertThat(httpBasicExtractor().apply(new Headers())).isNull(); } @Test public void testCustomCanExtractValidCredentials() { final Headers headers = new Headers(); headers.put("X-user", "foo"); headers.put("X-password", "bar"); assertThat(newCustomHeaderExtractor("X-user", "X-password").apply(headers)).isEqualTo(Pair.of("foo", "bar")); } @Test public void testCustomFallbackOnBasicIfMissingCustomCredentials() { final Headers headers = new Headers(); headers.put(HTTP_BASIC_AUTH_HEADER, "basic " + Base64.encode("foo:bar".getBytes())); assertThat(newCustomHeaderExtractor("X-user", "X-password").apply(headers)).isEqualTo(Pair.of("foo", "bar")); } @Test public void testCustomReturnNullOnMissingCredentials() { assertThat(httpBasicExtractor().apply(new Headers())).isNull(); } }