From f9411cf977892773761a0fbb80bb8c8041f0a2b0 Mon Sep 17 00:00:00 2001
From: neil_a_wilson <neil_a_wilson@localhost>
Date: Mon, 30 Jul 2007 06:26:11 +0000
Subject: [PATCH] Update the server to provide an idle time limit configuration option that can be used to terminate client connections that have been idle for too long. This can be controlled on a server-wide default level using the ds-cfg-idle-time-limit configuration attribute in the cn=config entry, but it can also be overridden on a per-user level with the ds-rlim-idle-time-limit operational attribute in the user's entry.
---
opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/GlobalConfiguration.xml | 22 +
opendj-sdk/opends/src/server/org/opends/server/api/ClientConnection.java | 55 +++
opendj-sdk/opends/src/server/org/opends/server/core/DirectoryServer.java | 34 ++
opendj-sdk/opends/src/server/org/opends/server/core/IdleTimeLimitThread.java | 193 +++++++++++++
opendj-sdk/opends/src/server/org/opends/server/messages/CoreMessages.java | 71 +++++
opendj-sdk/opends/resource/config/config.ldif | 2
opendj-sdk/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java | 96 ++++++
opendj-sdk/opends/src/server/org/opends/server/config/ConfigConstants.java | 9
opendj-sdk/opends/src/server/org/opends/server/core/CoreConfigManager.java | 2
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/IdleTimeLimitTestCase.java | 275 +++++++++++++++++++
opendj-sdk/opends/resource/schema/02-config.ldif | 9
opendj-sdk/opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java | 51 +++
12 files changed, 817 insertions(+), 2 deletions(-)
diff --git a/opendj-sdk/opends/resource/config/config.ldif b/opendj-sdk/opends/resource/config/config.ldif
index dd087a8..ed5c9d2 100644
--- a/opendj-sdk/opends/resource/config/config.ldif
+++ b/opendj-sdk/opends/resource/config/config.ldif
@@ -47,6 +47,7 @@
ds-cfg-reject-unauthenticated-requests: false
ds-cfg-default-password-policy: cn=Default Password Policy,cn=Password Policies,cn=config
ds-cfg-return-bind-error-messages: false
+ds-cfg-idle-time-limit: 0 seconds
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
@@ -1380,6 +1381,7 @@
ds-cfg-alternate-bind-dn: cn=Directory Manager
ds-rlim-size-limit: 0
ds-rlim-time-limit: 0
+ds-rlim-idle-time-limit: 0
ds-rlim-lookthrough-limit: 0
ds-pwp-password-policy-dn: cn=Root Password Policy,cn=Password Policies,cn=config
diff --git a/opendj-sdk/opends/resource/schema/02-config.ldif b/opendj-sdk/opends/resource/schema/02-config.ldif
index aec4017..e3e7559 100644
--- a/opendj-sdk/opends/resource/schema/02-config.ldif
+++ b/opendj-sdk/opends/resource/schema/02-config.ldif
@@ -1547,6 +1547,12 @@
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'OpenDS Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.26027.1.1.462 NAME 'ds-cfg-ssl-cipher-suite'
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'OpenDS Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.26027.1.1.463 NAME 'ds-cfg-idle-time-limit'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE
+ X-ORIGIN 'OpenDS Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.26027.1.1.464 NAME 'ds-rlim-idle-time-limit'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE USAGE directoryOperation
+ 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 )
@@ -1764,7 +1770,8 @@
ds-cfg-reject-unauthenticated-requests $
ds-cfg-bind-with-dn-requires-password $ ds-cfg-lookthrough-limit $
ds-cfg-smtp-server $ ds-cfg-allowed-task $ ds-cfg-disabled-privilege $
- ds-cfg-return-bind-error-messages ) X-ORIGIN 'OpenDS Directory Server' )
+ ds-cfg-return-bind-error-messages $ ds-cfg-idle-time-limit )
+ 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/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/GlobalConfiguration.xml b/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/GlobalConfiguration.xml
index f146ee9..31a8846 100644
--- a/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/GlobalConfiguration.xml
+++ b/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/GlobalConfiguration.xml
@@ -693,5 +693,27 @@
</adm:profile>
</adm:property>
+ <adm:property name="idle-time-limit" mandatory="false" multi-valued="false">
+ <adm:synopsis>
+ Specifies the maximum lenght of time that a client connection may remain
+ established since its last completed operation. A value of "0 seconds"
+ indicates that no idle time limit will be enforced.
+ </adm:synopsis>
+ <adm:default-behavior>
+ <adm:defined>
+ <adm:value>0 seconds</adm:value>
+ </adm:defined>
+ </adm:default-behavior>
+ <adm:syntax>
+ <adm:duration base-unit="ms" lower-limit="0"/>
+ </adm:syntax>
+ <adm:profile name="ldap">
+ <ldap:attribute>
+ <ldap:oid>1.3.6.1.4.1.26027.1.1.463</ldap:oid>
+ <ldap:name>ds-cfg-idle-time-limit</ldap:name>
+ </ldap:attribute>
+ </adm:profile>
+ </adm:property>
+
</adm:managed-object>
diff --git a/opendj-sdk/opends/src/server/org/opends/server/api/ClientConnection.java b/opendj-sdk/opends/src/server/org/opends/server/api/ClientConnection.java
index 1c268df..8c0fc8e 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/api/ClientConnection.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/api/ClientConnection.java
@@ -108,6 +108,9 @@
// The time that this client connection was established.
private long connectTime;
+ // The idle time limit for this client connection.
+ private long idleTimeLimit;
+
// The opaque information used for storing intermediate state
// information needed across multi-stage SASL binds.
private Object saslAuthState;
@@ -138,6 +141,7 @@
persistentSearches = new CopyOnWriteArrayList<PersistentSearch>();
sizeLimit = DirectoryServer.getSizeLimit();
timeLimit = DirectoryServer.getTimeLimit();
+ idleTimeLimit = DirectoryServer.getIdleTimeLimit();
lookthroughLimit = DirectoryServer.getLookthroughLimit();
finalized = false;
privileges = new HashSet<Privilege>();
@@ -1274,6 +1278,39 @@
/**
+ * Retrieves the maximum length of time in milliseconds that this
+ * client connection will be allowed to remain idle before it should
+ * be disconnected.
+ *
+ * @return The maximum length of time in milliseconds that this
+ * client connection will be allowed to remain idle before
+ * it should be disconnected.
+ */
+ public final long getIdleTimeLimit()
+ {
+ return idleTimeLimit;
+ }
+
+
+
+ /**
+ * Specifies the maximum length of time in milliseconds that this
+ * client connection will be allowed to remain idle before it should
+ * be disconnected.
+ *
+ * @param idleTimeLimit The maximum length of time in milliseconds
+ * that this client connection will be
+ * allowed to remain idle before it should be
+ * disconnected.
+ */
+ public void setIdleTimeLimit(long idleTimeLimit)
+ {
+ this.idleTimeLimit = idleTimeLimit;
+ }
+
+
+
+ /**
* Retrieves the default maximum number of entries that should
* checked for matches during a search.
*
@@ -1575,5 +1612,23 @@
this.networkGroup = networkGroup;
}
+
+
+ /**
+ * Retrieves the length of time in milliseconds that this client
+ * connection has been idle.
+ * <BR><BR>
+ * Note that the default implementation will always return zero.
+ * Subclasses associated with connection handlers should override
+ * this method if they wish to provided idle time limit
+ * functionality.
+ *
+ * @return The length of time in milliseconds that this client
+ * connection has been idle.
+ */
+ public long getIdleTime()
+ {
+ return 0L;
+ }
}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/config/ConfigConstants.java b/opendj-sdk/opends/src/server/org/opends/server/config/ConfigConstants.java
index 09b7bdd..afa68fe 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/config/ConfigConstants.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/config/ConfigConstants.java
@@ -3541,6 +3541,15 @@
/**
+ * The name of the operational attribute that may be included in user entries
+ * to specify an idle time limit to be applied for that user.
+ */
+ public static final String OP_ATTR_USER_IDLE_TIME_LIMIT =
+ NAME_PREFIX_RLIM + "idle-time-limit";
+
+
+
+ /**
* The name of the operational attribute that may be included in user
* entries to specify a size limit to be applied for that user.
*/
diff --git a/opendj-sdk/opends/src/server/org/opends/server/core/CoreConfigManager.java b/opendj-sdk/opends/src/server/org/opends/server/core/CoreConfigManager.java
index 5d3ef6d..c643145 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/core/CoreConfigManager.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/core/CoreConfigManager.java
@@ -337,6 +337,8 @@
DirectoryServer.setReturnBindErrorMessages(
globalConfig.isReturnBindErrorMessages());
+
+ DirectoryServer.setIdleTimeLimit(globalConfig.getIdleTimeLimit());
}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/core/DirectoryServer.java b/opendj-sdk/opends/src/server/org/opends/server/core/DirectoryServer.java
index 78ffd8b..4d56604 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/core/DirectoryServer.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/core/DirectoryServer.java
@@ -463,6 +463,9 @@
// The number of connections currently established to the server.
private long currentConnections;
+ // The idle time limit for the server.
+ private long idleTimeLimit;
+
// The maximum number of connections that will be allowed at any given time.
private long maxAllowedConnections;
@@ -731,6 +734,7 @@
directoryServer.allowedTasks = new LinkedHashSet<String>(0);
directoryServer.disabledPrivileges = new LinkedHashSet<Privilege>(0);
directoryServer.returnBindErrorMessages = false;
+ directoryServer.idleTimeLimit = 0L;
}
@@ -1204,6 +1208,7 @@
if (startConnectionHandlers)
{
startConnectionHandlers();
+ new IdleTimeLimitThread().start();
}
@@ -7526,6 +7531,35 @@
/**
+ * Retrieves the maximum length of time in milliseconds that client
+ * connections should be allowed to remain idle without being disconnected.
+ *
+ * @return The maximum length of time in milliseconds that client connections
+ * should be allowed to remain idle without being disconnected.
+ */
+ public static long getIdleTimeLimit()
+ {
+ return directoryServer.idleTimeLimit;
+ }
+
+
+
+ /**
+ * Specifies the maximum length of time in milliseconds that client
+ * connections should be allowed to remain idle without being disconnected.
+ *
+ * @param idleTimeLimit The maximum length of time in milliseconds that
+ * client connections should be allowed to remain idle
+ * without being disconnected.
+ */
+ public static void setIdleTimeLimit(long idleTimeLimit)
+ {
+ directoryServer.idleTimeLimit = idleTimeLimit;
+ }
+
+
+
+ /**
* Registers the provided backup task listener with the Directory Server.
*
* @param listener The backup task listener to register with the Directory
diff --git a/opendj-sdk/opends/src/server/org/opends/server/core/IdleTimeLimitThread.java b/opendj-sdk/opends/src/server/org/opends/server/core/IdleTimeLimitThread.java
new file mode 100644
index 0000000..3f228ae
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/core/IdleTimeLimitThread.java
@@ -0,0 +1,193 @@
+/*
+ * 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.core;
+
+
+
+import org.opends.server.admin.std.server.ConnectionHandlerCfg;
+import org.opends.server.api.ClientConnection;
+import org.opends.server.api.ConnectionHandler;
+import org.opends.server.api.DirectoryThread;
+import org.opends.server.api.ServerShutdownListener;
+import org.opends.server.loggers.debug.DebugTracer;
+import org.opends.server.types.DebugLogLevel;
+import org.opends.server.types.ErrorLogCategory;
+import org.opends.server.types.ErrorLogSeverity;
+import org.opends.server.types.DisconnectReason;
+
+import static org.opends.server.loggers.ErrorLogger.*;
+import static org.opends.server.loggers.debug.DebugLogger.*;
+import static org.opends.server.messages.CoreMessages.*;
+import static org.opends.server.messages.MessageHandler.*;
+import static org.opends.server.util.StaticUtils.*;
+
+
+
+/**
+ * This class defines a thread that will be used to teriminate client
+ * connections if they have been idle for too long.
+ */
+public class IdleTimeLimitThread
+ extends DirectoryThread
+ implements ServerShutdownListener
+{
+ /**
+ * The debug log tracer for this object.
+ */
+ private static final DebugTracer TRACER = getTracer();
+
+
+
+ // Indicates whether a shutdown request has been received.
+ private boolean shutdownRequested;
+
+
+
+ /**
+ * Creates a new instance of this idle time limit thread.
+ */
+ public IdleTimeLimitThread()
+ {
+ super("Idle Time Limit Thread");
+ setDaemon(true);
+
+ shutdownRequested = false;
+ DirectoryServer.registerShutdownListener(this);
+ }
+
+
+
+ /**
+ * Operates in a loop, teriminating any client connections that have been idle
+ * for too long.
+ */
+ public void run()
+ {
+ int disconnectMessageID = MSGID_IDLETIME_LIMIT_EXCEEDED;
+ String disconnectMessage = getMessage(disconnectMessageID);
+
+ long sleepTime = 5000L;
+
+ while (! shutdownRequested)
+ {
+ try
+ {
+ try
+ {
+ sleep(sleepTime);
+ } catch (InterruptedException ie) {}
+
+ sleepTime = 5000L;
+ for (ConnectionHandler ch : DirectoryServer.getConnectionHandlers())
+ {
+ ConnectionHandler<? extends ConnectionHandlerCfg> connHandler =
+ (ConnectionHandler<? extends ConnectionHandlerCfg>) ch;
+ for (ClientConnection c : connHandler.getClientConnections())
+ {
+ long idleTime = c.getIdleTime();
+ if (idleTime > 0)
+ {
+ long idleTimeLimit = c.getIdleTimeLimit();
+ if (idleTimeLimit > 0)
+ {
+ if (idleTime > idleTimeLimit)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugInfo("Terminating client connection " +
+ c.getConnectionID() +
+ " due to the idle time limit");
+ }
+
+ try
+ {
+ c.disconnect(DisconnectReason.IDLE_TIME_LIMIT_EXCEEDED,
+ true, disconnectMessage, disconnectMessageID);
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+
+ int msgID = MSGID_IDLETIME_DISCONNECT_ERROR;
+ String message = getMessage(msgID, c.getConnectionID(),
+ stackTraceToSingleLineString(e));
+ logError(ErrorLogCategory.CORE_SERVER,
+ ErrorLogSeverity.MILD_ERROR, message, msgID);
+ }
+ }
+ else
+ {
+ long shouldSleepTime = idleTimeLimit - idleTime;
+ if (shouldSleepTime < sleepTime)
+ {
+ sleepTime = shouldSleepTime;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+
+ int msgID = MSGID_IDLETIME_UNEXPECTED_ERROR;
+ String message = getMessage(msgID, stackTraceToSingleLineString(e));
+ logError(ErrorLogCategory.CORE_SERVER, ErrorLogSeverity.SEVERE_ERROR,
+ message, msgID);
+ }
+ }
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getShutdownListenerName()
+ {
+ return "Idle Time Limit Thread";
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public void processServerShutdown(String reason)
+ {
+ shutdownRequested = true;
+ }
+}
+
diff --git a/opendj-sdk/opends/src/server/org/opends/server/messages/CoreMessages.java b/opendj-sdk/opends/src/server/org/opends/server/messages/CoreMessages.java
index aa3b15f..56aedf4 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/messages/CoreMessages.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/messages/CoreMessages.java
@@ -6277,6 +6277,58 @@
/**
+ * The message ID for the message that will be used if a user entry contains
+ * multiple values for the user-specific idle time limit attribute. This
+ * takes a single argument, which is the DN of the user entry.
+ */
+ public static final int MSGID_BIND_MULTIPLE_USER_IDLE_TIME_LIMITS =
+ CATEGORY_MASK_CORE | SEVERITY_MASK_SEVERE_WARNING | 630;
+
+
+
+ /**
+ * The message ID for the message that will be used if an error occurs while
+ * trying to parse a user-specific idle time limit value as an integer. This
+ * takes two arguments, which are the provided time limit value and the DN of
+ * the user entry.
+ */
+ public static final int MSGID_BIND_CANNOT_PROCESS_USER_IDLE_TIME_LIMIT =
+ CATEGORY_MASK_CORE | SEVERITY_MASK_SEVERE_WARNING | 631;
+
+
+
+ /**
+ * The message ID for the message that will be used to indicate that the idle
+ * time limit has been exceeded for a client connection. It does not take any
+ * arguments.
+ */
+ public static final int MSGID_IDLETIME_LIMIT_EXCEEDED =
+ CATEGORY_MASK_CORE | SEVERITY_MASK_INFORMATIONAL | 632;
+
+
+
+ /**
+ * The message ID for the message that will be used if an error occurred while
+ * trying to terminate a client connection. It takes two arguments, which are
+ * the connection ID and a string representation of the exception that was
+ * caught.
+ */
+ public static final int MSGID_IDLETIME_DISCONNECT_ERROR =
+ CATEGORY_MASK_CORE | SEVERITY_MASK_MILD_ERROR | 632;
+
+
+
+ /**
+ * The message ID for the message that will be used if an unexpected error
+ * occurred in the time thread. It takes a single argument, which is a string
+ * representation of the exception that was caught.
+ */
+ public static final int MSGID_IDLETIME_UNEXPECTED_ERROR =
+ CATEGORY_MASK_CORE | SEVERITY_MASK_SEVERE_ERROR | 632;
+
+
+
+ /**
* Associates a set of generic messages with the message IDs defined
* in this class.
*/
@@ -7104,6 +7156,14 @@
"The user-specific time limit value %s contained in " +
"user entry %s could not be parsed as an integer. The " +
"default server time limit will be used");
+ registerMessage(MSGID_BIND_MULTIPLE_USER_IDLE_TIME_LIMITS,
+ "There are multiple user-specific idle time limit values " +
+ "contained in user entry %s. The default server idle " +
+ "time limit will be used");
+ registerMessage(MSGID_BIND_CANNOT_PROCESS_USER_IDLE_TIME_LIMIT,
+ "The user-specific idle time limit value %s contained in " +
+ "user entry %s could not be parsed as an integer. The " +
+ "default server idle time limit will be used");
registerMessage(MSGID_BIND_PASSWORD_EXPIRING,
"The user password is about to expire (time to " +
"expiration: %s)");
@@ -8549,6 +8609,17 @@
registerMessage(MSGID_ENTRYENCODECFG_INVALID_LENGTH,
"Unable to decode the provided entry encode " +
"configuration element because it has an invalid length");
+
+
+ registerMessage(MSGID_IDLETIME_LIMIT_EXCEEDED,
+ "This connection has been teriminated because it has " +
+ "remained idle for too long");
+ registerMessage(MSGID_IDLETIME_DISCONNECT_ERROR,
+ "An error occurred while attempting to disconnect " +
+ "client connection %d: %s");
+ registerMessage(MSGID_IDLETIME_UNEXPECTED_ERROR,
+ "An unexpected error occurred in the idle time limit " +
+ "thread: %s");
}
}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java b/opendj-sdk/opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java
index 4cf5799..cbf90ee 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java
@@ -89,6 +89,7 @@
import org.opends.server.types.ResultCode;
import org.opends.server.types.SearchResultEntry;
import org.opends.server.types.SearchResultReference;
+import org.opends.server.util.TimeThread;
@@ -108,6 +109,9 @@
+ // The time that the last operation was completed.
+ private AtomicLong lastCompletionTime;
+
// The next operation ID that should be used for this connection.
private AtomicLong nextOperationID;
@@ -240,6 +244,7 @@
ldapVersion = 3;
requestHandler = null;
+ lastCompletionTime = new AtomicLong(TimeThread.getTime());
nextOperationID = new AtomicLong(0);
connectionValid = true;
disconnectRequested = false;
@@ -1210,6 +1215,7 @@
}
operationsInProgress.remove(messageID);
+ lastCompletionTime.set(TimeThread.getTime());
throw de;
}
@@ -1247,7 +1253,15 @@
public boolean removeOperationInProgress(int messageID)
{
AbstractOperation operation = operationsInProgress.remove(messageID);
- return (operation != null);
+ if (operation == null)
+ {
+ return false;
+ }
+ else
+ {
+ lastCompletionTime.set(TimeThread.getTime());
+ return true;
+ }
}
@@ -1333,6 +1347,12 @@
}
}
+ if (! (operationsInProgress.isEmpty() &&
+ getPersistentSearches().isEmpty()))
+ {
+ lastCompletionTime.set(TimeThread.getTime());
+ }
+
operationsInProgress.clear();
@@ -1401,12 +1421,14 @@
}
operationsInProgress.remove(msgID);
+ lastCompletionTime.set(TimeThread.getTime());
}
for (PersistentSearch persistentSearch : getPersistentSearches())
{
DirectoryServer.deregisterPersistentSearch(persistentSearch);
+ lastCompletionTime.set(TimeThread.getTime());
}
}
catch (Exception e)
@@ -2789,5 +2811,32 @@
{
return connectionHandler.getSSLServerCertNickname();
}
+
+
+
+ /**
+ * Retrieves the length of time in milliseconds that this client
+ * connection has been idle.
+ * <BR><BR>
+ * Note that the default implementation will always return zero.
+ * Subclasses associated with connection handlers should override
+ * this method if they wish to provided idle time limit
+ * functionality.
+ *
+ * @return The length of time in milliseconds that this client
+ * connection has been idle.
+ */
+ public long getIdleTime()
+ {
+ if (operationsInProgress.isEmpty() && getPersistentSearches().isEmpty())
+ {
+ return (TimeThread.getTime() - lastCompletionTime.get());
+ }
+ else
+ {
+ // There's at least one operation in progress, so it's not idle.
+ return 0L;
+ }
+ }
}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java b/opendj-sdk/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java
index 5c7790c..b7ace2d 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java
@@ -3091,6 +3091,7 @@
int sizeLimit = DirectoryServer.getSizeLimit();
int timeLimit = DirectoryServer.getTimeLimit();
int lookthroughLimit = DirectoryServer.getLookthroughLimit();
+ long idleTimeLimit = DirectoryServer.getIdleTimeLimit();
boolean skipPostOperation = false;
// The password policy state information for this bind operation.
@@ -3705,6 +3706,53 @@
}
+ // See if the user's entry contains a custom idle time limit.
+ attrType = DirectoryServer.getAttributeType(
+ OP_ATTR_USER_IDLE_TIME_LIMIT, true);
+ attrList = userEntry.getAttribute(attrType);
+ if ((attrList != null) && (attrList.size() == 1))
+ {
+ Attribute a = attrList.get(0);
+ LinkedHashSet<AttributeValue> values = a.getValues();
+ Iterator<AttributeValue> iterator = values.iterator();
+ if (iterator.hasNext())
+ {
+ AttributeValue v = iterator.next();
+ if (iterator.hasNext())
+ {
+ int msgID = MSGID_BIND_MULTIPLE_USER_IDLE_TIME_LIMITS;
+ String message =
+ getMessage(msgID, String.valueOf(userEntry.getDN()));
+ logError(ErrorLogCategory.CORE_SERVER,
+ ErrorLogSeverity.SEVERE_WARNING, message, msgID);
+ }
+ else
+ {
+ try
+ {
+ idleTimeLimit =
+ 1000L * Long.parseLong(v.getStringValue());
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+
+ int msgID =
+ MSGID_BIND_CANNOT_PROCESS_USER_IDLE_TIME_LIMIT;
+ String message =
+ getMessage(msgID, v.getStringValue(),
+ String.valueOf(userEntry.getDN()));
+ logError(ErrorLogCategory.CORE_SERVER,
+ ErrorLogSeverity.SEVERE_WARNING, message, msgID);
+ }
+ }
+ }
+ }
+
+
// See if the user's entry contains a custom lookthrough limit.
attrType =
DirectoryServer.getAttributeType(
@@ -4272,6 +4320,53 @@
}
+ // See if the user's entry contains a custom idle time limit.
+ attrType = DirectoryServer.getAttributeType(
+ OP_ATTR_USER_IDLE_TIME_LIMIT, true);
+ attrList = saslAuthUserEntry.getAttribute(attrType);
+ if ((attrList != null) && (attrList.size() == 1))
+ {
+ Attribute a = attrList.get(0);
+ LinkedHashSet<AttributeValue> values = a.getValues();
+ Iterator<AttributeValue> iterator = values.iterator();
+ if (iterator.hasNext())
+ {
+ AttributeValue v = iterator.next();
+ if (iterator.hasNext())
+ {
+ int msgID = MSGID_BIND_MULTIPLE_USER_IDLE_TIME_LIMITS;
+ String message =
+ getMessage(msgID, String.valueOf(userDNString));
+ logError(ErrorLogCategory.CORE_SERVER,
+ ErrorLogSeverity.SEVERE_WARNING, message, msgID);
+ }
+ else
+ {
+ try
+ {
+ idleTimeLimit =
+ 1000L * Long.parseLong(v.getStringValue());
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+
+ int msgID =
+ MSGID_BIND_CANNOT_PROCESS_USER_IDLE_TIME_LIMIT;
+ String message =
+ getMessage(msgID, v.getStringValue(),
+ String.valueOf(userDNString));
+ logError(ErrorLogCategory.CORE_SERVER,
+ ErrorLogSeverity.SEVERE_WARNING, message, msgID);
+ }
+ }
+ }
+ }
+
+
// See if the user's entry contains a custom lookthrough limit.
attrType =
DirectoryServer.getAttributeType(
@@ -4422,6 +4517,7 @@
clientConnection.setAuthenticationInfo(authInfo);
clientConnection.setSizeLimit(sizeLimit);
clientConnection.setTimeLimit(timeLimit);
+ clientConnection.setIdleTimeLimit(idleTimeLimit);
clientConnection.setLookthroughLimit(lookthroughLimit);
clientConnection.setMustChangePassword(mustChangePassword);
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/IdleTimeLimitTestCase.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/IdleTimeLimitTestCase.java
new file mode 100644
index 0000000..7e1d908
--- /dev/null
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/IdleTimeLimitTestCase.java
@@ -0,0 +1,275 @@
+/*
+ * 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.core;
+
+
+
+import java.net.Socket;
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import org.opends.server.TestCaseUtils;
+import org.opends.server.protocols.asn1.*;
+import org.opends.server.protocols.ldap.*;
+import org.opends.server.tools.dsconfig.DSConfig;
+
+import static org.testng.Assert.*;
+
+
+
+/**
+ * A set of test cases that involve disconnecting clients due to the idle time
+ * limit.
+ */
+public class IdleTimeLimitTestCase
+ extends CoreTestCase
+{
+ /**
+ * Ensures that the Directory Server is running.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @BeforeClass()
+ public void startServer()
+ throws Exception
+ {
+ TestCaseUtils.startServer();
+ }
+
+
+
+ /**
+ * Tests the server-wide idle time limit for an anonymous connection.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test(groups="slow")
+ public void testServerWideAnonymousIdleTimeLimit()
+ throws Exception
+ {
+ String[] args =
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+ "-D", "cn=Directory Manager",
+ "-w", "password",
+ "set-global-configuration-prop",
+ "--set", "idle-time-limit:5 seconds"
+ };
+ assertEquals(DSConfig.main(args, false, System.out, System.err), 0);
+
+
+ Socket s = null;
+ try
+ {
+ s = new Socket("127.0.0.1", TestCaseUtils.getServerLdapPort());
+ ASN1Writer w = new ASN1Writer(s);
+ ASN1Reader r = new ASN1Reader(s);
+ r.setIOTimeout(60000);
+
+ LDAPMessage m = LDAPMessage.decode(r.readElement().decodeAsSequence());
+ ExtendedResponseProtocolOp extendedResponse =
+ m.getExtendedResponseProtocolOp();
+ assertEquals(extendedResponse.getOID(),
+ LDAPConstants.OID_NOTICE_OF_DISCONNECTION);
+
+ assertNull(r.readElement());
+ }
+ finally
+ {
+ try
+ {
+ s.close();
+ } catch (Exception e) {}
+
+ args = new String[]
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+ "-D", "cn=Directory Manager",
+ "-w", "password",
+ "set-global-configuration-prop",
+ "--set", "idle-time-limit:0 seconds"
+ };
+ assertEquals(DSConfig.main(args, false, System.out, System.err), 0);
+ }
+ }
+
+
+
+ /**
+ * Tests the server-wide idle time limit for an authenticated connection.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test(groups="slow")
+ public void testServerWideAuthenticatedIdleTimeLimit()
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(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"
+ );
+
+
+ String[] args =
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+ "-D", "cn=Directory Manager",
+ "-w", "password",
+ "set-global-configuration-prop",
+ "--set", "idle-time-limit:5 seconds"
+ };
+ assertEquals(DSConfig.main(args, false, System.out, System.err), 0);
+
+
+ Socket s = null;
+ try
+ {
+ s = new Socket("127.0.0.1", TestCaseUtils.getServerLdapPort());
+ ASN1Writer w = new ASN1Writer(s);
+ ASN1Reader r = new ASN1Reader(s);
+ r.setIOTimeout(60000);
+
+
+ BindRequestProtocolOp bindRequest = new BindRequestProtocolOp(
+ new ASN1OctetString("uid=test.user,o=test"), 3,
+ new ASN1OctetString("password"));
+ LDAPMessage m = new LDAPMessage(1, bindRequest);
+ w.writeElement(m.encode());
+
+
+ m = LDAPMessage.decode(r.readElement().decodeAsSequence());
+ BindResponseProtocolOp bindResponse = m.getBindResponseProtocolOp();
+ assertEquals(bindResponse.getResultCode(), 0);
+
+
+ m = LDAPMessage.decode(r.readElement().decodeAsSequence());
+ ExtendedResponseProtocolOp extendedResponse =
+ m.getExtendedResponseProtocolOp();
+ assertEquals(extendedResponse.getOID(),
+ LDAPConstants.OID_NOTICE_OF_DISCONNECTION);
+
+ assertNull(r.readElement());
+ }
+ finally
+ {
+ try
+ {
+ s.close();
+ } catch (Exception e) {}
+
+ args = new String[]
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+ "-D", "cn=Directory Manager",
+ "-w", "password",
+ "set-global-configuration-prop",
+ "--set", "idle-time-limit:0 seconds"
+ };
+ assertEquals(DSConfig.main(args, false, System.out, System.err), 0);
+ }
+ }
+
+
+
+ /**
+ * Tests a user-specific idle time limit.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test(groups="slow")
+ public void testUserSpecificIdleTimeLimit()
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(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-rlim-idle-time-limit: 5"
+ );
+
+
+ Socket s = null;
+ try
+ {
+ s = new Socket("127.0.0.1", TestCaseUtils.getServerLdapPort());
+ ASN1Writer w = new ASN1Writer(s);
+ ASN1Reader r = new ASN1Reader(s);
+ r.setIOTimeout(60000);
+
+
+ BindRequestProtocolOp bindRequest = new BindRequestProtocolOp(
+ new ASN1OctetString("uid=test.user,o=test"), 3,
+ new ASN1OctetString("password"));
+ LDAPMessage m = new LDAPMessage(1, bindRequest);
+ w.writeElement(m.encode());
+
+
+ m = LDAPMessage.decode(r.readElement().decodeAsSequence());
+ BindResponseProtocolOp bindResponse = m.getBindResponseProtocolOp();
+ assertEquals(bindResponse.getResultCode(), 0);
+
+
+ m = LDAPMessage.decode(r.readElement().decodeAsSequence());
+ ExtendedResponseProtocolOp extendedResponse =
+ m.getExtendedResponseProtocolOp();
+ assertEquals(extendedResponse.getOID(),
+ LDAPConstants.OID_NOTICE_OF_DISCONNECTION);
+
+ assertNull(r.readElement());
+ }
+ finally
+ {
+ try
+ {
+ s.close();
+ } catch (Exception e) {}
+ }
+ }
+}
+
--
Gitblit v1.10.0