/*
* 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 Sun Microsystems, Inc.
* Portions Copyright 2014-2015 ForgeRock AS
*/
package org.opends.server.admin.server;
import java.util.List;
import javax.naming.OperationNotSupportedException;
import javax.naming.ldap.LdapName;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.opendj.config.server.ConfigException;
import org.forgerock.opendj.ldap.ResultCode;
import org.opends.server.TestCaseUtils;
import org.opends.server.admin.AdminTestCase;
import org.opends.server.admin.TestCfg;
import org.opends.server.admin.TestChildCfg;
import org.opends.server.admin.TestChildCfgDefn;
import org.opends.server.admin.TestParentCfg;
import org.opends.server.admin.client.ldap.JNDIDirContextAdaptor;
import org.opends.server.admin.std.server.RootCfg;
import org.opends.server.core.AddOperation;
import org.forgerock.opendj.config.server.ConfigChangeResult;
import org.opends.server.types.Entry;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import static org.opends.server.protocols.internal.InternalClientConnection.*;
import static org.testng.Assert.*;
/**
* Test cases for constraints on the server-side.
*/
public final class ConstraintTest extends AdminTestCase {
/** Child DN. */
private static final String TEST_CHILD_1_DN = "cn=test child 1,cn=test children,cn=test parent 1,cn=test parents,cn=config";
/**
* A test child add listener.
*/
private static class AddListener implements
ConfigurationAddListener<TestChildCfg> {
/** {@inheritDoc} */
@Override
public ConfigChangeResult applyConfigurationAdd(TestChildCfg configuration) {
return new ConfigChangeResult();
}
/** {@inheritDoc} */
@Override
public boolean isConfigurationAddAcceptable(TestChildCfg configuration,
List<LocalizableMessage> unacceptableReasons) {
return true;
}
}
/**
* A test child delete listener.
*/
private static class DeleteListener implements
ConfigurationDeleteListener<TestChildCfg> {
/** {@inheritDoc} */
@Override
public ConfigChangeResult applyConfigurationDelete(
TestChildCfg configuration) {
return new ConfigChangeResult();
}
/** {@inheritDoc} */
@Override
public boolean isConfigurationDeleteAcceptable(TestChildCfg configuration,
List<LocalizableMessage> unacceptableReasons) {
return true;
}
}
/**
* A test child change listener.
*/
private static class ChangeListener implements
ConfigurationChangeListener<TestChildCfg> {
/** {@inheritDoc} */
@Override
public ConfigChangeResult applyConfigurationChange(
TestChildCfg configuration) {
return new ConfigChangeResult();
}
/** {@inheritDoc} */
@Override
public boolean isConfigurationChangeAcceptable(TestChildCfg configuration,
List<LocalizableMessage> unacceptableReasons) {
return true;
}
}
/** Test child 1 LDIF. */
private static final String[] TEST_CHILD_1 = new String[] {
"dn: cn=test child 1,cn=test children,cn=test parent 1,cn=test parents,cn=config",
"objectclass: top",
"objectclass: ds-cfg-test-child-dummy",
"cn: test child 1",
"ds-cfg-enabled: true",
"ds-cfg-java-class: org.opends.server.extensions.UserDefinedVirtualAttributeProvider",
"ds-cfg-attribute-type: description",
"ds-cfg-conflict-behavior: virtual-overrides-real"
};
/** Test LDIF. */
private static final String[] TEST_LDIF = new String[] {
// Base entries.
"dn: cn=test parents,cn=config",
"objectclass: top",
"objectclass: ds-cfg-branch",
"cn: test parents",
"",
// Parent 1 - uses default values for
// optional-multi-valued-dn-property.
"dn: cn=test parent 1,cn=test parents,cn=config",
"objectclass: top",
"objectclass: ds-cfg-test-parent-dummy",
"cn: test parent 1",
"ds-cfg-enabled: true",
"ds-cfg-java-class: org.opends.server.extensions.UserDefinedVirtualAttributeProvider",
"ds-cfg-attribute-type: description",
"ds-cfg-conflict-behavior: virtual-overrides-real",
"",
// Child base entries.
"dn:cn=test children,cn=test parent 1,cn=test parents,cn=config",
"objectclass: top",
"objectclass: ds-cfg-branch",
"cn: test children",
"",
};
/** JNDI LDAP context. */
private JNDIDirContextAdaptor adaptor;
/**
* Sets up tests
*
* @throws Exception
* If the server could not be initialized.
*/
@BeforeClass
public void setUp() throws Exception {
// This test suite depends on having the schema available, so
// we'll start the server.
TestCaseUtils.startServer();
TestCfg.setUp();
// Add test managed objects.
TestCaseUtils.addEntries(TEST_LDIF);
}
/**
* Tears down test environment.
*
* @throws Exception
* If the test entries could not be removed.
*/
@AfterClass
public void tearDown() throws Exception {
TestCfg.cleanup();
// Remove test entries.
deleteSubtree("cn=test parents,cn=config");
}
/**
* Tests that retrieval can succeed.
*
* @throws Exception
* If the test unexpectedly fails.
*/
@Test
public void testGetManagedObjectSuccess() throws Exception {
MockConstraint constraint = new MockConstraint(true, false);
try {
TestCaseUtils.addEntry(TEST_CHILD_1);
TestCfg.addConstraint(constraint);
TestParentCfg parent = getParent("test parent 1");
parent.getTestChild("test child 1");
} finally {
TestCfg.removeConstraint(constraint);
deleteSubtreeNoException(TEST_CHILD_1_DN);
}
}
/**
* Tests that retrieval can fail.
*
* @throws Exception
* If the test unexpectedly fails.
*/
@Test
public void testGetManagedObjectFail() throws Exception {
MockConstraint constraint = new MockConstraint(false, true);
try {
TestCaseUtils.addEntry(TEST_CHILD_1);
TestCfg.addConstraint(constraint);
TestParentCfg parent = getParent("test parent 1");
parent.getTestChild("test child 1");
} catch (ConfigException e) {
Throwable cause = e.getCause();
if (cause instanceof ConstraintViolationException) {
ConstraintViolationException cve = (ConstraintViolationException) cause;
Assert.assertEquals(cve.getMessages().size(), 1);
Assert.assertSame(cve.getManagedObject().getManagedObjectDefinition(),
TestChildCfgDefn.getInstance());
} else {
// Wrong cause.
throw e;
}
} finally {
TestCfg.removeConstraint(constraint);
deleteSubtreeNoException(TEST_CHILD_1_DN);
}
}
/**
* Tests that an add constraint can succeed.
*
* @throws Exception
* If the test unexpectedly fails.
*/
@Test
public void testAddConstraintSuccess() throws Exception {
TestParentCfg parent = getParent("test parent 1");
AddListener listener = new AddListener();
parent.addTestChildAddListener(listener);
MockConstraint constraint = new MockConstraint(true, false);
TestCfg.addConstraint(constraint);
try {
try {
// Add the entry.
addEntry(ResultCode.SUCCESS, TEST_CHILD_1);
} finally {
deleteSubtreeNoException(TEST_CHILD_1_DN);
}
} finally {
TestCfg.removeConstraint(constraint);
parent.removeTestChildAddListener(listener);
}
}
/**
* Tests that an add constraint can fail.
*
* @throws Exception
* If the test unexpectedly fails.
*/
@Test
public void testAddConstraintFail() throws Exception {
TestParentCfg parent = getParent("test parent 1");
AddListener listener = new AddListener();
parent.addTestChildAddListener(listener);
MockConstraint constraint = new MockConstraint(false, true);
TestCfg.addConstraint(constraint);
try {
try {
// Add the entry.
addEntry(ResultCode.UNWILLING_TO_PERFORM, TEST_CHILD_1);
} finally {
deleteSubtreeNoException(TEST_CHILD_1_DN);
}
} finally {
TestCfg.removeConstraint(constraint);
parent.removeTestChildAddListener(listener);
}
}
/**
* Tests that a delete constraint can succeed.
*
* @throws Exception
* If the test unexpectedly fails.
*/
@Test
public void testDeleteConstraintSuccess() throws Exception {
TestParentCfg parent = getParent("test parent 1");
DeleteListener listener = new DeleteListener();
parent.addTestChildDeleteListener(listener);
MockConstraint constraint = new MockConstraint(false, true);
TestCfg.addConstraint(constraint);
try {
// Add the entry.
TestCaseUtils.addEntry(TEST_CHILD_1);
// Now delete it - this should trigger the constraint.
deleteSubtree(TEST_CHILD_1_DN);
} finally {
TestCfg.removeConstraint(constraint);
parent.removeTestChildDeleteListener(listener);
deleteSubtreeNoException(TEST_CHILD_1_DN);
}
}
/**
* Tests that a delete constraint can fail.
*
* @throws Exception
* If the test unexpectedly fails.
*/
@Test
public void testDeleteConstraintFail() throws Exception {
TestParentCfg parent = getParent("test parent 1");
DeleteListener listener = new DeleteListener();
parent.addTestChildDeleteListener(listener);
MockConstraint constraint = new MockConstraint(true, false);
TestCfg.addConstraint(constraint);
try {
// Add the entry.
TestCaseUtils.addEntry(TEST_CHILD_1);
try {
// Now delete it - this should trigger the constraint.
deleteSubtree(TEST_CHILD_1_DN);
// Should not have succeeded.
Assert.fail("Delete constraint failed to prevent deletion");
} catch (OperationNotSupportedException e) {
// Ignore - this is the expected exception.
}
} finally {
TestCfg.removeConstraint(constraint);
parent.removeTestChildDeleteListener(listener);
deleteSubtreeNoException(TEST_CHILD_1_DN);
}
}
/**
* Tests that a modify constraint can succeed.
*
* @throws Exception
* If the test unexpectedly fails.
*/
@Test
public void testChangeConstraintSuccess() throws Exception {
TestParentCfg parent = getParent("test parent 1");
MockConstraint constraint = new MockConstraint(true, false);
try {
// Add the entry.
TestCaseUtils.addEntry(TEST_CHILD_1);
TestChildCfg child = parent.getTestChild("test child 1");
TestCfg.addConstraint(constraint);
ChangeListener listener = new ChangeListener();
child.addChangeListener(listener);
// Now modify it.
String[] changes = new String[] {
"dn: cn=test child 1,cn=test children,cn=test parent 1,cn=test parents,cn=config",
"changetype: modify",
"replace: ds-cfg-base-dn",
"ds-cfg-base-dn: dc=new value 1,dc=com",
"ds-cfg-base-dn: dc=new value 2,dc=com",
"-",
"replace: ds-cfg-group-dn",
"ds-cfg-group-dn: dc=new value 3,dc=com",
"ds-cfg-group-dn: dc=new value 4,dc=com"
};
int result = TestCaseUtils.applyModifications(true, changes);
Assert.assertEquals(result, ResultCode.SUCCESS.intValue());
} finally {
TestCfg.removeConstraint(constraint);
deleteSubtreeNoException(TEST_CHILD_1_DN);
}
}
/**
* Tests that a modify constraint can fail.
*
* @throws Exception
* If the test unexpectedly fails.
*/
@Test
public void testChangeConstraintFail() throws Exception {
TestParentCfg parent = getParent("test parent 1");
MockConstraint constraint = new MockConstraint(false, true);
try {
// Add the entry.
TestCaseUtils.addEntry(TEST_CHILD_1);
TestChildCfg child = parent.getTestChild("test child 1");
TestCfg.addConstraint(constraint);
ChangeListener listener = new ChangeListener();
child.addChangeListener(listener);
// Now modify it.
String[] changes = new String[] {
"dn: cn=test child 1,cn=test children,cn=test parent 1,cn=test parents,cn=config",
"changetype: modify",
"replace: ds-cfg-base-dn",
"ds-cfg-base-dn: dc=new value 1,dc=com",
"ds-cfg-base-dn: dc=new value 2,dc=com",
"-",
"replace: ds-cfg-group-dn",
"ds-cfg-group-dn: dc=new value 3,dc=com",
"ds-cfg-group-dn: dc=new value 4,dc=com"
};
int result = TestCaseUtils.applyModifications(true, changes);
Assert.assertEquals(result, ResultCode.UNWILLING_TO_PERFORM.intValue());
} finally {
TestCfg.removeConstraint(constraint);
deleteSubtreeNoException(TEST_CHILD_1_DN);
}
}
/** Add an entry and check its result. */
private void addEntry(ResultCode expected, String... lines) throws Exception {
Entry entry = TestCaseUtils.makeEntry(lines);
AddOperation add = getRootConnection().processAdd(entry);
assertEquals(add.getResultCode(), expected, add.getErrorMessage().toString());
}
private void deleteSubtreeNoException(String dn)
{
try
{
deleteSubtree(dn);
}
catch (Exception e)
{
// Ignore.
}
}
/** Deletes the named sub-tree. */
private void deleteSubtree(String dn) throws Exception {
getAdaptor().deleteSubtree(new LdapName(dn));
}
/** Gets the JNDI connection for the test server instance. */
private synchronized JNDIDirContextAdaptor getAdaptor() throws Exception {
if (adaptor == null) {
adaptor = JNDIDirContextAdaptor.simpleSSLBind("127.0.0.1", TestCaseUtils
.getServerAdminPort(), "cn=directory manager", "password");
}
return adaptor;
}
/** Gets the named parent configuration. */
private TestParentCfg getParent(String name) throws Exception {
ServerManagementContext ctx = ServerManagementContext.getInstance();
ServerManagedObject<RootCfg> root = ctx.getRootConfigurationManagedObject();
return root.getChild(
TestCfg.getTestOneToManyParentRelationDefinition(), name)
.getConfiguration();
}
}