001    /*
002     * CDDL HEADER START
003     *
004     * The contents of this file are subject to the terms of the
005     * Common Development and Distribution License, Version 1.0 only
006     * (the "License").  You may not use this file except in compliance
007     * with the License.
008     *
009     * You can obtain a copy of the license at
010     * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011     * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012     * See the License for the specific language governing permissions
013     * and limitations under the License.
014     *
015     * When distributing Covered Code, include this CDDL HEADER in each
016     * file and include the License file at
017     * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
018     * add the following below this CDDL HEADER, with the fields enclosed
019     * by brackets "[]" replaced with your own identifying information:
020     *      Portions Copyright [yyyy] [name of copyright owner]
021     *
022     * CDDL HEADER END
023     *
024     *
025     *      Copyright 2008 Sun Microsystems, Inc.
026     */
027    package org.opends.server.core;
028    import org.opends.messages.Message;
029    
030    
031    
032    import java.util.concurrent.ConcurrentHashMap;
033    import java.util.concurrent.CopyOnWriteArraySet;
034    
035    import org.opends.server.api.ChangeNotificationListener;
036    import org.opends.server.api.ClientConnection;
037    import org.opends.server.types.DisconnectReason;
038    import org.opends.server.types.DN;
039    import org.opends.server.types.Entry;
040    import org.opends.server.types.operation.PostResponseAddOperation;
041    import org.opends.server.types.operation.PostResponseDeleteOperation;
042    import org.opends.server.types.operation.PostResponseModifyOperation;
043    import org.opends.server.types.operation.PostResponseModifyDNOperation;
044    
045    import static org.opends.messages.CoreMessages.*;
046    /**
047     * This class provides a data structure which maps an authenticated user DN to
048     * the set of client connections authenticated as that user.  Note that a single
049     * client connection may be registered with two different user DNs if the client
050     * has different authentication and authorization identities.
051     * <BR><BR>
052     * This class also provides a mechanism for detecting changes to authenticated
053     * user entries and notifying the corresponding client connections so that they
054     * can update their cached versions.
055     */
056    public class AuthenticatedUsers
057           implements ChangeNotificationListener
058    {
059      // The mapping between authenticated user DNs and the associated client
060      // connection objects.
061      private ConcurrentHashMap<DN,CopyOnWriteArraySet<ClientConnection>>
062                   userMap;
063    
064    
065    
066      /**
067       * Creates a new instance of this authenticated users object.
068       */
069      public AuthenticatedUsers()
070      {
071        userMap = new ConcurrentHashMap<DN,CopyOnWriteArraySet<ClientConnection>>();
072    
073        DirectoryServer.registerChangeNotificationListener(this);
074      }
075    
076    
077    
078      /**
079       * Registers the provided user DN and client connection with this object.
080       *
081       * @param  userDN            The DN of the user associated with the provided
082       *                           client connection.
083       * @param  clientConnection  The client connection over which the user is
084       *                           authenticated.
085       */
086      public synchronized void put(DN userDN, ClientConnection clientConnection)
087      {
088        CopyOnWriteArraySet<ClientConnection> connectionSet = userMap.get(userDN);
089        if (connectionSet == null)
090        {
091          connectionSet = new CopyOnWriteArraySet<ClientConnection>();
092          connectionSet.add(clientConnection);
093          userMap.put(userDN, connectionSet);
094        }
095        else
096        {
097          connectionSet.add(clientConnection);
098        }
099      }
100    
101    
102    
103      /**
104       * Deregisters the provided user DN and client connection with this object.
105       *
106       * @param  userDN            The DN of the user associated with the provided
107       *                           client connection.
108       * @param  clientConnection  The client connection over which the user is
109       *                           authenticated.
110       */
111      public synchronized void remove(DN userDN, ClientConnection clientConnection)
112      {
113        CopyOnWriteArraySet<ClientConnection> connectionSet = userMap.get(userDN);
114        if (connectionSet != null)
115        {
116          connectionSet.remove(clientConnection);
117          if (connectionSet.isEmpty())
118          {
119            userMap.remove(userDN);
120          }
121        }
122      }
123    
124    
125    
126      /**
127       * Retrieves the set of client connections authenticated as the specified
128       * user.  This method is only intended for internal testing use and should not
129       * be called for any other purpose.
130       *
131       * @param  userDN  The DN of the user for which to retrieve the corresponding
132       *                 set of client connections.
133       *
134       * @return  The set of client connections authenticated as the specified user,
135       *          or {@code null} if there are none.
136       */
137      synchronized CopyOnWriteArraySet<ClientConnection> get(DN userDN)
138      {
139        return userMap.get(userDN);
140      }
141    
142    
143    
144      /**
145       * Performs any processing that may be required after an add
146       * operation.
147       *
148       * @param  addOperation  The add operation that was performed in the
149       *                       server.
150       * @param  entry         The entry that was added to the server.
151       */
152      public void handleAddOperation(
153                       PostResponseAddOperation addOperation,
154                       Entry entry)
155      {
156        // No implementation is required for add operations, since a connection
157        // can't be authenticated as a user that doesn't exist yet.
158      }
159    
160    
161    
162      /**
163       * Performs any processing that may be required after a delete
164       * operation.
165       *
166       * @param  deleteOperation  The delete operation that was performed
167       *                          in the server.
168       * @param  entry            The entry that was removed from the
169       *                          server.
170       */
171      public void handleDeleteOperation(
172                       PostResponseDeleteOperation deleteOperation,
173                       Entry entry)
174      {
175        // Identify any client connections that may be authenticated or
176        // authorized as the user whose entry has been deleted and terminate them.
177        CopyOnWriteArraySet<ClientConnection> connectionSet =
178             userMap.remove(entry.getDN());
179        if (connectionSet != null)
180        {
181          for (ClientConnection conn : connectionSet)
182          {
183            Message message = WARN_CLIENTCONNECTION_DISCONNECT_DUE_TO_DELETE.get(
184                    String.valueOf(entry.getDN()));
185    
186            conn.disconnect(DisconnectReason.OTHER, true, message);
187          }
188        }
189      }
190    
191    
192    
193      /**
194       * Performs any processing that may be required after a modify
195       * operation.
196       *
197       * @param  modifyOperation  The modify operation that was performed
198       *                          in the server.
199       * @param  oldEntry         The entry before it was updated.
200       * @param  newEntry         The entry after it was updated.
201       */
202      public void handleModifyOperation(
203                       PostResponseModifyOperation modifyOperation,
204                       Entry oldEntry, Entry newEntry)
205      {
206        // Identify any client connections that may be authenticated or authorized
207        // as the user whose entry has been modified and update them with the latest
208        // version of the entry.
209        CopyOnWriteArraySet<ClientConnection> connectionSet =
210             userMap.get(oldEntry.getDN());
211        if (connectionSet != null)
212        {
213          for (ClientConnection conn : connectionSet)
214          {
215            conn.updateAuthenticationInfo(oldEntry, newEntry);
216          }
217        }
218      }
219    
220    
221    
222      /**
223       * Performs any processing that may be required after a modify DN
224       * operation.
225       *
226       * @param  modifyDNOperation  The modify DN operation that was
227       *                            performed in the server.
228       * @param  oldEntry           The entry before it was updated.
229       * @param  newEntry           The entry after it was updated.
230       */
231      public void handleModifyDNOperation(
232                       PostResponseModifyDNOperation modifyDNOperation,
233                       Entry oldEntry, Entry newEntry)
234      {
235        // Identify any client connections that may be authenticated or authorized
236        // as the user whose entry has been modified and update them with the latest
237        // version of the entry.
238        CopyOnWriteArraySet<ClientConnection> connectionSet =
239             userMap.remove(oldEntry.getDN());
240        if (connectionSet != null)
241        {
242          synchronized (this)
243          {
244            CopyOnWriteArraySet<ClientConnection> existingNewSet =
245                 userMap.get(newEntry.getDN());
246            if (existingNewSet == null)
247            {
248              userMap.put(newEntry.getDN(), connectionSet);
249            }
250            else
251            {
252              existingNewSet.addAll(connectionSet);
253            }
254          }
255    
256          for (ClientConnection conn : connectionSet)
257          {
258            conn.updateAuthenticationInfo(oldEntry, newEntry);
259          }
260        }
261      }
262    }
263