From a791a43de44115e6e7f9de49a88cc2d452eb91a4 Mon Sep 17 00:00:00 2001
From: neil_a_wilson <neil_a_wilson@localhost>
Date: Sun, 29 Jul 2007 21:30:06 +0000
Subject: [PATCH] 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/resource/schema/02-config.ldif | 5
opends/src/server/org/opends/server/core/DirectoryServer.java | 31 ++++
opends/src/server/org/opends/server/messages/BackendMessages.java | 13 +
opends/tests/unit-tests-testng/src/server/org/opends/server/tasks/DummyTask.java | 64 +++++++++
opends/src/server/org/opends/server/backends/task/TaskScheduler.java | 7 +
opends/tests/unit-tests-testng/src/server/org/opends/server/tasks/AllowedTaskTestCase.java | 233 +++++++++++++++++++++++++++++++++
opends/resource/config/config.ldif | 12 +
opends/src/admin/defn/org/opends/server/admin/std/GlobalConfiguration.xml | 25 +++
opends/src/server/org/opends/server/core/CoreConfigManager.java | 2
9 files changed, 391 insertions(+), 1 deletions(-)
diff --git a/opends/resource/config/config.ldif b/opends/resource/config/config.ldif
index 5998b95..a5caa71 100644
--- a/opends/resource/config/config.ldif
+++ b/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
diff --git a/opends/resource/schema/02-config.ldif b/opends/resource/schema/02-config.ldif
index e67d9eb..a8d3197 100644
--- a/opends/resource/schema/02-config.ldif
+++ b/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'
diff --git a/opends/src/admin/defn/org/opends/server/admin/std/GlobalConfiguration.xml b/opends/src/admin/defn/org/opends/server/admin/std/GlobalConfiguration.xml
index f9901c5..e57546f 100644
--- a/opends/src/admin/defn/org/opends/server/admin/std/GlobalConfiguration.xml
+++ b/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>
diff --git a/opends/src/server/org/opends/server/backends/task/TaskScheduler.java b/opends/src/server/org/opends/server/backends/task/TaskScheduler.java
index bcd30ef..2fd6d42 100644
--- a/opends/src/server/org/opends/server/backends/task/TaskScheduler.java
+++ b/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.
diff --git a/opends/src/server/org/opends/server/core/CoreConfigManager.java b/opends/src/server/org/opends/server/core/CoreConfigManager.java
index 1ac0fcf..e712717 100644
--- a/opends/src/server/org/opends/server/core/CoreConfigManager.java
+++ b/opends/src/server/org/opends/server/core/CoreConfigManager.java
@@ -251,6 +251,8 @@
}
}
DirectoryServer.setMailServerPropertySets(mailServerProperties);
+
+ DirectoryServer.setAllowedTasks(globalConfig.getAllowedTask());
}
diff --git a/opends/src/server/org/opends/server/core/DirectoryServer.java b/opends/src/server/org/opends/server/core/DirectoryServer.java
index 10e3e66..865a256 100644
--- a/opends/src/server/org/opends/server/core/DirectoryServer.java
+++ b/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
diff --git a/opends/src/server/org/opends/server/messages/BackendMessages.java b/opends/src/server/org/opends/server/messages/BackendMessages.java
index 493f138..942417a 100644
--- a/opends/src/server/org/opends/server/messages/BackendMessages.java
+++ b/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: " +
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/tasks/AllowedTaskTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/tasks/AllowedTaskTestCase.java
new file mode 100644
index 0000000..ccdb0e2
--- /dev/null
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/tasks/AllowedTaskTestCase.java
@@ -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;
+ }
+}
+
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/tasks/DummyTask.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/tasks/DummyTask.java
new file mode 100644
index 0000000..05a6916
--- /dev/null
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/tasks/DummyTask.java
@@ -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;
+ }
+}
+
--
Gitblit v1.10.0