/* * 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 2008 Sun Microsystems, Inc. */ package org.opends.server.core; import static org.opends.server.util.StaticUtils.createEntry; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNotNull; import java.util.ArrayList; import org.opends.server.TestCaseUtils; import org.opends.server.api.Backend; import org.opends.server.api.ClientConnection; import org.opends.server.config.ConfigConstants; import org.opends.server.core.networkgroups.NetworkGroup; import org.opends.server.protocols.internal.InternalClientConnection; import org.opends.server.protocols.internal.InternalSearchOperation; import org.opends.server.protocols.ldap.LDAPAttribute; import org.opends.server.protocols.ldap.LDAPFilter; import org.opends.server.protocols.ldap.LDAPModification; import org.opends.server.types.*; import org.opends.server.util.StaticUtils; import org.opends.server.util.UtilTestCase; import org.opends.server.workflowelement.localbackend.LocalBackendWorkflowElement; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; /** * This class tests the 'manual' workflow configuration mode. The 'auto' * configuration mode does not require any specific unit test because by * default the server is running with the 'auto' mode. * * With the manual configuration mode, all the network groups, workflows * and workflow elements must be defined in the configuration file. */ public class WorkflowConfigurationTest extends UtilTestCase { // The base DN of the config backend private static final String configBaseDN = ConfigConstants.DN_CONFIG_ROOT; // The base DN of the rootDSE backend private static final String rootDSEBaseDN = ""; // The workflow configuration mode attribute private static final String workflowModeAttributeType = "ds-cfg-workflow-configuration-mode"; // The suffix attribute in a backend private static final String suffixAttributeType = "ds-cfg-base-dn"; // The auto/manual modes private static final String workflowConfigModeAuto = "auto"; private static final String workflowConfigModeManual = "manual"; //=========================================================================== // B E F O R E C L A S S //=========================================================================== /** * Set up the environment for performing the tests in this suite. * * @throws Exception if the environment could not be set up. */ @BeforeClass public void setUp() throws Exception { // Start the server so that we can update the configuration and execute // some LDAP operations TestCaseUtils.startServer(); // Add the attribute ds-cfg-workflow-configuration-mode with the // value 'auto initializeConfigurationMode(); checkBackendIsAccessible("o=test"); } //=========================================================================== // D A T A P R O V I D E R //=========================================================================== //=========================================================================== // U T I L S //=========================================================================== /** * Adds an attribute ds-cfg-workflow-configuration-mode in the entry * cn=config. The added value is 'auto'. */ private void initializeConfigurationMode() throws Exception { // Add the ds-cfg-workflow-configuration-mode attribute and set // its value to "auto" ModifyOperationBasis modifyOperation = getModifyOperation( configBaseDN, ModificationType.ADD, workflowModeAttributeType, workflowConfigModeAuto); modifyOperation.run(); assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS); } /** * Checks that test backend is accessible as well as config backend * and rootDSE backend. * * @param baseDN the baseDN of the backend to check */ private void checkBackendIsAccessible(String baseDN) throws Exception { // The config backend and rootDSE backend should always be accessible doSearch(rootDSEBaseDN, SearchScope.BASE_OBJECT, ResultCode.SUCCESS); doSearch(configBaseDN, SearchScope.BASE_OBJECT, ResultCode.SUCCESS); // The test backend should be accessible doSearch(baseDN, SearchScope.BASE_OBJECT, ResultCode.SUCCESS); } /** * Checks that test backend is not accessible while config backend * and rootDSE backend are. * * @param baseDN the baseDN of the backend to check */ private void checkBackendIsNotAccessible(String baseDN) throws Exception { // The config backend and rootDSE should always be accessible doSearch(rootDSEBaseDN, SearchScope.BASE_OBJECT, ResultCode.SUCCESS); doSearch(configBaseDN, SearchScope.BASE_OBJECT, ResultCode.SUCCESS); // The test backend should be accessible doSearch(baseDN, SearchScope.BASE_OBJECT, ResultCode.NO_SUCH_OBJECT); } /** * Sets the ds-cfg-workflow-configuration-mode attribute to 'auto' */ private void setModeAuto() throws Exception { ModifyOperationBasis modifyOperation = getModifyOperation( configBaseDN, ModificationType.REPLACE, workflowModeAttributeType, workflowConfigModeAuto); modifyOperation.run(); assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS); } /** * Sets the ds-cfg-workflow-configuration-mode attribute to 'auto' */ private void setModeManual() throws Exception { ModifyOperationBasis modifyOperation = getModifyOperation( configBaseDN, ModificationType.REPLACE, workflowModeAttributeType, workflowConfigModeManual); modifyOperation.run(); assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS); } /** * Performs a search on a provided base DN. * * @param baseDN the search base DN * @param scope the scope of the search * @param expectedResultCode the expected result code * * @return the search operation used for the test */ private InternalSearchOperation doSearch( String baseDN, SearchScope scope, ResultCode expectedResultCode ) throws Exception { InternalSearchOperation searchOperation = new InternalSearchOperation( InternalClientConnection.getRootConnection(), InternalClientConnection.nextOperationID(), InternalClientConnection.nextMessageID(), new ArrayList<Control>(), ByteString.valueOf(baseDN), scope, DereferencePolicy.NEVER_DEREF_ALIASES, Integer.MAX_VALUE, Integer.MAX_VALUE, false, LDAPFilter.decode("(objectClass=*)"), null, null); searchOperation.run(); assertEquals(searchOperation.getResultCode(), expectedResultCode); return searchOperation; } /** * Provides a modify operation. * * @param entryDN the DN of the entry targeted by the modify operation * @param modType the type of the modification * @param attributeType the type of the attribute to modify * @param attributeValue the value of the attribute to modify */ private static ModifyOperationBasis getModifyOperation( String entryDN, ModificationType modType, String attributeType, String attributeValue) { ArrayList<ByteString> ldapValues = new ArrayList<ByteString>(); ldapValues.add(ByteString.valueOf(attributeValue)); LDAPAttribute ldapAttr = new LDAPAttribute(attributeType, ldapValues); ArrayList<RawModification> ldapMods = new ArrayList<RawModification>(); ldapMods.add(new LDAPModification(modType, ldapAttr)); ModifyOperationBasis modifyOperation = new ModifyOperationBasis( InternalClientConnection.getRootConnection(), InternalClientConnection.nextOperationID(), InternalClientConnection.nextMessageID(), new ArrayList<Control>(), ByteString.valueOf(entryDN), ldapMods); return modifyOperation; } /** * Creates a workflow to handle a local backend. The internal network * group is used. * * @param baseDN the base DN of the workflow * @param backendID the backend which contains the baseDN * * @return the newly created workflow */ private WorkflowImpl createWorkflow(String baseDN, String backendID) throws Exception { // Get the backend Backend backend = DirectoryServer.getBackend(backendID); assertNotNull(backend); // Create the workflow element that wraps the local backend String workflowElementID = baseDN + "#" + backendID; LocalBackendWorkflowElement workflowElement = LocalBackendWorkflowElement.createAndRegister(workflowElementID, backend); // Create a workflow and register it with the server String workflowID = baseDN + "#" + backendID; WorkflowImpl workflowImpl = new WorkflowImpl( workflowID, DN.decode(baseDN), workflowElementID, workflowElement); workflowImpl.register(); // Register the workflow with the internal network group NetworkGroup.getInternalNetworkGroup().registerWorkflow(workflowImpl); return workflowImpl; } /** * Removes a workflow. * * @param baseDN the base DN of the workflow * @param backendID the backend which contains the baseDN */ private void removeWorkflow(String baseDN, String backendID) throws Exception { // Elaborate the workflow ID String workflowID = baseDN + "#" + backendID; // Deregister the workflow with the internal network group NetworkGroup.getInternalNetworkGroup().deregisterWorkflow(workflowID); // Deregister the workflow with the server Workflow workflow = WorkflowImpl.getWorkflow(workflowID); WorkflowImpl workflowImpl = (WorkflowImpl) workflow; workflowImpl.deregister(); } /** * Adds a new suffix in a backend. * * @param baseDN the DN of the suffix to add * @param backendID the identifier of the backend to which the suffix * is added */ private void addSuffix(String baseDN, String backendID) throws Exception { // Elaborate the DN of the backend config entry String backendDN = elaborateBackendDN(backendID); // Add a new suffix in the backend ModifyOperationBasis modifyOperation = getModifyOperation( backendDN, ModificationType.ADD, suffixAttributeType, baseDN); modifyOperation.run(); assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS); } /** * Create a base entry for a new suffix. * * @param baseDN the DN of the new base entry * @param backendID the identifier of the backend */ private void createBaseEntry(String baseDN, String backendID) throws Exception { Entry entry = StaticUtils.createEntry(DN.decode(baseDN)); AddOperationBasis addOperation = new AddOperationBasis( InternalClientConnection.getRootConnection(), InternalClientConnection.nextOperationID(), InternalClientConnection.nextMessageID(), null, entry.getDN(), entry.getObjectClasses(), entry.getUserAttributes(), entry.getOperationalAttributes()); addOperation.run(); assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS); } /** * Removes a new suffix in a backend. * * @param baseDN the DN of the suffix to remove * @param backendID the identifier of the backend to which the suffix * is removed * * @throw Exception if the backend does not exist or if the suffix * already exist in the backend */ private void removeSuffix(String baseDN, String backendID) throws Exception { // Elaborate the DN of the backend config entry String backendDN = elaborateBackendDN(backendID); // Add a new suffix in the backend ModifyOperationBasis modifyOperation = getModifyOperation( backendDN, ModificationType.DELETE, suffixAttributeType, baseDN); modifyOperation.run(); assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS); } /** * Elaborates a DN for a backend config entry. * * @param backendID the identifier of the backend to retrieve */ private String elaborateBackendDN(String backendID) { String backendDN = "ds-cfg-backend-id=" + backendID + ",cn=Backends,cn=config"; return backendDN; } //=========================================================================== // U T I L S using dsconfig //=========================================================================== /** * Initializes a memory-based backend. * * @param backendID the identifier of the backend to create * @param baseDN the DN of the suffix to create * @param createBaseEntry indicate whether to automatically create the base * entry and add it to the backend. * * @return the newly created backend * @throws Exception If an unexpected problem occurs. */ private static Backend dsconfigCreateMemoryBackend( String backendID, String baseDN, boolean createBaseEntry ) throws Exception { TestCaseUtils.dsconfig( "create-backend", "--backend-name", backendID, "--type", "memory", "--set", "base-dn:" + baseDN, "--set", "writability-mode:enabled", "--set", "enabled:true"); Backend backend = DirectoryServer.getBackend(backendID); if (createBaseEntry) { Entry e = createEntry(DN.decode(baseDN)); backend.addEntry(e, null); } return backend; } /** * Remove a backend. * * @param backendID the identifier of the backend to remove * * @throws Exception If an unexpected problem occurs. */ private static void dsconfigRemoveMemoryBackend( String backendID ) throws Exception { TestCaseUtils.dsconfig( "delete-backend", "--backend-name", backendID); } //=========================================================================== // T E S T C A S E S //=========================================================================== /** * This test checks the transition from mode 'auto' to 'manual' and * 'manual' back to 'auto'. In this test there is no configuration for * network group, workflow and workflow element. */ @Test public void transitionAutoManualAuto() throws Exception { // Settings String testBaseDN = "o=test"; // The ds-cfg-workflow-configuration-mode attribute value is "auto" // (default value), let's put the same value again. Putting the same // value should have no impact and we should be able to perform a search // on the test backend. setModeAuto(); checkBackendIsAccessible(testBaseDN); // Change the ds-cfg-workflow-configuration-mode attribute value // to "manual". The workflows should be fully reconfigured. But as // there is no configuration for the workflows, only cn=config and // rootDSE should be accessible. setModeManual(); checkBackendIsNotAccessible(testBaseDN); // Change the ds-cfg-workflow-configuration-mode attribute value // back to "auto". All the local backends should be accessible again. setModeAuto(); checkBackendIsAccessible(testBaseDN); } /** * This test checks the basic operation routing when configuration * mode is 'manual'. Few workflows are configured for the test. */ @Test public void basicRoutingManualMode() throws Exception { // Settings String testBaseDN = "o=test"; String testBackendID = "test"; // Workflow configuration mode is auto, so test backend should // be accessible setModeAuto(); checkBackendIsAccessible(testBaseDN); // Set the workflow configuration mode to manual. In this mode // no there is no workflow by default (but the config and rootDSE // workflows) so searches on the test backend should fail. setModeManual(); checkBackendIsNotAccessible(testBaseDN); // Create a workflow to handle o=test backend then check that test // backend is now accessible createWorkflow(testBaseDN, testBackendID); checkBackendIsAccessible(testBaseDN); // Remove the workflow and check that searches are failing. removeWorkflow(testBaseDN, testBackendID); checkBackendIsNotAccessible(testBaseDN); // Change workflow configuration mode back to auto and check that // test backend is still accessible setModeAuto(); checkBackendIsAccessible(testBaseDN); } /** * This test checks the add/remove of suffix in a backend in manual * configuration mode. */ @Test public void addRemoveSuffix() throws Exception { // Settings String testBaseDN2 = "o=addRemoveSuffix_1"; String testBaseDN3 = "o=addRemoveSuffix_2"; String testBackendID2 = "userRoot"; // make sure we are in auto mode and check that the new suffixes are // not already defined on the server. setModeAuto(); checkBackendIsNotAccessible(testBaseDN2); checkBackendIsNotAccessible(testBaseDN3); // Add a new suffix to the test backend and check that the new // suffix is accessible (we are in auto mode). addSuffix(testBaseDN2, testBackendID2); createBaseEntry(testBaseDN2, testBackendID2); checkBackendIsAccessible(testBaseDN2); // Remove the suffix and check that the removed suffix is no // more accessible. removeSuffix(testBaseDN2, testBackendID2); checkBackendIsNotAccessible(testBaseDN2); // Now move to the manual mode. setModeManual(); // Add a new suffix and configure a workflow to route operation // to this new suffix, then check that the new suffix is accessible. // Note that before we can create a base entry we need to configure // first a workflow to route the ADD operation to the right suffix. // This need not be to be done in auto mode because with the auto mode // the workflow is automatically created when a new suffix is added. addSuffix(testBaseDN3, testBackendID2); createWorkflow(testBaseDN3, testBackendID2); createBaseEntry(testBaseDN3, testBackendID2); checkBackendIsAccessible(testBaseDN3); // Finally remove the new workflow and suffix and check that the suffix // is no more accessible. removeWorkflow(testBaseDN3, testBackendID2); removeSuffix(testBaseDN3, testBackendID2); checkBackendIsNotAccessible(testBaseDN3); // Back to the original configuration mode setModeAuto(); } /** * This test checks the add/remove of a backend in manual configuration * mode. */ @Test public void addRemoveBackend() throws Exception { // Local settings String backendID1 = "addRemoveBackend_1"; String backendID2 = "addRemoveBackend_2"; String baseDN1 = "o=addRemoveBackendBaseDN_1"; String baseDN2 = "o=addRemoveBackendBaseDN_2"; // Make sure we are in auto mode and check the suffix is not accessible setModeAuto(); checkBackendIsNotAccessible(baseDN1); // Create a backend and check that the base entry is accessible. dsconfigCreateMemoryBackend(backendID1, baseDN1, true); checkBackendIsAccessible(baseDN1); // Remove the backend and check that the suffix is no more accessible. dsconfigRemoveMemoryBackend(backendID1); checkBackendIsNotAccessible(baseDN1); // Now move to the manual mode setModeManual(); checkBackendIsNotAccessible(baseDN2); // Create a backend and create a workflow to route operations to that // new backend. Then check that the base entry is accessible. dsconfigCreateMemoryBackend(backendID2, baseDN2, true); createWorkflow(baseDN2, backendID2); checkBackendIsAccessible(baseDN2); // Remove the workflow and the backend and check that the base entry // is no more accessible. removeWorkflow(baseDN2, backendID2); dsconfigRemoveMemoryBackend(backendID2); checkBackendIsNotAccessible(baseDN2); // Back to the original configuration mode setModeAuto(); } /** * This test checks the creation and utilization of network group * in the route process. */ @Test public void useNetworkGroup() throws Exception { // Local settings String backendID = "test"; String baseDN = "o=test"; // Move to the manual mode setModeManual(); // Create a route for o=test suffix in the internal network group. // Search on o=test should succeed. WorkflowImpl workflowImpl = createWorkflow(baseDN, backendID); InternalSearchOperation searchOperation = doSearch(baseDN, SearchScope.BASE_OBJECT, ResultCode.SUCCESS); // Create a network group and store it in the client connection. // As the network group is empty, all searches should fail with a // no such object result code. String networkGroupID = "useNetworkGroupID"; NetworkGroup networkGroup = new NetworkGroup(networkGroupID); ClientConnection clientConnection = searchOperation.getClientConnection(); clientConnection.setNetworkGroup(networkGroup); searchOperation.run(); assertEquals(searchOperation.getResultCode(), ResultCode.NO_SUCH_OBJECT); // Now register the o=test workflow and search again. The search // should succeed. networkGroup.registerWorkflow(workflowImpl); searchOperation.run(); assertEquals(searchOperation.getResultCode(), ResultCode.SUCCESS); // Put back the internal network group in the client connection // and check that searches are still working. clientConnection.setNetworkGroup(NetworkGroup.getInternalNetworkGroup()); searchOperation.run(); assertEquals(searchOperation.getResultCode(), ResultCode.SUCCESS); // Back to the original configuration mode setModeAuto(); } }