/*
|
* 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());
|
}
|
}
|
}
|