/*
|
* 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
|
*
|
*
|
* Copyright 2008-2009 Sun Microsystems, Inc.
|
* Portions Copyright 2011-2015 ForgeRock AS.
|
*/
|
package org.opends.server.controls;
|
|
import java.net.Socket;
|
import java.util.ArrayList;
|
import java.util.LinkedHashSet;
|
import java.util.List;
|
|
import org.forgerock.opendj.ldap.ByteString;
|
import org.forgerock.opendj.ldap.DereferenceAliasesPolicy;
|
import org.forgerock.opendj.ldap.ModificationType;
|
import org.forgerock.opendj.ldap.SearchScope;
|
import org.opends.server.TestCaseUtils;
|
import org.opends.server.protocols.ldap.*;
|
import org.opends.server.types.Control;
|
import org.opends.server.types.DirectoryException;
|
import org.opends.server.types.RawAttribute;
|
import org.opends.server.types.RawModification;
|
import org.opends.server.util.StaticUtils;
|
import org.testng.annotations.BeforeClass;
|
import org.testng.annotations.DataProvider;
|
import org.testng.annotations.Test;
|
|
import static org.opends.server.TestCaseUtils.*;
|
import static org.opends.server.util.ServerConstants.*;
|
import static org.testng.Assert.*;
|
|
/**
|
* This class contains test cases that verify the appropriate handling of the
|
* password policy control as defined in draft-behera-ldap-password-policy.
|
*/
|
public class PasswordPolicyControlTestCase
|
extends ControlsTestCase
|
{
|
/**
|
* Make sure that the server is running.
|
*
|
* @throws Exception If an unexpected problem occurs.
|
*/
|
@BeforeClass
|
public void startServer()
|
throws Exception
|
{
|
TestCaseUtils.startServer();
|
}
|
|
|
|
/**
|
* Tests that an appropriate password policy response control is returned for
|
* an add operation when the user's password is in a "must change" state.
|
* This test will also ensure that the bind response is also capable of
|
* including the password policy response control with the "change after
|
* reset" error type set.
|
*
|
* @throws Exception If an unexpected problem occurs.
|
*/
|
@Test
|
public void testAddMustChange()
|
throws Exception
|
{
|
TestCaseUtils.initializeTestBackend(true);
|
|
setPasswordPolicyProp("--set", "force-change-on-add:true");
|
|
TestCaseUtils.addEntry(
|
"dn: uid=test.user,o=test",
|
"objectClass: top",
|
"objectClass: person",
|
"objectClass: organizationalPerson",
|
"objectClass: inetOrgPerson",
|
"uid: test.user",
|
"givenName: Test",
|
"sn: User",
|
"cn: Test User",
|
"userPassword: password",
|
"ds-privilege-name: bypass-acl");
|
|
Socket s = new Socket("127.0.0.1", TestCaseUtils.getServerLdapPort());
|
org.opends.server.tools.LDAPReader r = new org.opends.server.tools.LDAPReader(s);
|
org.opends.server.tools.LDAPWriter w = new org.opends.server.tools.LDAPWriter(s);
|
|
try
|
{
|
List<Control> controls = new ArrayList<Control>();
|
controls.add(new LDAPControl(OID_PASSWORD_POLICY_CONTROL, true));
|
|
BindRequestProtocolOp bindRequest = new BindRequestProtocolOp(
|
ByteString.valueOf("uid=test.user,o=test"), 3,
|
ByteString.valueOf("password"));
|
LDAPMessage message = new LDAPMessage(1, bindRequest, controls);
|
w.writeMessage(message);
|
|
message = r.readMessage();
|
BindResponseProtocolOp bindResponse = message.getBindResponseProtocolOp();
|
assertEquals(bindResponse.getResultCode(), LDAPResultCode.SUCCESS);
|
|
controls = message.getControls();
|
assertNotNull(controls);
|
assertFalse(controls.isEmpty());
|
|
assertTrue(passwordPolicyControlExists(controls, PasswordPolicyErrorType.CHANGE_AFTER_RESET));
|
|
|
ArrayList<RawAttribute> rawAttrs = new ArrayList<RawAttribute>();
|
rawAttrs.add(RawAttribute.create("objectClass", "organizationalUnit"));
|
rawAttrs.add(RawAttribute.create("ou", "People"));
|
|
AddRequestProtocolOp addRequest = new AddRequestProtocolOp(
|
ByteString.valueOf("ou=People,o=test"), rawAttrs);
|
|
controls = new ArrayList<Control>();
|
controls.add(new LDAPControl(OID_PASSWORD_POLICY_CONTROL, true));
|
|
message = new LDAPMessage(2, addRequest, controls);
|
w.writeMessage(message);
|
|
message = r.readMessage();
|
AddResponseProtocolOp addResponse = message.getAddResponseProtocolOp();
|
assertNotEquals(addResponse.getResultCode(), LDAPResultCode.SUCCESS);
|
|
controls = message.getControls();
|
assertNotNull(controls);
|
assertFalse(controls.isEmpty());
|
|
assertTrue(passwordPolicyControlExists(controls, PasswordPolicyErrorType.CHANGE_AFTER_RESET));
|
}
|
finally
|
{
|
setPasswordPolicyProp("--set", "force-change-on-add:false");
|
|
StaticUtils.close(s);
|
}
|
}
|
|
|
|
/**
|
* Tests that an appropriate password policy response control is returned for
|
* an add operation in which the proposed password is pre-encoded.
|
*
|
* @throws Exception If an unexpected problem occurs.
|
*/
|
@Test
|
public void testAddPreEncodedPassword()
|
throws Exception
|
{
|
TestCaseUtils.initializeTestBackend(true);
|
|
Socket s = new Socket("127.0.0.1", TestCaseUtils.getServerLdapPort());
|
org.opends.server.tools.LDAPReader r = new org.opends.server.tools.LDAPReader(s);
|
org.opends.server.tools.LDAPWriter w = new org.opends.server.tools.LDAPWriter(s);
|
|
try
|
{
|
BindRequestProtocolOp bindRequest = new BindRequestProtocolOp(
|
ByteString.valueOf("cn=Directory Manager"), 3,
|
ByteString.valueOf("password"));
|
LDAPMessage message = new LDAPMessage(1, bindRequest);
|
w.writeMessage(message);
|
|
message = r.readMessage();
|
BindResponseProtocolOp bindResponse = message.getBindResponseProtocolOp();
|
assertEquals(bindResponse.getResultCode(), LDAPResultCode.SUCCESS);
|
|
|
ArrayList<RawAttribute> rawAttrs = new ArrayList<RawAttribute>();
|
rawAttrs.add(RawAttribute.create("objectClass", "inetOrgPerson"));
|
rawAttrs.add(RawAttribute.create("uid", "test.user"));
|
rawAttrs.add(RawAttribute.create("givenName", "Test"));
|
rawAttrs.add(RawAttribute.create("sn", "User"));
|
rawAttrs.add(RawAttribute.create("cn", "Test User"));
|
rawAttrs.add(RawAttribute.create("userPassword",
|
"{SSHA}0pZPpMIm6xSBIW4hGvR/72fjO4M9p3Ff1g7QFw=="));
|
|
AddRequestProtocolOp addRequest = new AddRequestProtocolOp(
|
ByteString.valueOf("ou=uid=test.user,o=test"), rawAttrs);
|
|
List<Control> controls = new ArrayList<Control>();
|
controls.add(new LDAPControl(OID_PASSWORD_POLICY_CONTROL, true));
|
|
message = new LDAPMessage(2, addRequest, controls);
|
w.writeMessage(message);
|
|
message = r.readMessage();
|
AddResponseProtocolOp addResponse = message.getAddResponseProtocolOp();
|
assertNotEquals(addResponse.getResultCode(), LDAPResultCode.SUCCESS);
|
|
controls = message.getControls();
|
assertNotNull(controls);
|
assertFalse(controls.isEmpty());
|
|
assertTrue(passwordPolicyControlExists(controls, PasswordPolicyErrorType.INSUFFICIENT_PASSWORD_QUALITY));
|
}
|
finally
|
{
|
StaticUtils.close(s);
|
}
|
}
|
|
private boolean passwordPolicyControlExists(List<Control> controls, PasswordPolicyErrorType expectedErrorType)
|
throws DirectoryException
|
{
|
boolean found = false;
|
for(Control c : controls)
|
{
|
if (c.getOID().equals(OID_PASSWORD_POLICY_CONTROL))
|
{
|
PasswordPolicyResponseControl pwpControl;
|
if(c instanceof LDAPControl)
|
{
|
pwpControl = PasswordPolicyResponseControl.DECODER.decode(c.isCritical(), ((LDAPControl) c).getValue());
|
}
|
else
|
{
|
pwpControl = (PasswordPolicyResponseControl)c;
|
}
|
assertEquals(pwpControl.getErrorType(), expectedErrorType);
|
found = true;
|
}
|
}
|
return found;
|
}
|
|
|
/**
|
* Tests that an appropriate password policy response control is returned for
|
* an add operation in which the proposed password fails validation.
|
*
|
* @throws Exception If an unexpected problem occurs.
|
*/
|
@Test
|
public void testAddPasswordFailsValidation()
|
throws Exception
|
{
|
TestCaseUtils.initializeTestBackend(true);
|
|
setPasswordPolicyProp("--add", "password-validator:Length-Based Password Validator");
|
|
Socket s = new Socket("127.0.0.1", TestCaseUtils.getServerLdapPort());
|
org.opends.server.tools.LDAPReader r = new org.opends.server.tools.LDAPReader(s);
|
org.opends.server.tools.LDAPWriter w = new org.opends.server.tools.LDAPWriter(s);
|
|
try
|
{
|
BindRequestProtocolOp bindRequest = new BindRequestProtocolOp(
|
ByteString.valueOf("cn=Directory Manager"), 3,
|
ByteString.valueOf("password"));
|
LDAPMessage message = new LDAPMessage(1, bindRequest);
|
w.writeMessage(message);
|
|
message = r.readMessage();
|
BindResponseProtocolOp bindResponse = message.getBindResponseProtocolOp();
|
assertEquals(bindResponse.getResultCode(), LDAPResultCode.SUCCESS);
|
|
|
ArrayList<RawAttribute> rawAttrs = new ArrayList<RawAttribute>();
|
rawAttrs.add(RawAttribute.create("objectClass", "inetOrgPerson"));
|
rawAttrs.add(RawAttribute.create("uid", "test.user"));
|
rawAttrs.add(RawAttribute.create("givenName", "Test"));
|
rawAttrs.add(RawAttribute.create("sn", "User"));
|
rawAttrs.add(RawAttribute.create("cn", "Test User"));
|
rawAttrs.add(RawAttribute.create("userPassword", "short"));
|
|
AddRequestProtocolOp addRequest = new AddRequestProtocolOp(
|
ByteString.valueOf("ou=uid=test.user,o=test"), rawAttrs);
|
|
List<Control> controls = new ArrayList<Control>();
|
controls.add(new LDAPControl(OID_PASSWORD_POLICY_CONTROL, true));
|
|
message = new LDAPMessage(2, addRequest, controls);
|
w.writeMessage(message);
|
|
message = r.readMessage();
|
AddResponseProtocolOp addResponse = message.getAddResponseProtocolOp();
|
assertNotEquals(addResponse.getResultCode(), LDAPResultCode.SUCCESS);
|
|
controls = message.getControls();
|
assertNotNull(controls);
|
assertFalse(controls.isEmpty());
|
|
assertTrue(passwordPolicyControlExists(controls, PasswordPolicyErrorType.INSUFFICIENT_PASSWORD_QUALITY));
|
}
|
finally
|
{
|
setPasswordPolicyProp("--remove", "password-validator:Length-Based Password Validator");
|
|
StaticUtils.close(s);
|
}
|
}
|
|
|
|
/**
|
* Tests that an appropriate password policy response control is returned for
|
* a bind operation in which the user's account is locked due to
|
* authentication failures.
|
*
|
* @throws Exception If an unexpected problem occurs.
|
*/
|
@Test
|
public void testBindLockedDueToFailures()
|
throws Exception
|
{
|
TestCaseUtils.initializeTestBackend(true);
|
|
setPasswordPolicyProp("--set", "lockout-failure-count:3");
|
|
TestCaseUtils.addEntry(
|
"dn: uid=test.user,o=test",
|
"objectClass: top",
|
"objectClass: person",
|
"objectClass: organizationalPerson",
|
"objectClass: inetOrgPerson",
|
"uid: test.user",
|
"givenName: Test",
|
"sn: User",
|
"cn: Test User",
|
"userPassword: password",
|
"ds-privilege-name: bypass-acl");
|
|
Socket s = new Socket("127.0.0.1", TestCaseUtils.getServerLdapPort());
|
org.opends.server.tools.LDAPReader r = new org.opends.server.tools.LDAPReader(s);
|
org.opends.server.tools.LDAPWriter w = new org.opends.server.tools.LDAPWriter(s);
|
|
try
|
{
|
BindRequestProtocolOp bindRequest = new BindRequestProtocolOp(
|
ByteString.valueOf("uid=test.user,o=test"), 3,
|
ByteString.valueOf("wrong"));
|
|
for (int i=1; i <= 3; i++)
|
{
|
LDAPMessage message = new LDAPMessage(1, bindRequest);
|
w.writeMessage(message);
|
|
message = r.readMessage();
|
BindResponseProtocolOp bindResponse = message.getBindResponseProtocolOp();
|
assertNotEquals(bindResponse.getResultCode(), LDAPResultCode.SUCCESS);
|
}
|
|
bindRequest = new BindRequestProtocolOp(
|
ByteString.valueOf("uid=test.user,o=test"), 3,
|
ByteString.valueOf("password"));
|
|
List<Control> controls = new ArrayList<Control>();
|
controls.add(new LDAPControl(OID_PASSWORD_POLICY_CONTROL, true));
|
|
LDAPMessage message = new LDAPMessage(4, bindRequest, controls);
|
w.writeMessage(message);
|
|
message = r.readMessage();
|
BindResponseProtocolOp bindResponse = message.getBindResponseProtocolOp();
|
assertNotEquals(bindResponse.getResultCode(), LDAPResultCode.SUCCESS);
|
|
controls = message.getControls();
|
assertNotNull(controls);
|
assertFalse(controls.isEmpty());
|
|
assertTrue(passwordPolicyControlExists(controls, PasswordPolicyErrorType.ACCOUNT_LOCKED));
|
}
|
finally
|
{
|
setPasswordPolicyProp("--set", "lockout-failure-count:0");
|
|
StaticUtils.close(s);
|
}
|
}
|
|
|
/**
|
* Tests that an appropriate password policy response control is returned for
|
* a compare operation when the user's password is in a "must change" state.
|
*
|
* @throws Exception If an unexpected problem occurs.
|
*/
|
@Test
|
public void testCompareMustChange()
|
throws Exception
|
{
|
TestCaseUtils.initializeTestBackend(true);
|
|
setPasswordPolicyProp("--set", "force-change-on-add:true");
|
|
TestCaseUtils.addEntry(
|
"dn: uid=test.user,o=test",
|
"objectClass: top",
|
"objectClass: person",
|
"objectClass: organizationalPerson",
|
"objectClass: inetOrgPerson",
|
"uid: test.user",
|
"givenName: Test",
|
"sn: User",
|
"cn: Test User",
|
"userPassword: password",
|
"ds-privilege-name: bypass-acl");
|
|
Socket s = new Socket("127.0.0.1", TestCaseUtils.getServerLdapPort());
|
org.opends.server.tools.LDAPReader r = new org.opends.server.tools.LDAPReader(s);
|
org.opends.server.tools.LDAPWriter w = new org.opends.server.tools.LDAPWriter(s);
|
|
try
|
{
|
BindRequestProtocolOp bindRequest = new BindRequestProtocolOp(
|
ByteString.valueOf("uid=test.user,o=test"), 3,
|
ByteString.valueOf("password"));
|
LDAPMessage message = new LDAPMessage(1, bindRequest);
|
w.writeMessage(message);
|
|
message = r.readMessage();
|
BindResponseProtocolOp bindResponse = message.getBindResponseProtocolOp();
|
assertEquals(bindResponse.getResultCode(), LDAPResultCode.SUCCESS);
|
|
|
CompareRequestProtocolOp compareRequest =
|
new CompareRequestProtocolOp(ByteString.valueOf("o=test"), "o",
|
ByteString.valueOf("test"));
|
|
List<Control> controls = new ArrayList<Control>();
|
controls.add(new LDAPControl(OID_PASSWORD_POLICY_CONTROL, true));
|
|
message = new LDAPMessage(2, compareRequest, controls);
|
w.writeMessage(message);
|
|
message = r.readMessage();
|
CompareResponseProtocolOp compareResponse =
|
message.getCompareResponseProtocolOp();
|
assertNotEquals(compareResponse.getResultCode(), LDAPResultCode.SUCCESS);
|
|
controls = message.getControls();
|
assertNotNull(controls);
|
assertFalse(controls.isEmpty());
|
|
assertTrue(passwordPolicyControlExists(controls, PasswordPolicyErrorType.CHANGE_AFTER_RESET));
|
}
|
finally
|
{
|
setPasswordPolicyProp("--set", "force-change-on-add:false");
|
|
StaticUtils.close(s);
|
}
|
}
|
|
|
|
/**
|
* Tests that an appropriate password policy response control is returned for
|
* a delete operation when the user's password is in a "must change" state.
|
*
|
* @throws Exception If an unexpected problem occurs.
|
*/
|
@Test
|
public void testDeleteMustChange()
|
throws Exception
|
{
|
TestCaseUtils.initializeTestBackend(true);
|
|
setPasswordPolicyProp("--set", "force-change-on-add:true");
|
|
TestCaseUtils.addEntries(
|
"dn: uid=test.user,o=test",
|
"objectClass: top",
|
"objectClass: person",
|
"objectClass: organizationalPerson",
|
"objectClass: inetOrgPerson",
|
"uid: test.user",
|
"givenName: Test",
|
"sn: User",
|
"cn: Test User",
|
"userPassword: password",
|
"ds-privilege-name: bypass-acl",
|
"",
|
"dn: ou=People,o=test",
|
"objectClass: top",
|
"objectClass: organizationalUnit",
|
"ou: People");
|
|
Socket s = new Socket("127.0.0.1", TestCaseUtils.getServerLdapPort());
|
org.opends.server.tools.LDAPReader r = new org.opends.server.tools.LDAPReader(s);
|
org.opends.server.tools.LDAPWriter w = new org.opends.server.tools.LDAPWriter(s);
|
|
try
|
{
|
BindRequestProtocolOp bindRequest = new BindRequestProtocolOp(
|
ByteString.valueOf("uid=test.user,o=test"), 3,
|
ByteString.valueOf("password"));
|
LDAPMessage message = new LDAPMessage(1, bindRequest);
|
w.writeMessage(message);
|
|
message = r.readMessage();
|
BindResponseProtocolOp bindResponse = message.getBindResponseProtocolOp();
|
assertEquals(bindResponse.getResultCode(), LDAPResultCode.SUCCESS);
|
|
|
DeleteRequestProtocolOp deleteRequest =
|
new DeleteRequestProtocolOp(ByteString.valueOf("ou=People,o=test"));
|
|
List<Control> controls = new ArrayList<Control>();
|
controls.add(new LDAPControl(OID_PASSWORD_POLICY_CONTROL, true));
|
|
message = new LDAPMessage(2, deleteRequest, controls);
|
w.writeMessage(message);
|
|
message = r.readMessage();
|
DeleteResponseProtocolOp deleteResponse =
|
message.getDeleteResponseProtocolOp();
|
assertNotEquals(deleteResponse.getResultCode(), LDAPResultCode.SUCCESS);
|
|
controls = message.getControls();
|
assertNotNull(controls);
|
assertFalse(controls.isEmpty());
|
|
assertTrue(passwordPolicyControlExists(controls, PasswordPolicyErrorType.CHANGE_AFTER_RESET));
|
}
|
finally
|
{
|
setPasswordPolicyProp("--set", "force-change-on-add:false");
|
|
StaticUtils.close(s);
|
}
|
}
|
|
|
|
|
/**
|
* Creates test data for testModifyMustChange.
|
*
|
* Fields:
|
* <userDN> <entryDN> <changeAfterReset>
|
*
|
* @return Returns test data for testModifyMustChange.
|
*/
|
@DataProvider(name = "testModifyMustChange")
|
public Object[][] createTestModifyMustChange() {
|
return new Object[][] {
|
// User does not need to change their password.
|
{ "uid=test.admin,o=test", "uid=test.admin,o=test", false },
|
{ "uid=test.admin,o=test", "uid=test.user,o=test", false },
|
{ "uid=test.admin,o=test", "o=test", false },
|
|
// User does need to change their password.
|
{ "uid=test.user,o=test", "uid=test.admin,o=test", true },
|
{ "uid=test.user,o=test", "uid=test.user,o=test", true },
|
{ "uid=test.user,o=test", "o=test", true }
|
};
|
}
|
|
|
|
/**
|
* Tests that an appropriate password policy response control is
|
* returned for a modify operation when the user's password is in a
|
* "must change" state.
|
*
|
* @param userDN
|
* The name of the user to bind as.
|
* @param entryDN
|
* The name of the entry to modify.
|
* @param changeAfterReset
|
* {@code true} if change after reset is expected.
|
* @throws Exception
|
* If an unexpected problem occurs.
|
*/
|
@Test(dataProvider="testModifyMustChange")
|
public void testModifyMustChange(String userDN, String entryDN, boolean changeAfterReset)
|
throws Exception
|
{
|
TestCaseUtils.initializeTestBackend(true);
|
|
TestCaseUtils.addEntry(
|
"dn: uid=test.admin,o=test",
|
"objectClass: top",
|
"objectClass: person",
|
"objectClass: organizationalPerson",
|
"objectClass: inetOrgPerson",
|
"uid: test.admin",
|
"givenName: Test Admin",
|
"sn: Admin",
|
"cn: Test Admin",
|
"userPassword: password",
|
"ds-privilege-name: bypass-acl");
|
|
setPasswordPolicyProp("--set", "force-change-on-add:true");
|
|
TestCaseUtils.addEntry(
|
"dn: uid=test.user,o=test",
|
"objectClass: top",
|
"objectClass: person",
|
"objectClass: organizationalPerson",
|
"objectClass: inetOrgPerson",
|
"uid: test.user",
|
"givenName: Test",
|
"sn: User",
|
"cn: Test User",
|
"userPassword: password",
|
"ds-privilege-name: bypass-acl");
|
|
Socket s = new Socket("127.0.0.1", TestCaseUtils.getServerLdapPort());
|
org.opends.server.tools.LDAPReader r = new org.opends.server.tools.LDAPReader(s);
|
org.opends.server.tools.LDAPWriter w = new org.opends.server.tools.LDAPWriter(s);
|
|
try
|
{
|
BindRequestProtocolOp bindRequest = new BindRequestProtocolOp(
|
ByteString.valueOf(userDN), 3,
|
ByteString.valueOf("password"));
|
LDAPMessage message = new LDAPMessage(1, bindRequest);
|
w.writeMessage(message);
|
|
message = r.readMessage();
|
BindResponseProtocolOp bindResponse = message.getBindResponseProtocolOp();
|
assertEquals(bindResponse.getResultCode(), LDAPResultCode.SUCCESS);
|
|
|
ArrayList<RawModification> mods = new ArrayList<RawModification>();
|
mods.add(RawModification.create(ModificationType.REPLACE, "description",
|
"foo"));
|
|
ModifyRequestProtocolOp modifyRequest =
|
new ModifyRequestProtocolOp(ByteString.valueOf(entryDN), mods);
|
|
List<Control> controls = new ArrayList<Control>();
|
controls.add(new LDAPControl(OID_PASSWORD_POLICY_CONTROL, true));
|
|
message = new LDAPMessage(2, modifyRequest, controls);
|
w.writeMessage(message);
|
|
message = r.readMessage();
|
ModifyResponseProtocolOp modifyResponse =
|
message.getModifyResponseProtocolOp();
|
|
if (changeAfterReset)
|
{
|
assertEquals(modifyResponse.getResultCode(),
|
LDAPResultCode.CONSTRAINT_VIOLATION);
|
}
|
else
|
{
|
assertEquals(modifyResponse.getResultCode(),
|
LDAPResultCode.SUCCESS);
|
}
|
|
controls = message.getControls();
|
assertNotNull(controls);
|
assertFalse(controls.isEmpty());
|
|
boolean found = false;
|
for(Control c : controls)
|
{
|
if (c.getOID().equals(OID_PASSWORD_POLICY_CONTROL))
|
{
|
PasswordPolicyResponseControl pwpControl;
|
if(c instanceof LDAPControl)
|
{
|
pwpControl =
|
PasswordPolicyResponseControl.DECODER.decode(c.isCritical(), ((LDAPControl)c).getValue());
|
}
|
else
|
{
|
pwpControl = (PasswordPolicyResponseControl)c;
|
}
|
if (changeAfterReset) {
|
assertEquals(pwpControl.getErrorType(),
|
PasswordPolicyErrorType.CHANGE_AFTER_RESET);
|
} else {
|
assertNull(pwpControl.getErrorType());
|
}
|
found = true;
|
}
|
}
|
assertTrue(found);
|
}
|
finally
|
{
|
setPasswordPolicyProp("--set", "force-change-on-add:false");
|
|
StaticUtils.close(s);
|
}
|
}
|
|
|
|
/**
|
* Tests that an appropriate password policy response control is
|
* returned for a modify operation when the authorized user is forced to
|
* change their own password before changing a different entry.
|
*
|
* @throws Exception
|
* If an unexpected problem occurs.
|
*/
|
@Test
|
public void testAuthzModifyMustChange()
|
throws Exception
|
{
|
TestCaseUtils.initializeTestBackend(true);
|
|
String userDN = "uid=test.admin,o=test";
|
String entryDN = "uid=test.user,o=test";
|
String authzDN = "uid=authz.user,o=test";
|
|
TestCaseUtils.addEntry(
|
"dn: uid=test.admin,o=test",
|
"objectClass: top",
|
"objectClass: person",
|
"objectClass: organizationalPerson",
|
"objectClass: inetOrgPerson",
|
"uid: test.admin",
|
"givenName: Test Admin",
|
"sn: Admin",
|
"cn: Test Admin",
|
"userPassword: password",
|
"ds-privilege-name: bypass-acl",
|
"ds-privilege-name: proxied-auth");
|
|
TestCaseUtils.addEntry(
|
"dn: uid=test.user,o=test",
|
"objectClass: top",
|
"objectClass: person",
|
"objectClass: organizationalPerson",
|
"objectClass: inetOrgPerson",
|
"uid: test.user",
|
"givenName: Test",
|
"sn: User",
|
"cn: Test User",
|
"userPassword: password",
|
"ds-privilege-name: bypass-acl");
|
|
setPasswordPolicyProp("--set", "force-change-on-add:true");
|
|
TestCaseUtils.addEntry(
|
"dn: uid=authz.user,o=test",
|
"objectClass: top",
|
"objectClass: person",
|
"objectClass: organizationalPerson",
|
"objectClass: inetOrgPerson",
|
"uid: authz.user",
|
"givenName: Authz",
|
"sn: User",
|
"cn: Authz User",
|
"userPassword: password",
|
"ds-privilege-name: bypass-acl");
|
|
Socket s = new Socket("127.0.0.1", TestCaseUtils.getServerLdapPort());
|
org.opends.server.tools.LDAPReader r = new org.opends.server.tools.LDAPReader(s);
|
org.opends.server.tools.LDAPWriter w = new org.opends.server.tools.LDAPWriter(s);
|
|
try
|
{
|
BindRequestProtocolOp bindRequest = new BindRequestProtocolOp(
|
ByteString.valueOf(userDN), 3,
|
ByteString.valueOf("password"));
|
LDAPMessage message = new LDAPMessage(1, bindRequest);
|
w.writeMessage(message);
|
|
message = r.readMessage();
|
BindResponseProtocolOp bindResponse = message.getBindResponseProtocolOp();
|
assertEquals(bindResponse.getResultCode(), LDAPResultCode.SUCCESS);
|
|
|
ArrayList<RawModification> mods = new ArrayList<RawModification>();
|
mods.add(RawModification.create(ModificationType.REPLACE, "description",
|
"foo"));
|
|
ModifyRequestProtocolOp modifyRequest =
|
new ModifyRequestProtocolOp(ByteString.valueOf(entryDN), mods);
|
|
List<Control> controls = new ArrayList<Control>();
|
controls.add(new LDAPControl(OID_PASSWORD_POLICY_CONTROL, true));
|
controls.add(new LDAPControl(OID_PROXIED_AUTH_V2, true,
|
ByteString.valueOf("dn:" + authzDN)));
|
|
message = new LDAPMessage(2, modifyRequest, controls);
|
w.writeMessage(message);
|
|
message = r.readMessage();
|
ModifyResponseProtocolOp modifyResponse =
|
message.getModifyResponseProtocolOp();
|
|
assertEquals(modifyResponse.getResultCode(),
|
LDAPResultCode.CONSTRAINT_VIOLATION);
|
|
controls = message.getControls();
|
assertNotNull(controls);
|
assertFalse(controls.isEmpty());
|
|
assertTrue(passwordPolicyControlExists(controls, PasswordPolicyErrorType.CHANGE_AFTER_RESET));
|
}
|
finally
|
{
|
setPasswordPolicyProp("--set", "force-change-on-add:false");
|
|
StaticUtils.close(s);
|
}
|
}
|
|
|
|
/**
|
* Tests that an appropriate password policy response control is returned for
|
* a modify operation when users do not have permission to change their own
|
* passwords.
|
*
|
* @throws Exception If an unexpected problem occurs.
|
*/
|
@Test
|
public void testModifyCannotChange()
|
throws Exception
|
{
|
TestCaseUtils.initializeTestBackend(true);
|
|
setPasswordPolicyProp("--set", "allow-user-password-changes:false");
|
|
TestCaseUtils.addEntry(
|
"dn: uid=test.user,o=test",
|
"objectClass: top",
|
"objectClass: person",
|
"objectClass: organizationalPerson",
|
"objectClass: inetOrgPerson",
|
"uid: test.user",
|
"givenName: Test",
|
"sn: User",
|
"cn: Test User",
|
"userPassword: password",
|
"ds-privilege-name: bypass-acl");
|
|
Socket s = new Socket("127.0.0.1", TestCaseUtils.getServerLdapPort());
|
org.opends.server.tools.LDAPReader r = new org.opends.server.tools.LDAPReader(s);
|
org.opends.server.tools.LDAPWriter w = new org.opends.server.tools.LDAPWriter(s);
|
|
try
|
{
|
BindRequestProtocolOp bindRequest = new BindRequestProtocolOp(
|
ByteString.valueOf("uid=test.user,o=test"), 3,
|
ByteString.valueOf("password"));
|
LDAPMessage message = new LDAPMessage(1, bindRequest);
|
w.writeMessage(message);
|
|
message = r.readMessage();
|
BindResponseProtocolOp bindResponse = message.getBindResponseProtocolOp();
|
assertEquals(bindResponse.getResultCode(), LDAPResultCode.SUCCESS);
|
|
|
ArrayList<RawModification> mods = new ArrayList<RawModification>();
|
mods.add(RawModification.create(ModificationType.REPLACE, "userPassword",
|
"newpassword"));
|
|
ModifyRequestProtocolOp modifyRequest =
|
new ModifyRequestProtocolOp(
|
ByteString.valueOf("uid=test.user,o=test"), mods);
|
|
List<Control> controls = new ArrayList<Control>();
|
controls.add(new LDAPControl(OID_PASSWORD_POLICY_CONTROL, true));
|
|
message = new LDAPMessage(2, modifyRequest, controls);
|
w.writeMessage(message);
|
|
message = r.readMessage();
|
ModifyResponseProtocolOp modifyResponse =
|
message.getModifyResponseProtocolOp();
|
assertNotEquals(modifyResponse.getResultCode(), LDAPResultCode.SUCCESS);
|
|
controls = message.getControls();
|
assertNotNull(controls);
|
assertFalse(controls.isEmpty());
|
|
assertTrue(passwordPolicyControlExists(controls, PasswordPolicyErrorType.PASSWORD_MOD_NOT_ALLOWED));
|
}
|
finally
|
{
|
setPasswordPolicyProp("--set", "allow-user-password-changes:true");
|
|
StaticUtils.close(s);
|
}
|
}
|
|
/**
|
* Tests that an appropriate password policy response control is returned for
|
* a modify operation when the proposed password is in the user's password
|
* history.
|
*
|
* @throws Exception If an unexpected problem occurs.
|
*/
|
@Test
|
public void testModifyPasswordInHistory()
|
throws Exception
|
{
|
TestCaseUtils.initializeTestBackend(true);
|
|
setPasswordPolicyProp("--set", "password-history-count:5");
|
|
TestCaseUtils.addEntry(
|
"dn: uid=test.user,o=test",
|
"objectClass: top",
|
"objectClass: person",
|
"objectClass: organizationalPerson",
|
"objectClass: inetOrgPerson",
|
"uid: test.user",
|
"givenName: Test",
|
"sn: User",
|
"cn: Test User",
|
"userPassword: password",
|
"ds-privilege-name: bypass-acl");
|
|
Socket s = new Socket("127.0.0.1", TestCaseUtils.getServerLdapPort());
|
org.opends.server.tools.LDAPReader r = new org.opends.server.tools.LDAPReader(s);
|
org.opends.server.tools.LDAPWriter w = new org.opends.server.tools.LDAPWriter(s);
|
|
try
|
{
|
BindRequestProtocolOp bindRequest = new BindRequestProtocolOp(
|
ByteString.valueOf("uid=test.user,o=test"), 3,
|
ByteString.valueOf("password"));
|
LDAPMessage message = new LDAPMessage(1, bindRequest);
|
w.writeMessage(message);
|
|
message = r.readMessage();
|
BindResponseProtocolOp bindResponse = message.getBindResponseProtocolOp();
|
assertEquals(bindResponse.getResultCode(), LDAPResultCode.SUCCESS);
|
|
|
ArrayList<RawModification> mods = new ArrayList<RawModification>();
|
mods.add(RawModification.create(ModificationType.REPLACE, "userPassword",
|
"password"));
|
|
ModifyRequestProtocolOp modifyRequest =
|
new ModifyRequestProtocolOp(
|
ByteString.valueOf("uid=test.user,o=test"), mods);
|
|
List<Control> controls = new ArrayList<Control>();
|
controls.add(new LDAPControl(OID_PASSWORD_POLICY_CONTROL, true));
|
|
message = new LDAPMessage(2, modifyRequest, controls);
|
w.writeMessage(message);
|
|
message = r.readMessage();
|
ModifyResponseProtocolOp modifyResponse =
|
message.getModifyResponseProtocolOp();
|
assertNotEquals(modifyResponse.getResultCode(), LDAPResultCode.SUCCESS);
|
|
controls = message.getControls();
|
assertNotNull(controls);
|
assertFalse(controls.isEmpty());
|
|
assertTrue(passwordPolicyControlExists(controls, PasswordPolicyErrorType.PASSWORD_IN_HISTORY));
|
}
|
finally
|
{
|
setPasswordPolicyProp("--set", "password-history-count:0");
|
|
StaticUtils.close(s);
|
}
|
}
|
|
|
|
/**
|
* Tests that an appropriate password policy response control is returned for
|
* a modify operation when the user didn't provide their current password when
|
* it was required.
|
*
|
* @throws Exception If an unexpected problem occurs.
|
*/
|
@Test
|
public void testModifyMissingCurrentPassword()
|
throws Exception
|
{
|
TestCaseUtils.initializeTestBackend(true);
|
|
setPasswordPolicyProp("--set", "password-change-requires-current-password:true");
|
|
TestCaseUtils.addEntry(
|
"dn: uid=test.user,o=test",
|
"objectClass: top",
|
"objectClass: person",
|
"objectClass: organizationalPerson",
|
"objectClass: inetOrgPerson",
|
"uid: test.user",
|
"givenName: Test",
|
"sn: User",
|
"cn: Test User",
|
"userPassword: password",
|
"ds-privilege-name: bypass-acl");
|
|
Socket s = new Socket("127.0.0.1", TestCaseUtils.getServerLdapPort());
|
org.opends.server.tools.LDAPReader r = new org.opends.server.tools.LDAPReader(s);
|
org.opends.server.tools.LDAPWriter w = new org.opends.server.tools.LDAPWriter(s);
|
|
try
|
{
|
BindRequestProtocolOp bindRequest = new BindRequestProtocolOp(
|
ByteString.valueOf("uid=test.user,o=test"), 3,
|
ByteString.valueOf("password"));
|
LDAPMessage message = new LDAPMessage(1, bindRequest);
|
w.writeMessage(message);
|
|
message = r.readMessage();
|
BindResponseProtocolOp bindResponse = message.getBindResponseProtocolOp();
|
assertEquals(bindResponse.getResultCode(), LDAPResultCode.SUCCESS);
|
|
|
ArrayList<RawModification> mods = new ArrayList<RawModification>();
|
mods.add(RawModification.create(ModificationType.REPLACE, "userPassword",
|
"newpassword"));
|
|
ModifyRequestProtocolOp modifyRequest =
|
new ModifyRequestProtocolOp(
|
ByteString.valueOf("uid=test.user,o=test"), mods);
|
|
List<Control> controls = new ArrayList<Control>();
|
controls.add(new LDAPControl(OID_PASSWORD_POLICY_CONTROL, true));
|
|
message = new LDAPMessage(2, modifyRequest, controls);
|
w.writeMessage(message);
|
|
message = r.readMessage();
|
ModifyResponseProtocolOp modifyResponse =
|
message.getModifyResponseProtocolOp();
|
assertNotEquals(modifyResponse.getResultCode(), LDAPResultCode.SUCCESS);
|
|
controls = message.getControls();
|
assertNotNull(controls);
|
assertFalse(controls.isEmpty());
|
|
assertTrue(passwordPolicyControlExists(controls, PasswordPolicyErrorType.MUST_SUPPLY_OLD_PASSWORD));
|
}
|
finally
|
{
|
setPasswordPolicyProp("--set", "password-change-requires-current-password:false");
|
|
StaticUtils.close(s);
|
}
|
}
|
|
|
|
/**
|
* Tests that an appropriate password policy response control is returned for
|
* a modify operation when the user tried to perform multiple password changes
|
* without respecting the minimum age.
|
*
|
* @throws Exception If an unexpected problem occurs.
|
*/
|
@Test
|
public void testModifyMinimumPasswordAge()
|
throws Exception
|
{
|
TestCaseUtils.initializeTestBackend(true);
|
|
setPasswordPolicyProp("--set", "min-password-age:24 hours");
|
|
TestCaseUtils.addEntry(
|
"dn: uid=test.user,o=test",
|
"objectClass: top",
|
"objectClass: person",
|
"objectClass: organizationalPerson",
|
"objectClass: inetOrgPerson",
|
"uid: test.user",
|
"givenName: Test",
|
"sn: User",
|
"cn: Test User",
|
"userPassword: password",
|
"ds-privilege-name: bypass-acl");
|
|
Socket s = new Socket("127.0.0.1", TestCaseUtils.getServerLdapPort());
|
org.opends.server.tools.LDAPReader r = new org.opends.server.tools.LDAPReader(s);
|
org.opends.server.tools.LDAPWriter w = new org.opends.server.tools.LDAPWriter(s);
|
|
try
|
{
|
BindRequestProtocolOp bindRequest = new BindRequestProtocolOp(
|
ByteString.valueOf("uid=test.user,o=test"), 3,
|
ByteString.valueOf("password"));
|
LDAPMessage message = new LDAPMessage(1, bindRequest);
|
w.writeMessage(message);
|
|
message = r.readMessage();
|
BindResponseProtocolOp bindResponse = message.getBindResponseProtocolOp();
|
assertEquals(bindResponse.getResultCode(), LDAPResultCode.SUCCESS);
|
|
|
ArrayList<RawModification> mods = new ArrayList<RawModification>();
|
mods.add(RawModification.create(ModificationType.REPLACE, "userPassword",
|
"newpassword"));
|
|
ModifyRequestProtocolOp modifyRequest =
|
new ModifyRequestProtocolOp(
|
ByteString.valueOf("uid=test.user,o=test"), mods);
|
|
List<Control> controls = new ArrayList<Control>();
|
controls.add(new LDAPControl(OID_PASSWORD_POLICY_CONTROL, true));
|
|
message = new LDAPMessage(2, modifyRequest, controls);
|
w.writeMessage(message);
|
|
message = r.readMessage();
|
ModifyResponseProtocolOp modifyResponse =
|
message.getModifyResponseProtocolOp();
|
assertNotEquals(modifyResponse.getResultCode(), LDAPResultCode.SUCCESS);
|
|
controls = message.getControls();
|
assertNotNull(controls);
|
assertFalse(controls.isEmpty());
|
|
assertTrue(passwordPolicyControlExists(controls, PasswordPolicyErrorType.PASSWORD_TOO_YOUNG));
|
}
|
finally
|
{
|
setPasswordPolicyProp("--set", "min-password-age:0 seconds");
|
|
StaticUtils.close(s);
|
}
|
}
|
|
|
|
/**
|
* Tests that an appropriate password policy response control is returned for
|
* a modify DN operation when the user's password is in a "must change" state.
|
*
|
* @throws Exception If an unexpected problem occurs.
|
*/
|
@Test
|
public void testModifyDNMustChange()
|
throws Exception
|
{
|
TestCaseUtils.initializeTestBackend(true);
|
|
setPasswordPolicyProp("--set", "force-change-on-add:true");
|
|
TestCaseUtils.addEntries(
|
"dn: uid=test.user,o=test",
|
"objectClass: top",
|
"objectClass: person",
|
"objectClass: organizationalPerson",
|
"objectClass: inetOrgPerson",
|
"uid: test.user",
|
"givenName: Test",
|
"sn: User",
|
"cn: Test User",
|
"userPassword: password",
|
"ds-privilege-name: bypass-acl",
|
"",
|
"dn: ou=People,o=test",
|
"objectClass: top",
|
"objectClass: organizationalUnit",
|
"ou: People");
|
|
Socket s = new Socket("127.0.0.1", TestCaseUtils.getServerLdapPort());
|
org.opends.server.tools.LDAPReader r = new org.opends.server.tools.LDAPReader(s);
|
org.opends.server.tools.LDAPWriter w = new org.opends.server.tools.LDAPWriter(s);
|
|
try
|
{
|
BindRequestProtocolOp bindRequest = new BindRequestProtocolOp(
|
ByteString.valueOf("uid=test.user,o=test"), 3,
|
ByteString.valueOf("password"));
|
LDAPMessage message = new LDAPMessage(1, bindRequest);
|
w.writeMessage(message);
|
|
message = r.readMessage();
|
BindResponseProtocolOp bindResponse = message.getBindResponseProtocolOp();
|
assertEquals(bindResponse.getResultCode(), LDAPResultCode.SUCCESS);
|
|
|
ModifyDNRequestProtocolOp modifyDNRequest =
|
new ModifyDNRequestProtocolOp(
|
ByteString.valueOf("ou=People,o=test"),
|
ByteString.valueOf("ou=Users"), true);
|
|
List<Control> controls = new ArrayList<Control>();
|
controls.add(new LDAPControl(OID_PASSWORD_POLICY_CONTROL, true));
|
|
message = new LDAPMessage(2, modifyDNRequest, controls);
|
w.writeMessage(message);
|
|
message = r.readMessage();
|
ModifyDNResponseProtocolOp modifyDNResponse =
|
message.getModifyDNResponseProtocolOp();
|
assertNotEquals(modifyDNResponse.getResultCode(), LDAPResultCode.SUCCESS);
|
|
controls = message.getControls();
|
assertNotNull(controls);
|
assertFalse(controls.isEmpty());
|
|
assertTrue(passwordPolicyControlExists(controls, PasswordPolicyErrorType.CHANGE_AFTER_RESET));
|
}
|
finally
|
{
|
setPasswordPolicyProp("--set", "force-change-on-add:false");
|
|
StaticUtils.close(s);
|
}
|
}
|
|
/**
|
* Tests that an appropriate password policy response control is returned for
|
* a search operation when the user's password is in a "must change" state.
|
*
|
* @throws Exception If an unexpected problem occurs.
|
*/
|
@Test
|
public void testSearchMustChange()
|
throws Exception
|
{
|
TestCaseUtils.initializeTestBackend(true);
|
|
setPasswordPolicyProp("--set", "force-change-on-add:true");
|
|
TestCaseUtils.addEntry(
|
"dn: uid=test.user,o=test",
|
"objectClass: top",
|
"objectClass: person",
|
"objectClass: organizationalPerson",
|
"objectClass: inetOrgPerson",
|
"uid: test.user",
|
"givenName: Test",
|
"sn: User",
|
"cn: Test User",
|
"userPassword: password",
|
"ds-privilege-name: bypass-acl");
|
|
Socket s = new Socket("127.0.0.1", TestCaseUtils.getServerLdapPort());
|
org.opends.server.tools.LDAPReader r = new org.opends.server.tools.LDAPReader(s);
|
org.opends.server.tools.LDAPWriter w = new org.opends.server.tools.LDAPWriter(s);
|
|
try
|
{
|
BindRequestProtocolOp bindRequest = new BindRequestProtocolOp(
|
ByteString.valueOf("uid=test.user,o=test"), 3,
|
ByteString.valueOf("password"));
|
LDAPMessage message = new LDAPMessage(1, bindRequest);
|
w.writeMessage(message);
|
|
message = r.readMessage();
|
BindResponseProtocolOp bindResponse = message.getBindResponseProtocolOp();
|
assertEquals(bindResponse.getResultCode(), LDAPResultCode.SUCCESS);
|
|
|
SearchRequestProtocolOp searchRequest =
|
new SearchRequestProtocolOp(ByteString.valueOf("o=test"),
|
SearchScope.BASE_OBJECT,
|
DereferenceAliasesPolicy.NEVER, 0, 0, false,
|
LDAPFilter.objectClassPresent(),
|
new LinkedHashSet<String>());
|
|
List<Control> controls = new ArrayList<Control>();
|
controls.add(new LDAPControl(OID_PASSWORD_POLICY_CONTROL, true));
|
|
message = new LDAPMessage(2, searchRequest, controls);
|
w.writeMessage(message);
|
|
message = r.readMessage();
|
SearchResultDoneProtocolOp searchDone =
|
message.getSearchResultDoneProtocolOp();
|
assertNotEquals(searchDone.getResultCode(), LDAPResultCode.SUCCESS);
|
|
controls = message.getControls();
|
assertNotNull(controls);
|
assertFalse(controls.isEmpty());
|
|
assertTrue(passwordPolicyControlExists(controls, PasswordPolicyErrorType.CHANGE_AFTER_RESET));
|
}
|
finally
|
{
|
setPasswordPolicyProp("--set", "force-change-on-add:false");
|
|
StaticUtils.close(s);
|
}
|
}
|
|
private void setPasswordPolicyProp(String arg, String value)
|
{
|
TestCaseUtils.dsconfig("set-password-policy-prop", "--policy-name", "Default Password Policy", arg, value);
|
}
|
}
|