From a09e50d8d41c0f50c486742f4cc2343083c635e3 Mon Sep 17 00:00:00 2001
From: ludovicp <ludovicp@localhost>
Date: Fri, 25 Jun 2010 09:25:49 +0000
Subject: [PATCH] Fixes issues #4552 #4557, making sure plugins and internal services are properly handling subtree move or delete. The changes particularly resolve problems raised by the community with the referential integrity and the isMemberOf plug-ins. Unit-tests have been updated to cover those cases
---
opends/src/server/org/opends/server/core/AuthenticatedUsers.java | 245 ++++++++++++++++++++++++++++++++++++++-----------
1 files changed, 190 insertions(+), 55 deletions(-)
diff --git a/opends/src/server/org/opends/server/core/AuthenticatedUsers.java b/opends/src/server/org/opends/server/core/AuthenticatedUsers.java
index 55e9b70..43226c2 100644
--- a/opends/src/server/org/opends/server/core/AuthenticatedUsers.java
+++ b/opends/src/server/org/opends/server/core/AuthenticatedUsers.java
@@ -22,20 +22,25 @@
* CDDL HEADER END
*
*
- * Copyright 2008 Sun Microsystems, Inc.
+ * Copyright 2008-2010 Sun Microsystems, Inc.
*/
package org.opends.server.core;
+import java.util.HashSet;
+import java.util.Set;
import org.opends.messages.Message;
-import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.opends.server.api.ChangeNotificationListener;
import org.opends.server.api.ClientConnection;
+import org.opends.server.api.DITCacheMap;
+import org.opends.server.loggers.debug.DebugTracer;
import org.opends.server.types.DisconnectReason;
import org.opends.server.types.DN;
+import org.opends.server.types.DebugLogLevel;
import org.opends.server.types.Entry;
import org.opends.server.types.operation.PostResponseAddOperation;
import org.opends.server.types.operation.PostResponseDeleteOperation;
@@ -43,6 +48,8 @@
import org.opends.server.types.operation.PostResponseModifyDNOperation;
import static org.opends.messages.CoreMessages.*;
+import static org.opends.server.loggers.debug.DebugLogger.*;
+
/**
* This class provides a data structure which maps an authenticated user DN to
* the set of client connections authenticated as that user. Note that a single
@@ -56,11 +63,17 @@
public class AuthenticatedUsers
implements ChangeNotificationListener
{
+ /**
+ * The tracer object for the debug logger.
+ */
+ private static final DebugTracer TRACER = getTracer();
+
// The mapping between authenticated user DNs and the associated client
// connection objects.
- private ConcurrentHashMap<DN,CopyOnWriteArraySet<ClientConnection>>
- userMap;
+ private DITCacheMap<CopyOnWriteArraySet<ClientConnection>> userMap;
+ // Lock to protect internal data structures.
+ private final ReentrantReadWriteLock lock;
/**
@@ -68,7 +81,8 @@
*/
public AuthenticatedUsers()
{
- userMap = new ConcurrentHashMap<DN,CopyOnWriteArraySet<ClientConnection>>();
+ userMap = new DITCacheMap<CopyOnWriteArraySet<ClientConnection>>();
+ lock = new ReentrantReadWriteLock();
DirectoryServer.registerChangeNotificationListener(this);
}
@@ -83,18 +97,27 @@
* @param clientConnection The client connection over which the user is
* authenticated.
*/
- public synchronized void put(DN userDN, ClientConnection clientConnection)
+ public void put(DN userDN, ClientConnection clientConnection)
{
- CopyOnWriteArraySet<ClientConnection> connectionSet = userMap.get(userDN);
- if (connectionSet == null)
+ lock.writeLock().lock();
+ try
{
- connectionSet = new CopyOnWriteArraySet<ClientConnection>();
- connectionSet.add(clientConnection);
- userMap.put(userDN, connectionSet);
+ CopyOnWriteArraySet<ClientConnection> connectionSet =
+ userMap.get(userDN);
+ if (connectionSet == null)
+ {
+ connectionSet = new CopyOnWriteArraySet<ClientConnection>();
+ connectionSet.add(clientConnection);
+ userMap.put(userDN, connectionSet);
+ }
+ else
+ {
+ connectionSet.add(clientConnection);
+ }
}
- else
+ finally
{
- connectionSet.add(clientConnection);
+ lock.writeLock().unlock();
}
}
@@ -108,17 +131,26 @@
* @param clientConnection The client connection over which the user is
* authenticated.
*/
- public synchronized void remove(DN userDN, ClientConnection clientConnection)
+ public void remove(DN userDN, ClientConnection clientConnection)
{
- CopyOnWriteArraySet<ClientConnection> connectionSet = userMap.get(userDN);
- if (connectionSet != null)
+ lock.writeLock().lock();
+ try
{
- connectionSet.remove(clientConnection);
- if (connectionSet.isEmpty())
+ CopyOnWriteArraySet<ClientConnection> connectionSet =
+ userMap.get(userDN);
+ if (connectionSet != null)
{
- userMap.remove(userDN);
+ connectionSet.remove(clientConnection);
+ if (connectionSet.isEmpty())
+ {
+ userMap.remove(userDN);
+ }
}
}
+ finally
+ {
+ lock.writeLock().unlock();
+ }
}
@@ -136,7 +168,15 @@
*/
synchronized CopyOnWriteArraySet<ClientConnection> get(DN userDN)
{
- return userMap.get(userDN);
+ lock.readLock().lock();
+ try
+ {
+ return userMap.get(userDN);
+ }
+ finally
+ {
+ lock.readLock().unlock();
+ }
}
@@ -172,11 +212,22 @@
PostResponseDeleteOperation deleteOperation,
Entry entry)
{
- // Identify any client connections that may be authenticated or
- // authorized as the user whose entry has been deleted and terminate them.
- CopyOnWriteArraySet<ClientConnection> connectionSet =
- userMap.remove(entry.getDN());
- if (connectionSet != null)
+ // Identify any client connections that may be authenticated
+ // or authorized as the user whose entry has been deleted and
+ // terminate them.
+ Set<CopyOnWriteArraySet<ClientConnection>> arraySet =
+ new HashSet<CopyOnWriteArraySet<ClientConnection>>();
+ lock.writeLock().lock();
+ try
+ {
+ userMap.removeSubtree(entry.getDN(), arraySet);
+ }
+ finally
+ {
+ lock.writeLock().unlock();
+ }
+ for (CopyOnWriteArraySet<ClientConnection>
+ connectionSet : arraySet)
{
for (ClientConnection conn : connectionSet)
{
@@ -203,18 +254,26 @@
PostResponseModifyOperation modifyOperation,
Entry oldEntry, Entry newEntry)
{
- // Identify any client connections that may be authenticated or authorized
- // as the user whose entry has been modified and update them with the latest
- // version of the entry.
- CopyOnWriteArraySet<ClientConnection> connectionSet =
- userMap.get(oldEntry.getDN());
- if (connectionSet != null)
+ // Identify any client connections that may be authenticated
+ // or authorized as the user whose entry has been modified
+ // and update them with the latest version of the entry.
+ lock.writeLock().lock();
+ try
{
- for (ClientConnection conn : connectionSet)
+ CopyOnWriteArraySet<ClientConnection> connectionSet =
+ userMap.get(oldEntry.getDN());
+ if (connectionSet != null)
{
- conn.updateAuthenticationInfo(oldEntry, newEntry);
+ for (ClientConnection conn : connectionSet)
+ {
+ conn.updateAuthenticationInfo(oldEntry, newEntry);
+ }
}
}
+ finally
+ {
+ lock.writeLock().unlock();
+ }
}
@@ -232,32 +291,108 @@
PostResponseModifyDNOperation modifyDNOperation,
Entry oldEntry, Entry newEntry)
{
- // Identify any client connections that may be authenticated or authorized
- // as the user whose entry has been modified and update them with the latest
- // version of the entry.
- CopyOnWriteArraySet<ClientConnection> connectionSet =
- userMap.remove(oldEntry.getDN());
- if (connectionSet != null)
- {
- synchronized (this)
- {
- CopyOnWriteArraySet<ClientConnection> existingNewSet =
- userMap.get(newEntry.getDN());
- if (existingNewSet == null)
- {
- userMap.put(newEntry.getDN(), connectionSet);
- }
- else
- {
- existingNewSet.addAll(connectionSet);
- }
- }
+ String oldDNString = oldEntry.getDN().toNormalizedString();
+ String newDNString = newEntry.getDN().toNormalizedString();
- for (ClientConnection conn : connectionSet)
+ // Identify any client connections that may be authenticated
+ // or authorized as the user whose entry has been modified
+ // and update them with the latest version of the entry.
+ lock.writeLock().lock();
+ try
+ {
+ Set<CopyOnWriteArraySet<ClientConnection>> arraySet =
+ new HashSet<CopyOnWriteArraySet<ClientConnection>>();
+ userMap.removeSubtree(oldEntry.getDN(), arraySet);
+ for (CopyOnWriteArraySet<ClientConnection>
+ connectionSet : arraySet)
{
- conn.updateAuthenticationInfo(oldEntry, newEntry);
+ DN authNDN = null;
+ DN authZDN = null;
+ DN newAuthNDN = null;
+ DN newAuthZDN = null;
+ CopyOnWriteArraySet<ClientConnection> newAuthNSet = null;
+ CopyOnWriteArraySet<ClientConnection> newAuthZSet = null;
+ for (ClientConnection conn : connectionSet)
+ {
+ if (authNDN == null)
+ {
+ authNDN = conn.getAuthenticationInfo().getAuthenticationDN();
+ try
+ {
+ StringBuilder builder = new StringBuilder(
+ authNDN.toNormalizedString());
+ int oldDNIndex = builder.lastIndexOf(oldDNString);
+ builder.replace(oldDNIndex, builder.length(),
+ newDNString);
+ String newAuthNDNString = builder.toString();
+ newAuthNDN = DN.decode(newAuthNDNString);
+ }
+ catch (Exception e)
+ {
+ // Shouldnt happen.
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+ }
+ }
+ if (authZDN == null)
+ {
+ authZDN = conn.getAuthenticationInfo().getAuthorizationDN();
+ try
+ {
+ StringBuilder builder = new StringBuilder(
+ authZDN.toNormalizedString());
+ int oldDNIndex = builder.lastIndexOf(oldDNString);
+ builder.replace(oldDNIndex, builder.length(),
+ newDNString);
+ String newAuthZDNString = builder.toString();
+ newAuthZDN = DN.decode(newAuthZDNString);
+ }
+ catch (Exception e)
+ {
+ // Shouldnt happen.
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+ }
+ }
+ if ((newAuthNDN != null) && (authNDN != null) &&
+ authNDN.isDescendantOf(oldEntry.getDN()))
+ {
+ if (newAuthNSet == null)
+ {
+ newAuthNSet = new CopyOnWriteArraySet<ClientConnection>();
+ }
+ conn.getAuthenticationInfo().setAuthenticationDN(newAuthNDN);
+ newAuthNSet.add(conn);
+ }
+ if ((newAuthZDN != null) && (authZDN != null) &&
+ authZDN.isDescendantOf(oldEntry.getDN()))
+ {
+ if (newAuthZSet == null)
+ {
+ newAuthZSet = new CopyOnWriteArraySet<ClientConnection>();
+ }
+ conn.getAuthenticationInfo().setAuthorizationDN(newAuthZDN);
+ newAuthZSet.add(conn);
+ }
+ }
+ if ((newAuthNDN != null) && (newAuthNSet != null))
+ {
+ userMap.put(newAuthNDN, newAuthNSet);
+ }
+ if ((newAuthZDN != null) && (newAuthZSet != null))
+ {
+ userMap.put(newAuthZDN, newAuthZSet);
+ }
}
}
+ finally
+ {
+ lock.writeLock().unlock();
+ }
}
}
--
Gitblit v1.10.0