mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

neil_a_wilson
29.30.2007 a791a43de44115e6e7f9de49a88cc2d452eb91a4
Implement support for restricting the set of tasks that can be invoked in the
server. A new configuration attribute, ds-cfg-allowed-task, is now available
in the cn=config entry, and any attempt to invoke a task whose fully-qualified
class name is not included in this attribute will be rejected.

OpenDS Issue Number: 2024
2 files added
7 files modified
392 ■■■■■ changed files
opends/resource/config/config.ldif 12 ●●●●● patch | view | raw | blame | history
opends/resource/schema/02-config.ldif 5 ●●●● patch | view | raw | blame | history
opends/src/admin/defn/org/opends/server/admin/std/GlobalConfiguration.xml 25 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/task/TaskScheduler.java 7 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/CoreConfigManager.java 2 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/DirectoryServer.java 31 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/messages/BackendMessages.java 13 ●●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/tasks/AllowedTaskTestCase.java 233 ●●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/tasks/DummyTask.java 64 ●●●●● patch | view | raw | blame | history
opends/resource/config/config.ldif
@@ -46,6 +46,18 @@
ds-cfg-bind-with-dn-requires-password: true
ds-cfg-reject-unauthenticated-requests: false
ds-cfg-default-password-policy: cn=Default Password Policy,cn=Password Policies,cn=config
ds-cfg-allowed-task: org.opends.server.tasks.AddSchemaFileTask
ds-cfg-allowed-task: org.opends.server.tasks.BackupTask
ds-cfg-allowed-task: org.opends.server.tasks.DisconnectClientTask
ds-cfg-allowed-task: org.opends.server.tasks.EnterLockdownModeTask
ds-cfg-allowed-task: org.opends.server.tasks.ExportTask
ds-cfg-allowed-task: org.opends.server.tasks.ImportTask
ds-cfg-allowed-task: org.opends.server.tasks.InitializeTargetTask
ds-cfg-allowed-task: org.opends.server.tasks.InitializeTask
ds-cfg-allowed-task: org.opends.server.tasks.LeaveLockdownModeTask
ds-cfg-allowed-task: org.opends.server.tasks.RebuildTask
ds-cfg-allowed-task: org.opends.server.tasks.RestoreTask
ds-cfg-allowed-task: org.opends.server.tasks.ShutdownTask
dn: cn=Access Control Handler,cn=config
objectClass: top
opends/resource/schema/02-config.ldif
@@ -1530,6 +1530,8 @@
attributeTypes: ( 1.3.6.1.4.1.26027.1.1.455
  NAME 'ds-task-disconnect-notify-client' SYNTAX 1.3.6.1.4.1.1466.115.121.1.7
  SINGLE-VALUE X-ORIGIN 'OpenDS Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.26027.1.1.456 NAME 'ds-cfg-allowed-task'
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'OpenDS Directory Server' )
objectClasses: ( 1.3.6.1.4.1.26027.1.2.1
  NAME 'ds-cfg-access-control-handler' SUP top STRUCTURAL
  MUST ( cn $ ds-cfg-acl-handler-class $ ds-cfg-acl-handler-enabled )
@@ -1744,7 +1746,8 @@
  ds-cfg-proxied-authorization-identity-mapper-dn $ ds-cfg-writability-mode $
  ds-cfg-reject-unauthenticated-requests  $
  ds-cfg-bind-with-dn-requires-password $ ds-cfg-lookthrough-limit $
  ds-cfg-smtp-server ) X-ORIGIN 'OpenDS Directory Server' )
  ds-cfg-smtp-server $ ds-cfg-allowed-task )
  X-ORIGIN 'OpenDS Directory Server' )
objectClasses: ( 1.3.6.1.4.1.26027.1.2.41 NAME 'ds-cfg-root-dn' SUP top
  AUXILIARY MAY ds-cfg-alternate-bind-dn X-ORIGIN 'OpenDS Directory Server' )
objectClasses: ( 1.3.6.1.4.1.26027.1.2.42 NAME 'ds-cfg-root-dse'
opends/src/admin/defn/org/opends/server/admin/std/GlobalConfiguration.xml
@@ -501,5 +501,30 @@
    </adm:profile>
  </adm:property>
  <adm:property name="allowed-task" mandatory="false" multi-valued="true">
    <adm:synopsis>
      Specifies the fully-qualified name of a Java class that may be invoked in
      the server.  Any attempt to invoke a task not included in the list of
      allowed tasks will be rejected.
    </adm:synopsis>
    <adm:default-behavior>
      <adm:alias>
        <adm:synopsis>
          If no values are defined, then the server will not allow any tasks to
          be invoked.
        </adm:synopsis>
      </adm:alias>
    </adm:default-behavior>
    <adm:syntax>
      <adm:string />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.454</ldap:oid>
        <ldap:name>ds-cfg-allowed-task</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
</adm:managed-object>
opends/src/server/org/opends/server/backends/task/TaskScheduler.java
@@ -1829,6 +1829,13 @@
    }
    String taskClassName = value.getStringValue();
    if (! DirectoryServer.getAllowedTasks().contains(taskClassName))
    {
      int    msgID   = MSGID_TASKSCHED_NOT_ALLOWED_TASK;
      String message = getMessage(msgID, taskClassName);
      throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
                                   msgID);
    }
    // Try to load the specified class.
opends/src/server/org/opends/server/core/CoreConfigManager.java
@@ -251,6 +251,8 @@
      }
    }
    DirectoryServer.setMailServerPropertySets(mailServerProperties);
    DirectoryServer.setAllowedTasks(globalConfig.getAllowedTask());
  }
opends/src/server/org/opends/server/core/DirectoryServer.java
@@ -522,6 +522,9 @@
  // The error logger that will be used during the Directory Server startup.
  private TextErrorLogPublisher startupErrorLogPublisher;
  // The set of allowed task classes.
  private Set<String> allowedTasks;
  // The fully-qualified name of the configuration handler class.
  private String configClass;
@@ -718,6 +721,7 @@
         new CopyOnWriteArrayList<ExportTaskListener>();
    directoryServer.importTaskListeners =
         new CopyOnWriteArrayList<ImportTaskListener>();
    directoryServer.allowedTasks = new LinkedHashSet<String>(0);
  }
@@ -7392,6 +7396,33 @@
  /**
   * Retrieves a set containing the names of the allowed tasks that may be
   * invoked in the server.
   *
   * @return  A set containing the names of the allowed tasks that may be
   *          invoked in the server.
   */
  public static Set<String> getAllowedTasks()
  {
    return directoryServer.allowedTasks;
  }
  /**
   * Specifies the set of allowed tasks that may be invoked in the server.
   *
   * @param  allowedTasks  A set containing the names of the allowed tasks that
   *                       may be invoked in the server.
   */
  public static void setAllowedTasks(Set<String> allowedTasks)
  {
    directoryServer.allowedTasks = allowedTasks;
  }
  /**
   * Registers the provided backup task listener with the Directory Server.
   *
   * @param  listener  The backup task listener to register with the Directory
opends/src/server/org/opends/server/messages/BackendMessages.java
@@ -3227,6 +3227,16 @@
  /**
   * The message ID for the message that will be used if an attempt is made to
   * schedule a task that is not included in the set of allowed tasks.  This
   * takes a single argument, which is the name of the target class.
   */
  public static final int MSGID_TASKSCHED_NOT_ALLOWED_TASK =
       CATEGORY_MASK_BACKEND | SEVERITY_MASK_SEVERE_ERROR | 298;
  /**
   * Associates a set of generic messages with the message IDs defined in this
   * class.
   */
@@ -4238,6 +4248,9 @@
                    "The provided task entry contains multiple values for " +
                    "the %s attribute, which is used to specify the task " +
                    "class name, but only a single value is allowed");
    registerMessage(MSGID_TASKSCHED_NOT_ALLOWED_TASK,
                    "The Directory Server is not configured to allow task " +
                    "%s to be invoked");
    registerMessage(MSGID_TASKSCHED_CANNOT_LOAD_CLASS,
                    "An error occurred while attempting to load class %s " +
                    "specified in attribute %s of the provided task entry:  " +
opends/tests/unit-tests-testng/src/server/org/opends/server/tasks/AllowedTaskTestCase.java
New file
@@ -0,0 +1,233 @@
/*
 * 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
 *
 *
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.tasks;
import org.testng.annotations.Test;
import org.testng.annotations.BeforeClass;
import org.opends.server.TestCaseUtils;
import org.opends.server.backends.task.Task;
import org.opends.server.backends.task.TaskBackend;
import org.opends.server.backends.task.TaskState;
import org.opends.server.core.DirectoryServer;
import org.opends.server.extensions.GetConnectionIDExtendedOperation;
import org.opends.server.protocols.ldap.LDAPResultCode;
import org.opends.server.tools.LDAPModify;
import org.opends.server.tools.dsconfig.DSConfig;
import org.opends.server.types.DN;
import static org.testng.Assert.*;
import static org.opends.server.util.ServerConstants.*;
/**
 * Tests the ability of the server to control the set of tasks that are allowed
 * to be executed.
 */
public class AllowedTaskTestCase
       extends TasksTestCase
{
  /**
   * Make sure that the Directory Server is running.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @BeforeClass()
  public void startServer()
         throws Exception
  {
    TestCaseUtils.startServer();
  }
  /**
   * Tests the function of the ds-cfg-allowed-task configuration attribute
   * using a dummy task.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testAllowedTask()
         throws Exception
  {
    // Try to add the dummy task and expect it to fail because it's not allowed.
    String path = TestCaseUtils.createTempFile(
      "dn: ds-task-id=testAllowedTask 1,cn=Scheduled Tasks,cn=Tasks",
      "changetype: add",
      "objectClass: top",
      "objectClass: ds-task",
      "ds-task-id: testAllowedTask 1",
      "ds-task-class-name: org.opends.server.tasks.DummyTask");
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    assertEquals(LDAPModify.mainModify(args, false, System.out, System.err),
                 LDAPResultCode.UNWILLING_TO_PERFORM);
    // Update the set of allowed tasks to include the dummy task.
    args = new String[]
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "set-global-configuration-prop",
      "--add", "allowed-task:org.opends.server.tasks.DummyTask"
    };
    assertEquals(DSConfig.main(args, false, System.out, System.err), 0);
    // Now verify that we can add the task and have it complete successfully.
    path = TestCaseUtils.createTempFile(
      "dn: ds-task-id=testAllowedTask 2,cn=Scheduled Tasks,cn=Tasks",
      "changetype: add",
      "objectClass: top",
      "objectClass: ds-task",
      "ds-task-id: testAllowedTask 2",
      "ds-task-class-name: org.opends.server.tasks.DummyTask");
    args = new String[]
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    assertEquals(LDAPModify.mainModify(args, false, System.out, System.err),
                 LDAPResultCode.SUCCESS);
    Task task = getCompletedTask(DN.decode(
         "ds-task-id=testAllowedTask 2,cn=Scheduled Tasks,cn=Tasks"));
    assertNotNull(task);
    assertEquals(task.getTaskState(), TaskState.COMPLETED_SUCCESSFULLY);
    // Remove the task class from the set of allowed tasks and verify that we
    // can no longer schedule the task.
    args = new String[]
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "set-global-configuration-prop",
      "--remove", "allowed-task:org.opends.server.tasks.DummyTask"
    };
    assertEquals(DSConfig.main(args, false, System.out, System.err), 0);
    // Now verify that we can add the task and have it complete successfully.
    path = TestCaseUtils.createTempFile(
      "dn: ds-task-id=testAllowedTask 3,cn=Scheduled Tasks,cn=Tasks",
      "changetype: add",
      "objectClass: top",
      "objectClass: ds-task",
      "ds-task-id: testAllowedTask 3",
      "ds-task-class-name: org.opends.server.tasks.DummyTask");
    args = new String[]
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    assertEquals(LDAPModify.mainModify(args, false, System.out, System.err),
                 LDAPResultCode.UNWILLING_TO_PERFORM);
  }
  /**
   * Retrieves the specified task from the server, waiting for it to finish all
   * the running its going to do before returning.
   *
   * @param  taskEntryDN  The DN of the entry for the task to retrieve.
   *
   * @return  The requested task entry.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  private Task getCompletedTask(DN taskEntryDN)
          throws Exception
  {
    TaskBackend taskBackend =
         (TaskBackend) DirectoryServer.getBackend(DN.decode("cn=tasks"));
    Task task = taskBackend.getScheduledTask(taskEntryDN);
    if (task == null)
    {
      long stopWaitingTime = System.currentTimeMillis() + 10000L;
      while ((task == null) && (System.currentTimeMillis() < stopWaitingTime))
      {
        Thread.sleep(10);
        task = taskBackend.getScheduledTask(taskEntryDN);
      }
    }
    if (task == null)
    {
      throw new AssertionError("There is no such task " +
                               taskEntryDN.toString());
    }
    if (! TaskState.isDone(task.getTaskState()))
    {
      long stopWaitingTime = System.currentTimeMillis() + 20000L;
      while ((! TaskState.isDone(task.getTaskState())) &&
             (System.currentTimeMillis() < stopWaitingTime))
      {
        Thread.sleep(10);
      }
    }
    if (! TaskState.isDone(task.getTaskState()))
    {
      throw new AssertionError("Task " + taskEntryDN.toString() +
                               " did not complete in a timely manner.");
    }
    return task;
  }
}
opends/tests/unit-tests-testng/src/server/org/opends/server/tasks/DummyTask.java
New file
@@ -0,0 +1,64 @@
/*
 * 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
 *
 *
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.tasks;
import org.opends.server.backends.task.Task;
import org.opends.server.backends.task.TaskState;
import org.opends.server.types.DirectoryException;
/**
 * This class provides an implementation of a Directory Server task always
 * completes successfully.  It is intended only for testing purposes.
 */
public class DummyTask
       extends Task
{
  /**
   * {@inheritDoc}
   */
  @Override
  public void initializeTask()
         throws DirectoryException
  {
    // No implementation is required.
  }
  /**
   * {@inheritDoc}
   */
  protected TaskState runTask()
  {
    return TaskState.COMPLETED_SUCCESSFULLY;
  }
}