/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at * trunk/opends/resource/legal-notices/OpenDS.LICENSE * or https://OpenDS.dev.java.net/OpenDS.LICENSE. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, * add the following below this CDDL HEADER, with the fields enclosed * by brackets "[]" replaced with your own identifying information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Copyright 2011 ForgeRock AS. */ package org.opends.server.api; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertTrue; import org.opends.server.TestCaseUtils; import org.opends.server.core.BindOperation; import org.opends.server.core.DirectoryServer; import org.opends.server.protocols.internal.InternalClientConnection; import org.opends.server.types.*; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; /** * Test authentication policy interaction. */ public class AuthenticationPolicyTestCase extends APITestCase { /** * A mock policy which records which methods have been called and their * parameters. */ private final class MockPolicy extends AuthenticationPolicy { private boolean isPolicyFinalized = false; private boolean isStateFinalized = false; private final boolean matches; private ByteString matchedPassword = null; /** * Returns {@code true} if {@code finalizeAuthenticationPolicy} was called. * * @return {@code true} if {@code finalizeAuthenticationPolicy} was called. */ public boolean isPolicyFinalized() { return isPolicyFinalized; } /** * Returns {@code true} if {@code finalizeStateAfterBind} was called. * * @return {@code true} if {@code finalizeStateAfterBind} was called. */ public boolean isStateFinalized() { return isStateFinalized; } /** * Returns the password which was tested. * * @return The password which was tested. */ public String getMatchedPassword() { return matchedPassword.toString(); } /** * Creates a new mock policy. * * @param matches * The result to always return from {@code passwordMatches}. */ public MockPolicy(boolean matches) { this.matches = matches; } /** * {@inheritDoc} */ public DN getDN() { return policyDN; } /** * {@inheritDoc} */ public AuthenticationPolicyState createAuthenticationPolicyState( Entry userEntry, long time) throws DirectoryException { return new AuthenticationPolicyState() { /** * {@inheritDoc} */ public boolean passwordMatches(ByteString password) throws DirectoryException { matchedPassword = password; return matches; } /** * {@inheritDoc} */ public void finalizeStateAfterBind() throws DirectoryException { isStateFinalized = true; } /** * {@inheritDoc} */ public AuthenticationPolicy getAuthenticationPolicy() { return MockPolicy.this; } }; } /** * {@inheritDoc} */ public void finalizeAuthenticationPolicy() { isPolicyFinalized = true; } } private final String policyDNString = "cn=test policy,o=test"; private final String userDNString = "cn=test user,o=test"; private DN policyDN; /** * Ensures that the Directory Server is running and creates a test backend * containing a single test user. * * @throws Exception * If an unexpected problem occurs. */ @BeforeClass() public void beforeClass() throws Exception { TestCaseUtils.startServer(); policyDN = DN.decode(policyDNString); } /** * Test simple authentication where password validation succeeds. * * @throws Exception * If an unexpected exception occurred. */ @Test public void testSimpleBindAllowed() throws Exception { testSimpleBind(true); } /** * Test simple authentication where password validation fails. * * @throws Exception * If an unexpected exception occurred. */ @Test public void testSimpleBindRefused() throws Exception { testSimpleBind(false); } /** * Test simple authentication where password validation succeeds. * * @throws Exception * If an unexpected exception occurred. */ @Test public void testSASLPLAINBindAllowed() throws Exception { testSASLPLAINBind(true); } /** * Test simple authentication where password validation fails. * * @throws Exception * If an unexpected exception occurred. */ @Test public void testSASLPLAINBindRefused() throws Exception { testSASLPLAINBind(false); } private void testSimpleBind(boolean allow) throws Exception { MockPolicy policy = new MockPolicy(allow); DirectoryServer.registerAuthenticationPolicy(policyDN, policy); try { // Create an empty test backend 'o=test' TestCaseUtils.initializeTestBackend(true); /* * The test user which who will be authenticated. */ TestCaseUtils.addEntries( /* @formatter:off */ "dn: " + userDNString, "objectClass: top", "objectClass: person", "ds-pwp-password-policy-dn: " + policyDNString, "userPassword: password", "sn: user", "cn: test user" /* @formatter:on */ ); // Perform the simple bind. InternalClientConnection conn = InternalClientConnection .getRootConnection(); BindOperation bind = conn.processSimpleBind(userDNString, "password"); // Check authentication result. assertEquals(bind.getResultCode(), allow ? ResultCode.SUCCESS : ResultCode.INVALID_CREDENTIALS); // Verify interaction with the policy/state. assertTrue(policy.isStateFinalized()); assertFalse(policy.isPolicyFinalized()); assertEquals(policy.getMatchedPassword(), "password"); } finally { DirectoryServer.deregisterAuthenticationPolicy(policyDN); assertTrue(policy.isPolicyFinalized()); } } private void testSASLPLAINBind(boolean allow) throws Exception { MockPolicy policy = new MockPolicy(allow); DirectoryServer.registerAuthenticationPolicy(policyDN, policy); try { // Create an empty test backend 'o=test' TestCaseUtils.initializeTestBackend(true); /* * The test user which who will be authenticated. */ TestCaseUtils.addEntries( /* @formatter:off */ "dn: " + userDNString, "objectClass: top", "objectClass: person", "ds-pwp-password-policy-dn: " + policyDNString, "userPassword: password", "sn: user", "cn: test user" /* @formatter:on */ ); // Perform the simple bind. InternalClientConnection conn = InternalClientConnection .getRootConnection(); ByteStringBuilder credentials = new ByteStringBuilder(); credentials.append((byte) 0); credentials.append("dn:" + userDNString); credentials.append((byte) 0); credentials.append("password"); BindOperation bind = conn.processSASLBind(DN.nullDN(), "PLAIN", credentials.toByteString()); // Check authentication result. assertEquals(bind.getResultCode(), allow ? ResultCode.SUCCESS : ResultCode.INVALID_CREDENTIALS); // Verify interaction with the policy/state. assertTrue(policy.isStateFinalized()); assertFalse(policy.isPolicyFinalized()); assertEquals(policy.getMatchedPassword(), "password"); } finally { DirectoryServer.deregisterAuthenticationPolicy(policyDN); assertTrue(policy.isPolicyFinalized()); } } }