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 2006-2008 Sun Microsystems, Inc.
026     */
027    package org.opends.server.api;
028    
029    
030    
031    import java.net.InetAddress;
032    import java.nio.ByteBuffer;
033    import java.nio.channels.Selector;
034    import java.util.Collection;
035    import java.util.HashSet;
036    import java.util.List;
037    import java.util.Set;
038    import java.util.concurrent.CopyOnWriteArrayList;
039    
040    import org.opends.messages.Message;
041    import org.opends.server.api.plugin.PluginResult;
042    import org.opends.server.core.DirectoryServer;
043    import org.opends.server.core.PersistentSearch;
044    import org.opends.server.core.PluginConfigManager;
045    import org.opends.server.core.SearchOperation;
046    import org.opends.server.core.NetworkGroup;
047    import org.opends.server.loggers.debug.DebugTracer;
048    import org.opends.server.types.AbstractOperation;
049    import org.opends.server.types.Attribute;
050    import org.opends.server.types.AttributeType;
051    import org.opends.server.types.AttributeValue;
052    import org.opends.server.types.AuthenticationInfo;
053    import org.opends.server.types.CancelRequest;
054    import org.opends.server.types.CancelResult;
055    import org.opends.server.types.DebugLogLevel;
056    import org.opends.server.types.DirectoryException;
057    import org.opends.server.types.DisconnectReason;
058    import org.opends.server.types.DN;
059    import org.opends.server.types.Entry;
060    import org.opends.server.types.IntermediateResponse;
061    import org.opends.server.types.Operation;
062    import org.opends.server.types.Privilege;
063    import org.opends.server.types.SearchResultEntry;
064    import org.opends.server.types.SearchResultReference;
065    import org.opends.server.util.TimeThread;
066    
067    import static org.opends.messages.CoreMessages.*;
068    import static org.opends.server.config.ConfigConstants.*;
069    import static org.opends.server.loggers.debug.DebugLogger.*;
070    import static org.opends.server.util.StaticUtils.*;
071    
072    
073    
074    /**
075     * This class defines the set of methods and structures that must be
076     * implemented by a Directory Server client connection.
077     */
078    @org.opends.server.types.PublicAPI(
079         stability=org.opends.server.types.StabilityLevel.VOLATILE,
080         mayInstantiate=true,
081         mayExtend=true,
082         mayInvoke=true)
083    public abstract class ClientConnection
084    {
085      /**
086       * The tracer object for the debug logger.
087       */
088      private static final DebugTracer TRACER = getTracer();
089    
090      // The set of authentication information for this client connection.
091      private AuthenticationInfo authenticationInfo;
092    
093      // Indicates whether a bind is currently in progress on this client
094      // connection.  If so, then no other operations should be allowed
095      // until the bind completes.
096      private boolean bindInProgress;
097    
098      // Indicates whether any necessary finalization work has been done
099      // for this client connection.
100      private boolean finalized;
101    
102      // The set of privileges assigned to this client connection.
103      private HashSet<Privilege> privileges;
104    
105      // The size limit for use with this client connection.
106      private int sizeLimit;
107    
108      // The time limit for use with this client connection.
109      private int timeLimit;
110    
111      // The lookthrough limit for use with this client connection.
112      private int lookthroughLimit;
113    
114      // The time that this client connection was established.
115      private long connectTime;
116    
117      // The idle time limit for this client connection.
118      private long idleTimeLimit;
119    
120      // The opaque information used for storing intermediate state
121      // information needed across multi-stage SASL binds.
122      private Object saslAuthState;
123    
124      // A string representation of the time that this client connection
125      // was established.
126      private String connectTimeString;
127    
128      // A set of persistent searches registered for this client.
129      private CopyOnWriteArrayList<PersistentSearch> persistentSearches;
130    
131      // The network group to which the connection belongs to.
132      private NetworkGroup networkGroup;
133    
134    
135    
136      /**
137       * Performs the appropriate initialization generic to all client
138       * connections.
139       */
140      protected ClientConnection()
141      {
142        connectTime        = TimeThread.getTime();
143        connectTimeString  = TimeThread.getGMTTime();
144        authenticationInfo = new AuthenticationInfo();
145        saslAuthState      = null;
146        bindInProgress     = false;
147        persistentSearches = new CopyOnWriteArrayList<PersistentSearch>();
148        sizeLimit          = DirectoryServer.getSizeLimit();
149        timeLimit          = DirectoryServer.getTimeLimit();
150        idleTimeLimit      = DirectoryServer.getIdleTimeLimit();
151        lookthroughLimit   = DirectoryServer.getLookthroughLimit();
152        finalized          = false;
153        privileges         = new HashSet<Privilege>();
154        networkGroup       = NetworkGroup.getDefaultNetworkGroup();
155      }
156    
157    
158    
159      /**
160       * Performs any internal cleanup that may be necessary when this
161       * client connection is disconnected, or if not on disconnec, then
162       * ultimately whenever it is reaped by the garbage collector.  In
163       * this case, it will be used to ensure that the connection is
164       * deregistered with the {@code AuthenticatedUsers} manager, and
165       * will then invoke the {@code finalizeClientConnection} method.
166       */
167     @org.opends.server.types.PublicAPI(
168          stability=org.opends.server.types.StabilityLevel.PRIVATE,
169          mayInstantiate=false,
170          mayExtend=false,
171          mayInvoke=true,
172          notes="This method should only be invoked by connection " +
173                 "handlers.")
174      protected final void finalizeConnectionInternal()
175      {
176        if (finalized)
177        {
178          return;
179        }
180    
181        finalized = true;
182    
183        // Deregister with the set of authenticated users.
184        Entry authNEntry = authenticationInfo.getAuthenticationEntry();
185        Entry authZEntry = authenticationInfo.getAuthorizationEntry();
186    
187        if (authNEntry != null)
188        {
189          if ((authZEntry == null) ||
190              authZEntry.getDN().equals(authNEntry.getDN()))
191          {
192            DirectoryServer.getAuthenticatedUsers().remove(
193                 authNEntry.getDN(), this);
194          }
195          else
196          {
197            DirectoryServer.getAuthenticatedUsers().remove(
198                 authNEntry.getDN(), this);
199            DirectoryServer.getAuthenticatedUsers().remove(
200                 authZEntry.getDN(), this);
201          }
202        }
203        else if (authZEntry != null)
204        {
205          DirectoryServer.getAuthenticatedUsers().remove(
206               authZEntry.getDN(), this);
207        }
208    
209        try
210        {
211          finalizeClientConnection();
212        }
213        catch (Exception e)
214        {
215          if (debugEnabled())
216          {
217            TRACER.debugCaught(DebugLogLevel.ERROR, e);
218          }
219        }
220      }
221    
222    
223    
224      /**
225       * Performs any cleanup work that may be necessary when this client
226       * connection is terminated.  By default, no action is taken.
227       * <BR><BR>
228       * If possible, this method will be invoked when the client
229       * connection is disconnected.  If it isn't invoked at that time,
230       * then it will be called when the client connection object is
231       * finalized by the garbage collector.
232       */
233     @org.opends.server.types.PublicAPI(
234          stability=org.opends.server.types.StabilityLevel.VOLATILE,
235          mayInstantiate=false,
236          mayExtend=true,
237          mayInvoke=false)
238      protected void finalizeClientConnection()
239      {
240        // No implementation is required by default.
241      }
242    
243    
244    
245      /**
246       * Retrieves the time that this connection was established, measured
247       * in the number of milliseconds since January 1, 1970 UTC.
248       *
249       * @return  The time that this connection was established, measured
250       *          in the number of milliseconds since January 1, 1970 UTC.
251       */
252      public final long getConnectTime()
253      {
254        return connectTime;
255      }
256    
257    
258    
259      /**
260       * Retrieves a string representation of the time that this
261       * connection was established.
262       *
263       * @return  A string representation of the time that this connection
264       *          was established.
265       */
266      public final String getConnectTimeString()
267      {
268        return connectTimeString;
269      }
270    
271    
272    
273      /**
274       * Retrieves the unique identifier that has been assigned to this
275       * connection.
276       *
277       * @return  The unique identifier that has been assigned to this
278       *          connection.
279       */
280      public abstract long getConnectionID();
281    
282    
283    
284      /**
285       * Retrieves the connection handler that accepted this client
286       * connection.
287       *
288       * @return  The connection handler that accepted this client
289       *          connection.
290       */
291      public abstract ConnectionHandler getConnectionHandler();
292    
293    
294    
295      /**
296       * Retrieves the protocol that the client is using to communicate
297       * with the Directory Server.
298       *
299       * @return  The protocol that the client is using to communicate
300       *          with the Directory Server.
301       */
302      public abstract String getProtocol();
303    
304    
305    
306      /**
307       * Retrieves a string representation of the address of the client.
308       *
309       * @return  A string representation of the address of the client.
310       */
311      public abstract String getClientAddress();
312    
313    
314    
315      /**
316       * Retrieves a string representation of the address on the server to
317       * which the client connected.
318       *
319       * @return  A string representation of the address on the server to
320       *          which the client connected.
321       */
322      public abstract String getServerAddress();
323    
324    
325    
326      /**
327       * Retrieves the {@code java.net.InetAddress} associated with the
328       * remote client system.
329       *
330       * @return  The {@code java.net.InetAddress} associated with the
331       *          remote client system.  It may be {@code null} if the
332       *          client is not connected over an IP-based connection.
333       */
334      public abstract InetAddress getRemoteAddress();
335    
336    
337    
338      /**
339       * Retrieves the {@code java.net.InetAddress} for the Directory
340       * Server system to which the client has established the connection.
341       *
342       * @return  The {@code java.net.InetAddress} for the Directory
343       *          Server system to which the client has established the
344       *          connection.  It may be {@code null} if the client is not
345       *          connected over an IP-based connection.
346       */
347      public abstract InetAddress getLocalAddress();
348    
349    
350    
351      /**
352       * Indicates whether this client connection is currently using a
353       * secure mechanism to communicate with the server.  Note that this
354       * may change over time based on operations performed by the client
355       * or server (e.g., it may go from {@code false} to {@code true} if
356       * if the client uses the StartTLS extended operation).
357       *
358       * @return  {@code true} if the client connection is currently using
359       *          a secure mechanism to communicate with the server, or
360       *          {@code false} if not.
361       */
362      public abstract boolean isSecure();
363    
364    
365    
366      /**
367       * Retrieves the connection security provider for this client
368       * connection.
369       *
370       * @return  The connection security provider for this client
371       *          connection.
372       */
373      public abstract ConnectionSecurityProvider
374                           getConnectionSecurityProvider();
375    
376    
377    
378      /**
379       * Specifies the connection security provider for this client
380       * connection.
381       *
382       * @param  securityProvider  The connection security provider to use
383       *                           for communication on this client
384       *                           connection.
385       */
386      public abstract void setConnectionSecurityProvider(
387                                ConnectionSecurityProvider
388                                     securityProvider);
389    
390    
391    
392      /**
393       * Retrieves the human-readable name of the security mechanism that
394       * is used to protect communication with this client.
395       *
396       * @return  The human-readable name of the security mechanism that
397       *          is used to protect communication with this client, or
398       *          {@code null} if no security is in place.
399       */
400      public abstract String getSecurityMechanism();
401    
402    
403    
404      /**
405       * Retrieves a {@code Selector} that may be used to ensure that
406       * write  operations complete in a timely manner, or terminate the
407       * connection in the event that they fail to do so.  This is an
408       * optional method for client connections, and the default
409       * implementation returns {@code null} to indicate that the maximum
410       * blocked write time limit is not supported for this connection.
411       * Subclasses that do wish to support this functionality should
412       * return a valid {@code Selector} object.
413       *
414       * @return  The {@code Selector} that may be used to ensure that
415       *          write operations complete in a timely manner, or
416       *          {@code null} if this client connection does not support
417       *          maximum blocked write time limit functionality.
418       */
419      public Selector getWriteSelector()
420      {
421        // There will not be a write selector in the default
422        // implementation.
423        return null;
424      }
425    
426    
427    
428      /**
429       * Retrieves the maximum length of time in milliseconds that
430       * attempts to write data to the client should be allowed to block.
431       * A value of zero indicates there should be no limit.
432       *
433       * @return  The maximum length of time in milliseconds that attempts
434       *          to write data to the client should be allowed to block,
435       *          or zero if there should be no limit.
436       */
437      public long getMaxBlockedWriteTimeLimit()
438      {
439        // By default, we'll return 0, which indicates that there should
440        // be no maximum time limit.  Subclasses should override this if
441        // they want to support a maximum blocked write time limit.
442        return 0L;
443      }
444    
445    
446    
447      /**
448       * Indicates that the data in the provided buffer has been read from
449       * the client and should be processed.  The contents of the provided
450       * buffer will be in clear-text (the data may have been passed
451       * through a connection security provider to obtain the clear-text
452       * version), and may contain part or all of one or more client
453       * requests.
454       *
455       * @param  buffer  The byte buffer containing the data available for
456       *                 reading.
457       *
458       * @return  {@code true} if all the data in the provided buffer was
459       *          processed and the client connection can remain
460       *          established, or {@code false} if a decoding error
461       *          occurred and requests from this client should no longer
462       *          be processed.  Note that if this method does return
463       *          {@code false}, then it must have already disconnected
464       *          the client.
465       */
466      public abstract boolean processDataRead(ByteBuffer buffer);
467    
468    
469    
470      /**
471       * Sends a response to the client based on the information in the
472       * provided operation.
473       *
474       * @param  operation  The operation for which to send the response.
475       */
476      public abstract void sendResponse(Operation operation);
477    
478    
479    
480      /**
481       * Sends the provided search result entry to the client.
482       *
483       * @param  searchOperation  The search operation with which the
484       *                          entry is associated.
485       * @param  searchEntry      The search result entry to be sent to
486       *                          the client.
487       *
488       * @throws  DirectoryException  If a problem occurs while attempting
489       *                              to send the entry to the client and
490       *                              the search should be terminated.
491       */
492      public abstract void sendSearchEntry(
493                                SearchOperation searchOperation,
494                                SearchResultEntry searchEntry)
495             throws DirectoryException;
496    
497    
498    
499      /**
500       * Sends the provided search result reference to the client.
501       *
502       * @param  searchOperation  The search operation with which the
503       *                          reference is associated.
504       * @param  searchReference  The search result reference to be sent
505       *                          to the client.
506       *
507       * @return  {@code true} if the client is able to accept referrals,
508       *          or {@code false} if the client cannot handle referrals
509       *          and no more attempts should be made to send them for the
510       *          associated search operation.
511       *
512       * @throws  DirectoryException  If a problem occurs while attempting
513       *                              to send the reference to the client
514       *                              and the search should be terminated.
515       */
516      public abstract boolean sendSearchReference(
517                                   SearchOperation searchOperation,
518                                   SearchResultReference searchReference)
519             throws DirectoryException;
520    
521    
522    
523      /**
524       * Invokes the intermediate response plugins on the provided
525       * response message and sends it to the client.
526       *
527       * @param  intermediateResponse  The intermediate response message
528       *                               to be sent.
529       *
530       * @return  {@code true} if processing on the associated operation
531       *          should continue, or {@code false} if not.
532       */
533      public final boolean sendIntermediateResponse(
534                                IntermediateResponse intermediateResponse)
535      {
536        // Invoke the intermediate response plugins for the response
537        // message.
538        PluginConfigManager pluginConfigManager =
539             DirectoryServer.getPluginConfigManager();
540        PluginResult.IntermediateResponse pluginResult =
541             pluginConfigManager.invokeIntermediateResponsePlugins(
542                                      intermediateResponse);
543    
544        boolean continueProcessing = true;
545        if (pluginResult.sendResponse())
546        {
547          continueProcessing =
548               sendIntermediateResponseMessage(intermediateResponse);
549        }
550    
551        return (continueProcessing && pluginResult.continueProcessing());
552      }
553    
554    
555    
556    
557      /**
558       * Sends the provided intermediate response message to the client.
559       *
560       * @param  intermediateResponse  The intermediate response message
561       *                               to be sent.
562       *
563       * @return  {@code true} if processing on the associated operation
564       *          should continue, or {@code false} if not.
565       */
566      protected abstract boolean
567           sendIntermediateResponseMessage(
568                IntermediateResponse intermediateResponse);
569    
570    
571    
572      /**
573       * Closes the connection to the client, optionally sending it a
574       * message indicating the reason for the closure.  Note that the
575       * ability to send a notice of disconnection may not be available
576       * for all protocols or under all circumstances.  Also note that
577       * when attempting to disconnect a client connection as a part of
578       * operation processing (e.g., within a plugin or other extension),
579       * the {@code disconnectClient} method within that operation should
580       * be called rather than invoking this method directly.
581       * <BR><BR>
582       * All subclasses must invoke the {@code finalizeConnectionInternal}
583       * method during the course of processing this method.
584       *
585       * @param  disconnectReason  The disconnect reason that provides the
586       *                           generic cause for the disconnect.
587       * @param  sendNotification  Indicates whether to try to provide
588       *                           notification to the client that the
589       *                           connection will be closed.
590       * @param  message           The message to send to the client.  It
591       *                           may be {@code null} if no notification
592       *                           is to be sent.
593       */
594      public abstract void disconnect(DisconnectReason disconnectReason,
595                                      boolean sendNotification,
596                                      Message message);
597    
598    
599    
600      /**
601       * Indicates whether a bind operation is in progress on this client
602       * connection.  If so, then no new operations should be allowed
603       * until the bind has completed.
604       *
605       * @return  {@code true} if a bind operation is in progress on this
606       *          connection, or {@code false} if not.
607       */
608      public boolean bindInProgress()
609      {
610        return bindInProgress;
611      }
612    
613    
614    
615      /**
616       * Specifies whether a bind operation is in progress on this client
617       * connection.  If so, then no new operations should be allowed
618       * until the bind has completed.
619       *
620       * @param  bindInProgress  Specifies whether a bind operation is in
621       *                         progress on this client connection.
622       */
623      public void setBindInProgress(boolean bindInProgress)
624      {
625        this.bindInProgress = bindInProgress;
626      }
627    
628    
629    
630      /**
631       * Indicates whether the user associated with this client connection
632       * must change their password before they will be allowed to do
633       * anything else.
634       *
635       * @return  {@code true} if the user associated with this client
636       *          connection must change their password before they will
637       *          be allowed to do anything else, or {@code false} if not.
638       */
639      public final boolean mustChangePassword()
640      {
641        if (authenticationInfo == null)
642        {
643          return false;
644        }
645        else
646        {
647          return authenticationInfo.mustChangePassword();
648        }
649      }
650    
651    
652    
653      /**
654       * Specifies whether the user associated with this client connection
655       * must change their password before they will be allowed to do
656       * anything else.
657       *
658       * @param  mustChangePassword  Specifies whether the user associated
659       *                             with this client connection must
660       *                             change their password before they
661       *                             will be allowed to do anything else.
662       */
663      public final void setMustChangePassword(boolean mustChangePassword)
664      {
665        if (authenticationInfo == null)
666        {
667          setAuthenticationInfo(new AuthenticationInfo());
668        }
669    
670        authenticationInfo.setMustChangePassword(mustChangePassword);
671      }
672    
673    
674    
675      /**
676       * Retrieves the set of operations in progress for this client
677       * connection.  This list must not be altered by any caller.
678       *
679       * @return  The set of operations in progress for this client
680       *          connection.
681       */
682      public abstract Collection<AbstractOperation>
683                                          getOperationsInProgress();
684    
685    
686    
687      /**
688       * Retrieves the operation in progress with the specified message
689       * ID.
690       *
691       * @param  messageID  The message ID of the operation to retrieve.
692       *
693       * @return  The operation in progress with the specified message ID,
694       *          or {@code null} if no such operation could be found.
695       */
696      public abstract AbstractOperation
697                              getOperationInProgress(int messageID);
698    
699    
700    
701      /**
702       * Removes the provided operation from the set of operations in
703       * progress for this client connection.  Note that this does not
704       * make any attempt to cancel any processing that may already be in
705       * progress for the operation.
706       *
707       * @param  messageID  The message ID of the operation to remove from
708       *                    the set of operations in progress.
709       *
710       * @return  {@code true} if the operation was found and removed from
711       *          the set of operations in progress, or {@code false} if
712       *          not.
713       */
714      public abstract boolean removeOperationInProgress(int messageID);
715    
716    
717    
718      /**
719       * Retrieves the set of persistent searches registered for this
720       * client.
721       *
722       * @return  The set of persistent searches registered for this
723       *          client.
724       */
725      public final CopyOnWriteArrayList<PersistentSearch>
726                        getPersistentSearches()
727      {
728        return persistentSearches;
729      }
730    
731    
732    
733      /**
734       * Registers the provided persistent search for this client.  Note
735       * that this should only be called by
736       * {@code DirectoryServer.registerPersistentSearch} and not through
737       * any other means.
738       *
739       * @param  persistentSearch  The persistent search to register for
740       *                           this client.
741       */
742     @org.opends.server.types.PublicAPI(
743          stability=org.opends.server.types.StabilityLevel.PRIVATE,
744          mayInstantiate=false,
745          mayExtend=false,
746          mayInvoke=false)
747      public final void registerPersistentSearch(PersistentSearch
748                                                      persistentSearch)
749      {
750        persistentSearches.add(persistentSearch);
751      }
752    
753    
754    
755      /**
756       * Deregisters the provided persistent search for this client.  Note
757       * that this should only be called by
758       * {@code DirectoryServer.deregisterPersistentSearch} and not
759       * through any other means.
760       *
761       * @param  persistentSearch  The persistent search to deregister for
762       *                           this client.
763       */
764     @org.opends.server.types.PublicAPI(
765          stability=org.opends.server.types.StabilityLevel.PRIVATE,
766          mayInstantiate=false,
767          mayExtend=false,
768          mayInvoke=false)
769      public final void deregisterPersistentSearch(PersistentSearch
770                                                        persistentSearch)
771      {
772        persistentSearches.remove(persistentSearch);
773      }
774    
775    
776    
777      /**
778       * Attempts to cancel the specified operation.
779       *
780       * @param  messageID      The message ID of the operation to cancel.
781       * @param  cancelRequest  An object providing additional information
782       *                        about how the cancel should be processed.
783       *
784       * @return  A cancel result that either indicates that the cancel
785       *          was successful or provides a reason that it was not.
786       */
787      public abstract CancelResult cancelOperation(int messageID,
788                                        CancelRequest cancelRequest);
789    
790    
791    
792      /**
793       * Attempts to cancel all operations in progress on this connection.
794       *
795       * @param  cancelRequest  An object providing additional information
796       *                        about how the cancel should be processed.
797       */
798      public abstract void cancelAllOperations(
799                                CancelRequest cancelRequest);
800    
801    
802    
803      /**
804       * Attempts to cancel all operations in progress on this connection
805       * except the operation with the specified message ID.
806       *
807       * @param  cancelRequest  An object providing additional information
808       *                        about how the cancel should be processed.
809       * @param  messageID      The message ID of the operation that
810       *                        should not be canceled.
811       */
812      public abstract void cancelAllOperationsExcept(
813                                CancelRequest cancelRequest,
814                                int messageID);
815    
816    
817    
818      /**
819       * Retrieves information about the authentication that has been
820       * performed for this connection.
821       *
822       * @return  Information about the user that is currently
823       *          authenticated on this connection.
824       */
825      public AuthenticationInfo getAuthenticationInfo()
826      {
827        return authenticationInfo;
828      }
829    
830    
831    
832      /**
833       * Specifies information about the authentication that has been
834       * performed for this connection.
835       *
836       * @param  authenticationInfo  Information about the authentication
837       *                             that has been performed for this
838       *                             connection.  It should not be
839       *                             {@code null}.
840       */
841      public void setAuthenticationInfo(AuthenticationInfo
842                                             authenticationInfo)
843      {
844        if (this.authenticationInfo != null)
845        {
846          Entry authNEntry =
847                     this.authenticationInfo.getAuthenticationEntry();
848          Entry authZEntry =
849                     this.authenticationInfo.getAuthorizationEntry();
850    
851          if (authNEntry != null)
852          {
853            if ((authZEntry == null) ||
854                authZEntry.getDN().equals(authNEntry.getDN()))
855            {
856              DirectoryServer.getAuthenticatedUsers().remove(
857                   authNEntry.getDN(), this);
858            }
859            else
860            {
861              DirectoryServer.getAuthenticatedUsers().remove(
862                   authNEntry.getDN(), this);
863              DirectoryServer.getAuthenticatedUsers().remove(
864                   authZEntry.getDN(), this);
865            }
866          }
867          else if (authZEntry != null)
868          {
869            DirectoryServer.getAuthenticatedUsers().remove(
870                 authZEntry.getDN(), this);
871          }
872        }
873    
874        if (authenticationInfo == null)
875        {
876          this.authenticationInfo = new AuthenticationInfo();
877          updatePrivileges(null, false);
878        }
879        else
880        {
881          this.authenticationInfo = authenticationInfo;
882    
883          Entry authNEntry = authenticationInfo.getAuthenticationEntry();
884          Entry authZEntry = authenticationInfo.getAuthorizationEntry();
885    
886          if (authNEntry != null)
887          {
888            if ((authZEntry == null) ||
889                authZEntry.getDN().equals(authNEntry.getDN()))
890            {
891              DirectoryServer.getAuthenticatedUsers().put(
892                   authNEntry.getDN(), this);
893            }
894            else
895            {
896              DirectoryServer.getAuthenticatedUsers().put(
897                   authNEntry.getDN(), this);
898              DirectoryServer.getAuthenticatedUsers().put(
899                   authZEntry.getDN(), this);
900            }
901          }
902          else
903          {
904            if (authZEntry != null)
905            {
906              DirectoryServer.getAuthenticatedUsers().put(
907                   authZEntry.getDN(), this);
908            }
909          }
910    
911          updatePrivileges(authZEntry, authenticationInfo.isRoot());
912        }
913      }
914    
915    
916    
917      /**
918       * Updates the cached entry associated with either the
919       * authentication and/or authorization identity with the provided
920       * version.
921       *
922       * @param  oldEntry  The user entry currently serving as the
923       *                   authentication and/or authorization identity.
924       * @param  newEntry  The updated entry that should replace the
925       *                   existing entry.  It may optionally have a
926       *                   different DN than the old entry.
927       */
928      public final void updateAuthenticationInfo(Entry oldEntry,
929                                                 Entry newEntry)
930      {
931        Entry authNEntry = authenticationInfo.getAuthenticationEntry();
932        Entry authZEntry = authenticationInfo.getAuthorizationEntry();
933    
934        if ((authNEntry != null) &&
935            authNEntry.getDN().equals(oldEntry.getDN()))
936        {
937          if ((authZEntry == null) ||
938              (! authZEntry.getDN().equals(authNEntry.getDN())))
939          {
940            setAuthenticationInfo(
941                 authenticationInfo.duplicate(newEntry, authZEntry));
942            updatePrivileges(newEntry, authenticationInfo.isRoot());
943          }
944          else
945          {
946            setAuthenticationInfo(
947                 authenticationInfo.duplicate(newEntry, newEntry));
948            updatePrivileges(newEntry, authenticationInfo.isRoot());
949          }
950        }
951        else if ((authZEntry != null) &&
952                 (authZEntry.getDN().equals(oldEntry.getDN())))
953        {
954          setAuthenticationInfo(
955               authenticationInfo.duplicate(authNEntry, newEntry));
956        }
957      }
958    
959    
960    
961      /**
962       * Sets properties in this client connection to indicate that the
963       * client is unauthenticated.  This includes setting the
964       * authentication info structure to an empty default, as well as
965       * setting the size and time limit values to their defaults.
966       */
967      public void setUnauthenticated()
968      {
969        setAuthenticationInfo(new AuthenticationInfo());
970        this.sizeLimit          = DirectoryServer.getSizeLimit();
971        this.timeLimit          = DirectoryServer.getTimeLimit();
972      }
973    
974    
975    
976      /**
977       * Indicates whether the authenticated client has the specified
978       * privilege.
979       *
980       * @param  privilege  The privilege for which to make the
981       *                    determination.
982       * @param  operation  The operation being processed which needs to
983       *                    make the privilege determination, or
984       *                    {@code null} if there is no associated
985       *                    operation.
986       *
987       * @return  {@code true} if the authenticated client has the
988       *          specified privilege, or {@code false} if not.
989       */
990      public boolean hasPrivilege(Privilege privilege,
991                                  Operation operation)
992      {
993        if (privilege == Privilege.PROXIED_AUTH)
994        {
995          // This determination should always be made against the
996          // authentication identity rather than the authorization
997          // identity.
998          Entry authEntry = authenticationInfo.getAuthenticationEntry();
999          boolean isRoot  = authenticationInfo.isRoot();
1000          return getPrivileges(authEntry,
1001                               isRoot).contains(Privilege.PROXIED_AUTH) ||
1002                 DirectoryServer.isDisabled(Privilege.PROXIED_AUTH);
1003        }
1004    
1005        boolean result;
1006        if (operation == null)
1007        {
1008          result = privileges.contains(privilege);
1009          if (debugEnabled())
1010          {
1011            DN authDN = authenticationInfo.getAuthenticationDN();
1012    
1013            Message message = INFO_CLIENTCONNECTION_AUDIT_HASPRIVILEGE
1014                    .get(getConnectionID(), -1L,
1015                         String.valueOf(authDN),
1016                         privilege.getName(), result);
1017            TRACER.debugMessage(DebugLogLevel.INFO, message.toString());
1018          }
1019        }
1020        else
1021        {
1022          if (operation.getAuthorizationDN().equals(
1023                   authenticationInfo.getAuthorizationDN()) ||
1024              (operation.getAuthorizationDN().equals(DN.NULL_DN) &&
1025               !authenticationInfo.isAuthenticated())) {
1026            result = privileges.contains(privilege) ||
1027                     DirectoryServer.isDisabled(privilege);
1028            if (debugEnabled())
1029            {
1030              DN authDN = authenticationInfo.getAuthenticationDN();
1031    
1032              Message message =
1033                      INFO_CLIENTCONNECTION_AUDIT_HASPRIVILEGE.get(
1034                        getConnectionID(),
1035                        operation.getOperationID(),
1036                        String.valueOf(authDN),
1037                        privilege.getName(), result);
1038              TRACER.debugMessage(DebugLogLevel.INFO, message.toString());
1039            }
1040          }
1041          else
1042          {
1043            Entry authorizationEntry = operation.getAuthorizationEntry();
1044            if (authorizationEntry == null)
1045            {
1046              result = false;
1047            }
1048            else
1049            {
1050              boolean isRoot =
1051                   DirectoryServer.isRootDN(authorizationEntry.getDN());
1052              result = getPrivileges(authorizationEntry,
1053                                     isRoot).contains(privilege) ||
1054                       DirectoryServer.isDisabled(privilege);
1055            }
1056          }
1057        }
1058    
1059        return result;
1060      }
1061    
1062    
1063    
1064      /**
1065       * Indicates whether the authenticate client has all of the
1066       * specified privileges.
1067       *
1068       * @param  privileges  The array of privileges for which to make the
1069       *                     determination.
1070       * @param  operation   The operation being processed which needs to
1071       *                     make the privilege determination, or
1072       *                     {@code null} if there is no associated
1073       *                     operation.
1074       *
1075       * @return  {@code true} if the authenticated client has all of the
1076       *          specified privileges, or {@code false} if not.
1077       */
1078      public boolean hasAllPrivileges(Privilege[] privileges,
1079                                      Operation operation)
1080      {
1081        HashSet<Privilege> privSet = this.privileges;
1082    
1083        if (debugEnabled())
1084        {
1085          for (Privilege p : privileges)
1086          {
1087            if (! privSet.contains(p))
1088            {
1089              return false;
1090            }
1091          }
1092    
1093          return true;
1094        }
1095        else
1096        {
1097          boolean result = true;
1098          StringBuilder buffer = new StringBuilder();
1099          buffer.append("{");
1100    
1101          for (int i=0; i < privileges.length; i++)
1102          {
1103            if (i > 0)
1104            {
1105              buffer.append(",");
1106            }
1107    
1108            buffer.append(privileges[i].getName());
1109    
1110            if (! privSet.contains(privileges[i]))
1111            {
1112              result = false;
1113            }
1114          }
1115    
1116          buffer.append(" }");
1117    
1118          if (operation == null)
1119          {
1120            DN authDN = authenticationInfo.getAuthenticationDN();
1121    
1122            Message message =
1123                    INFO_CLIENTCONNECTION_AUDIT_HASPRIVILEGES.get(
1124                      getConnectionID(), -1L,
1125                      String.valueOf(authDN),
1126                      buffer.toString(), result);
1127            TRACER.debugMessage(DebugLogLevel.INFO,
1128                    message.toString());
1129          }
1130          else
1131          {
1132            DN authDN = authenticationInfo.getAuthenticationDN();
1133    
1134            Message message = INFO_CLIENTCONNECTION_AUDIT_HASPRIVILEGES
1135                    .get(
1136                      getConnectionID(),
1137                      operation.getOperationID(),
1138                      String.valueOf(authDN),
1139                      buffer.toString(), result);
1140            TRACER.debugMessage(DebugLogLevel.INFO, message.toString());
1141          }
1142    
1143          return result;
1144        }
1145      }
1146    
1147    
1148    
1149      /**
1150       * Retrieves the set of privileges encoded in the provided entry.
1151       *
1152       * @param  entry   The entry to use to obtain the privilege
1153       *                 information.
1154       * @param  isRoot  Indicates whether the set of root privileges
1155       *                 should be automatically included in the
1156       *                 privilege set.
1157       *
1158       * @return  A set of the privileges that should be assigned.
1159       */
1160      private HashSet<Privilege> getPrivileges(Entry entry,
1161                                               boolean isRoot)
1162      {
1163        if (entry == null)
1164        {
1165          return new HashSet<Privilege>(0);
1166        }
1167    
1168        HashSet<Privilege> newPrivileges = new HashSet<Privilege>();
1169        HashSet<Privilege> removePrivileges = new HashSet<Privilege>();
1170    
1171        if (isRoot)
1172        {
1173          newPrivileges.addAll(DirectoryServer.getRootPrivileges());
1174        }
1175    
1176        AttributeType privType =
1177             DirectoryServer.getAttributeType(OP_ATTR_PRIVILEGE_NAME);
1178        List<Attribute> attrList = entry.getAttribute(privType);
1179        if (attrList != null)
1180        {
1181          for (Attribute a : attrList)
1182          {
1183            for (AttributeValue v : a.getValues())
1184            {
1185              String privName = toLowerCase(v.getStringValue());
1186    
1187              // If the name of the privilege is prefixed with a minus
1188              // sign, then we will take away that privilege from the
1189              // user.  We'll handle that at the end so that we can make
1190              // sure it's not added back later.
1191              if (privName.startsWith("-"))
1192              {
1193                privName = privName.substring(1);
1194                Privilege p = Privilege.privilegeForName(privName);
1195                if (p == null)
1196                {
1197                  // FIXME -- Generate an administrative alert.
1198    
1199                  // We don't know what privilege to remove, so we'll
1200                  // remove all of them.
1201                  newPrivileges.clear();
1202                  return newPrivileges;
1203                }
1204                else
1205                {
1206                  removePrivileges.add(p);
1207                }
1208              }
1209              else
1210              {
1211                Privilege p = Privilege.privilegeForName(privName);
1212                if (p == null)
1213                {
1214                  // FIXME -- Generate an administrative alert.
1215                }
1216                else
1217                {
1218                  newPrivileges.add(p);
1219                }
1220              }
1221            }
1222          }
1223        }
1224    
1225        for (Privilege p : removePrivileges)
1226        {
1227          newPrivileges.remove(p);
1228        }
1229    
1230        return newPrivileges;
1231      }
1232    
1233    
1234    
1235      /**
1236       * Updates the privileges associated with this client connection
1237       * object based on the provided entry for the authentication
1238       * identity.
1239       *
1240       * @param  entry   The entry for the authentication identity
1241       *                 associated with this client connection.
1242       * @param  isRoot  Indicates whether the associated user is a root
1243       *                 user and should automatically inherit the root
1244       *                 privilege set.
1245       */
1246      private void updatePrivileges(Entry entry, boolean isRoot)
1247      {
1248        privileges = getPrivileges(entry, isRoot);
1249      }
1250    
1251    
1252    
1253      /**
1254       * Retrieves an opaque set of information that may be used for
1255       * processing multi-stage SASL binds.
1256       *
1257       * @return  An opaque set of information that may be used for
1258       *          processing multi-stage SASL binds.
1259       */
1260      public final Object getSASLAuthStateInfo()
1261      {
1262        return saslAuthState;
1263      }
1264    
1265    
1266    
1267      /**
1268       * Specifies an opaque set of information that may be used for
1269       * processing multi-stage SASL binds.
1270       *
1271       * @param  saslAuthState  An opaque set of information that may be
1272       *                        used for processing multi-stage SASL
1273       *                        binds.
1274       */
1275      public final void setSASLAuthStateInfo(Object saslAuthState)
1276      {
1277        this.saslAuthState = saslAuthState;
1278      }
1279    
1280    
1281    
1282      /**
1283       * Retrieves the size limit that will be enforced for searches
1284       * performed using this client connection.
1285       *
1286       * @return  The size limit that will be enforced for searches
1287       *          performed using this client connection.
1288       */
1289      public final int getSizeLimit()
1290      {
1291        return sizeLimit;
1292      }
1293    
1294    
1295    
1296      /**
1297       * Specifies the size limit that will be enforced for searches
1298       * performed using this client connection.
1299       *
1300       * @param  sizeLimit  The size limit that will be enforced for
1301       *                    searches performed using this client
1302       *                    connection.
1303       */
1304      public void setSizeLimit(int sizeLimit)
1305      {
1306        this.sizeLimit = sizeLimit;
1307      }
1308    
1309    
1310    
1311      /**
1312       * Retrieves the maximum length of time in milliseconds that this
1313       * client connection will be allowed to remain idle before it should
1314       * be disconnected.
1315       *
1316       * @return  The maximum length of time in milliseconds that this
1317       *          client connection will be allowed to remain idle before
1318       *          it should be disconnected.
1319       */
1320      public final long getIdleTimeLimit()
1321      {
1322        return idleTimeLimit;
1323      }
1324    
1325    
1326    
1327      /**
1328       * Specifies the maximum length of time in milliseconds that this
1329       * client connection will be allowed to remain idle before it should
1330       * be disconnected.
1331       *
1332       * @param  idleTimeLimit  The maximum length of time in milliseconds
1333       *                        that this client connection will be
1334       *                        allowed to remain idle before it should be
1335       *                        disconnected.
1336       */
1337      public void setIdleTimeLimit(long idleTimeLimit)
1338      {
1339        this.idleTimeLimit = idleTimeLimit;
1340      }
1341    
1342    
1343    
1344      /**
1345       * Retrieves the default maximum number of entries that should
1346       * checked for matches during a search.
1347       *
1348       * @return  The default maximum number of entries that should
1349       *          checked for matches during a search.
1350       */
1351      public final int getLookthroughLimit()
1352      {
1353        return lookthroughLimit;
1354      }
1355    
1356    
1357    
1358      /**
1359       * Specifies the default maximum number of entries that should
1360       * be checked for matches during a search.
1361       *
1362       * @param  lookthroughLimit  The default maximum number of
1363       *                           entries that should be check for
1364       *                           matches during a search.
1365       */
1366      public void setLookthroughLimit(int lookthroughLimit)
1367      {
1368        this.lookthroughLimit = lookthroughLimit;
1369      }
1370    
1371    
1372    
1373      /**
1374       * Retrieves the time limit that will be enforced for searches
1375       * performed using this client connection.
1376       *
1377       * @return  The time limit that will be enforced for searches
1378       *          performed using this client connection.
1379       */
1380      public final int getTimeLimit()
1381      {
1382        return timeLimit;
1383      }
1384    
1385    
1386    
1387      /**
1388       * Specifies the time limit that will be enforced for searches
1389       * performed using this client connection.
1390       *
1391       * @param  timeLimit  The time limit that will be enforced for
1392       *                    searches performed using this client
1393       *                    connection.
1394       */
1395      public void setTimeLimit(int timeLimit)
1396      {
1397        this.timeLimit = timeLimit;
1398      }
1399    
1400    
1401    
1402      /**
1403       * Retrieves a one-line summary of this client connection in a form
1404       * that is suitable for including in the monitor entry for the
1405       * associated connection handler.  It should be in a format that is
1406       * both humand readable and machine parseable (e.g., a
1407       * space-delimited name-value list, with quotes around the values).
1408       *
1409       * @return  A one-line summary of this client connection in a form
1410       *          that is suitable for including in the monitor entry for
1411       *          the associated connection handler.
1412       */
1413      public abstract String getMonitorSummary();
1414    
1415    
1416    
1417      /**
1418       * Indicates whether the user associated with this client connection
1419       * should be considered a member of the specified group, optionally
1420       * evaluated within the context of the provided operation.  If an
1421       * operation is given, then the determination should be made based
1422       * on the authorization identity for that operation.  If the
1423       * operation is {@code null}, then the determination should be made
1424       * based on the authorization identity for this client connection.
1425       * Note that this is a point-in-time determination and the caller
1426       * must not cache the result.
1427       *
1428       * @param  group      The group for which to make the determination.
1429       * @param  operation  The operation to use to obtain the
1430       *                    authorization identity for which to make the
1431       *                    determination, or {@code null} if the
1432       *                    authorization identity should be obtained from
1433       *                    this client connection.
1434       *
1435       * @return  {@code true} if the target user is currently a member of
1436       *          the specified group, or {@code false} if not.
1437       *
1438       * @throws  DirectoryException  If a problem occurs while attempting
1439       *                             to make the determination.
1440       */
1441      public boolean isMemberOf(Group group, Operation operation)
1442             throws DirectoryException
1443      {
1444        if (operation == null)
1445        {
1446          return group.isMember(authenticationInfo.getAuthorizationDN());
1447        }
1448        else
1449        {
1450          return group.isMember(operation.getAuthorizationDN());
1451        }
1452      }
1453    
1454    
1455    
1456      /**
1457       * Retrieves the set of groups in which the user associated with
1458       * this client connection may be considered to be a member.  If an
1459       * operation is provided, then the determination should be made
1460       * based on the authorization identity for that operation.  If the
1461       * operation is {@code null}, then it should be made based on the
1462       * authorization identity for this client connection.  Note that
1463       * this is a point-in-time determination and the caller must not
1464       * cache the result.
1465       *
1466       * @param  operation  The operation to use to obtain the
1467       *                    authorization identity for which to retrieve
1468       *                    the associated groups, or {@code null} if the
1469       *                    authorization identity should be obtained from
1470       *                    this client connection.
1471       *
1472       * @return  The set of groups in which the target user is currently
1473       *          a member.
1474       *
1475       * @throws  DirectoryException  If a problem occurs while attempting
1476       *                              to make the determination.
1477       */
1478      public Set<Group> getGroups(Operation operation)
1479             throws DirectoryException
1480      {
1481        // FIXME -- This probably isn't the most efficient implementation.
1482        DN authzDN;
1483        if (operation == null)
1484        {
1485          if ((authenticationInfo == null) ||
1486              (! authenticationInfo.isAuthenticated()))
1487          {
1488            authzDN = null;
1489          }
1490          else
1491          {
1492            authzDN = authenticationInfo.getAuthorizationDN();
1493          }
1494        }
1495        else
1496        {
1497          authzDN = operation.getAuthorizationDN();
1498        }
1499    
1500        if ((authzDN == null) || authzDN.isNullDN())
1501        {
1502          return java.util.Collections.<Group>emptySet();
1503        }
1504    
1505        Entry userEntry = DirectoryServer.getEntry(authzDN);
1506        if (userEntry == null)
1507        {
1508          return java.util.Collections.<Group>emptySet();
1509        }
1510    
1511        HashSet<Group> groupSet = new HashSet<Group>();
1512        for (Group g :
1513             DirectoryServer.getGroupManager().getGroupInstances())
1514        {
1515          if (g.isMember(userEntry))
1516          {
1517            groupSet.add(g);
1518          }
1519        }
1520    
1521        return groupSet;
1522      }
1523    
1524    
1525    
1526      /**
1527       * Retrieves the DN of the key manager provider that should be used
1528       * for operations requiring access to a key manager.  The default
1529       * implementation returns {@code null} to indicate that no key
1530       * manager provider is avaialble, but subclasses should override
1531       * this method to return a valid DN if they perform operations which
1532       * may need access to a key manager.
1533       *
1534       * @return  The DN of the key manager provider that should be used
1535       *          for operations requiring access to a key manager, or
1536       *          {@code null} if there is no key manager provider
1537       *          configured for this client connection.
1538       */
1539      public DN getKeyManagerProviderDN()
1540      {
1541        // In the default implementation, we'll return null.
1542        return null;
1543      }
1544    
1545    
1546    
1547      /**
1548       * Retrieves the DN of the trust manager provider that should be
1549       * used for operations requiring access to a trust manager.  The
1550       * default implementation returns {@code null} to indicate that no
1551       * trust manager provider is avaialble, but subclasses should
1552       * override this method to return a valid DN if they perform
1553       * operations which may need access to a trust manager.
1554       *
1555       * @return  The DN of the trust manager provider that should be used
1556       *          for operations requiring access to a trust manager, or
1557       *          {@code null} if there is no trust manager provider
1558       *          configured for this client connection.
1559       */
1560      public DN getTrustManagerProviderDN()
1561      {
1562        // In the default implementation, we'll return null.
1563        return null;
1564      }
1565    
1566    
1567    
1568      /**
1569       * Retrieves the alias of the server certificate that should be used
1570       * for operations requiring a server certificate.  The default
1571       * implementation returns {@code null} to indicate that any alias is
1572       * acceptable.
1573       *
1574       * @return  The alias of the server certificate that should be used
1575       *          for operations requring a server certificate, or
1576       *          {@code null} if any alias is acceptable.
1577       */
1578      public String getCertificateAlias()
1579      {
1580        // In the default implementation, we'll return null.
1581        return null;
1582      }
1583    
1584    
1585    
1586      /**
1587       * Retrieves a string representation of this client connection.
1588       *
1589       * @return  A string representation of this client connection.
1590       */
1591      public final String toString()
1592      {
1593        StringBuilder buffer = new StringBuilder();
1594        toString(buffer);
1595        return buffer.toString();
1596      }
1597    
1598    
1599    
1600      /**
1601       * Appends a string representation of this client connection to the
1602       * provided buffer.
1603       *
1604       * @param  buffer  The buffer to which the information should be
1605       *                 appended.
1606       */
1607      public abstract void toString(StringBuilder buffer);
1608    
1609    
1610    
1611      /**
1612       * Performs any work that may be needed before the JVM invokes
1613       * garbage collection for this object.  In this case, it makes sure
1614       * to deregister with the Directory Server as a change notification
1615       * listener.  If a subclass wishes to perform custom finalization
1616       * processing, then it should override this method and make sure to
1617       * invoke {@code super.finalize} as its first call.
1618       */
1619      protected void finalize()
1620      {
1621        finalizeConnectionInternal();
1622      }
1623    
1624    
1625      /**
1626       * Returns the network group to which the connection belongs.
1627       *
1628       * @return the network group attached to the connection
1629       */
1630      public final NetworkGroup getNetworkGroup()
1631      {
1632        return networkGroup;
1633      }
1634    
1635      /**
1636       * Sets the network group to which the connection belongs.
1637       *
1638       * @param networkGroup  the network group to which the
1639       *                      connections belongs to
1640       */
1641      public final void setNetworkGroup (NetworkGroup networkGroup)
1642      {
1643        this.networkGroup = networkGroup;
1644      }
1645    
1646    
1647    
1648      /**
1649       * Retrieves the length of time in milliseconds that this client
1650       * connection has been idle.
1651       * <BR><BR>
1652       * Note that the default implementation will always return zero.
1653       * Subclasses associated with connection handlers should override
1654       * this method if they wish to provided idle time limit
1655       * functionality.
1656       *
1657       * @return  The length of time in milliseconds that this client
1658       *          connection has been idle.
1659       */
1660      public long getIdleTime()
1661      {
1662        return 0L;
1663      }
1664    }
1665