AuthenticationPolicyTestCase.java revision 88f16d892d54fd8c3e190cc1f6363638b11ae1a3
/*
* 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 legal-notices/CDDLv1_0.txt
* or http://forgerock.org/license/CDDLv1.0.html.
* 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 legal-notices/CDDLv1_0.txt.
* 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
*
*
* Portions Copyright 2011-2015 ForgeRock AS.
* Portions Copyright 2014 ForgeRock AS
*/
package org.opends.server.api;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertNull;
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.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.ByteStringBuilder;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
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 final boolean isDisabled;
private final boolean matches;
private boolean isPolicyFinalized;
private boolean isStateFinalized;
private ByteString matchedPassword;
/**
* 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 ByteString getMatchedPassword()
{
return matchedPassword;
}
/**
* Creates a new mock policy.
*
* @param matches
* The result to always return from {@code passwordMatches}.
* @param isDisabled
* The result to return from {@code isDisabled}.
*/
public MockPolicy(boolean matches, boolean isDisabled)
{
this.matches = matches;
this.isDisabled = isDisabled;
}
/**
* {@inheritDoc}
*/
public DN getDN()
{
return policyDN;
}
/**
* {@inheritDoc}
*/
public AuthenticationPolicyState createAuthenticationPolicyState(
Entry userEntry, long time) throws DirectoryException
{
return new AuthenticationPolicyState(userEntry)
{
/**
* {@inheritDoc}
*/
public boolean passwordMatches(ByteString password)
throws DirectoryException
{
matchedPassword = password;
return matches;
}
/**
* {@inheritDoc}
*/
public boolean isDisabled()
{
return MockPolicy.this.isDisabled;
}
/**
* {@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.valueOf(policyDNString);
}
/**
* Returns test data for the simple/sasl tests.
*
* @return Test data for the simple/sasl tests.
*/
@DataProvider
public Object[][] testBindData()
{
// @formatter:off
return new Object[][] {
/* password matches, account is disabled */
{ false, false },
{ false, true },
{ true, false },
{ true, true },
};
// @formatter:on
}
/**
* Test simple authentication where password validation succeeds.
*
* @param matches
* The result to always return from {@code passwordMatches}.
* @param isDisabled
* The result to return from {@code isDisabled}.
* @throws Exception
* If an unexpected exception occurred.
*/
@Test(dataProvider = "testBindData")
public void testSimpleBind(boolean matches, boolean isDisabled)
throws Exception
{
MockPolicy policy = new MockPolicy(matches, isDisabled);
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(),
matches & !isDisabled ? ResultCode.SUCCESS
: ResultCode.INVALID_CREDENTIALS);
// Verify interaction with the policy/state.
assertTrue(policy.isStateFinalized());
assertFalse(policy.isPolicyFinalized());
if (!isDisabled)
{
assertEquals(policy.getMatchedPassword().toString(), "password");
}
else
{
// If the account is disabled then the password should not have been
// checked. This is important because we want to avoid potentially
// expensive password fetches (e.g. PTA).
assertNull(policy.getMatchedPassword());
}
}
finally
{
DirectoryServer.deregisterAuthenticationPolicy(policyDN);
assertTrue(policy.isPolicyFinalized());
}
}
/**
* Test simple authentication where password validation succeeds.
*
* @param matches
* The result to always return from {@code passwordMatches}.
* @param isDisabled
* The result to return from {@code isDisabled}.
* @throws Exception
* If an unexpected exception occurred.
*/
@Test(dataProvider = "testBindData")
public void testSASLPLAINBind(boolean matches, boolean isDisabled)
throws Exception
{
MockPolicy policy = new MockPolicy(matches, isDisabled);
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.rootDN(), "PLAIN",
credentials.toByteString());
// Check authentication result.
assertEquals(bind.getResultCode(),
matches & !isDisabled ? ResultCode.SUCCESS
: ResultCode.INVALID_CREDENTIALS);
// Verify interaction with the policy/state.
assertTrue(policy.isStateFinalized());
assertFalse(policy.isPolicyFinalized());
if (!isDisabled)
{
assertEquals(policy.getMatchedPassword().toString(), "password");
}
else
{
// If the account is disabled then the password should not have been
// checked. This is important because we want to avoid potentially
// expensive password fetches (e.g. PTA).
assertNull(policy.getMatchedPassword());
}
}
finally
{
DirectoryServer.deregisterAuthenticationPolicy(policyDN);
assertTrue(policy.isPolicyFinalized());
}
}
}