All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.unboundid.ldap.sdk.examples.AuthRateThread Maven / Gradle / Ivy

Go to download

The UnboundID LDAP SDK for Java is a fast, comprehensive, and easy-to-use Java API for communicating with LDAP directory servers and performing related tasks like reading and writing LDIF, encoding and decoding data using base64 and ASN.1 BER, and performing secure communication. This package contains the Standard Edition of the LDAP SDK, which is a complete, general-purpose library for communicating with LDAPv3 directory servers.

There is a newer version: 7.0.1
Show newest version
/*
 * Copyright 2009-2023 Ping Identity Corporation
 * All Rights Reserved.
 */
/*
 * Copyright 2009-2023 Ping Identity Corporation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*
 * Copyright (C) 2009-2023 Ping Identity Corporation
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License (GPLv2 only)
 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see .
 */
package com.unboundid.ldap.sdk.examples;



import java.util.List;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;

import com.unboundid.ldap.sdk.BindRequest;
import com.unboundid.ldap.sdk.Control;
import com.unboundid.ldap.sdk.CRAMMD5BindRequest;
import com.unboundid.ldap.sdk.DIGESTMD5BindRequest;
import com.unboundid.ldap.sdk.Filter;
import com.unboundid.ldap.sdk.LDAPConnection;
import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldap.sdk.PLAINBindRequest;
import com.unboundid.ldap.sdk.ResultCode;
import com.unboundid.ldap.sdk.SearchRequest;
import com.unboundid.ldap.sdk.SearchResult;
import com.unboundid.ldap.sdk.SearchScope;
import com.unboundid.ldap.sdk.SimpleBindRequest;
import com.unboundid.util.Debug;
import com.unboundid.util.FixedRateBarrier;
import com.unboundid.util.NotNull;
import com.unboundid.util.Nullable;
import com.unboundid.util.ResultCodeCounter;
import com.unboundid.util.StaticUtils;
import com.unboundid.util.ValuePattern;



/**
 * This class provides a thread that may be used to repeatedly perform
 * authentication processing.
 */
final class AuthRateThread
      extends Thread
{
  /**
   * The authentication type value that will be used to indicate that simple
   * authentication should be performed.
   */
  private static final int AUTH_TYPE_SIMPLE = 0;



  /**
   * The authentication type value that will be used to indicate that CRAM-MD5
   * authentication should be performed.
   */
  private static final int AUTH_TYPE_CRAM_MD5 = 1;



  /**
   * The authentication type value that will be used to indicate that DIGEST-MD5
   * authentication should be performed.
   */
  private static final int AUTH_TYPE_DIGEST_MD5 = 2;



  /**
   * The authentication type value that will be used to indicate that PLAIN
   * authentication should be performed.
   */
  private static final int AUTH_TYPE_PLAIN = 3;



  // Indicates whether a request has been made to stop running.
  @NotNull private final AtomicBoolean stopRequested;

  // The number of authrate threads that are currently running.
  @NotNull private final AtomicInteger runningThreads;

  // The counter used to track the number of searches performed.
  @NotNull private final AtomicLong authCounter;

  // The value that will be updated with total duration of the searches.
  @NotNull private final AtomicLong authDurations;

  // The counter used to track the number of errors encountered while searching.
  @NotNull private final AtomicLong errorCounter;

  // The result code for this thread.
  @NotNull private final AtomicReference resultCode;

  // The thread that is actually performing the searches.
  @NotNull private final AtomicReference authThread;

  // A reference to the associated authrate tool.
  @NotNull private final AuthRate authRate;

  // Indicates whether the authentication attempts should only include bind
  // operations without the initial search.
  private final boolean bindOnly;

  // The set of controls to include in bind requests.
  @NotNull private final Control[] bindControls;

  // The barrier that will be used to coordinate starting among all the threads.
  @NotNull private final CyclicBarrier startBarrier;

  // The barrier to use for controlling the rate of auths.  null if no
  // rate-limiting should be used.
  @Nullable private final FixedRateBarrier fixedRateBarrier;

  // The type of authentication to perform.
  private final int authType;

  // The connection to use for the binds.
  @Nullable private LDAPConnection bindConnection;

  // The connection to use for the searches.
  @Nullable private LDAPConnection searchConnection;

  // The result code counter to use for failed operations.
  @NotNull private final ResultCodeCounter rcCounter;

  // The search request to generate.
  @Nullable private final SearchRequest searchRequest;

  // The password to use to authenticate.
  @NotNull private final String userPassword;

  // The value pattern to use for the base DNs.
  @NotNull private final ValuePattern baseDN;

  //The value pattern to use for the filters.
  @Nullable private final ValuePattern filter;



  /**
   * Creates a new auth rate thread with the provided information.
   *
   * @param  authRate          A reference to the associated authrate tool.
   * @param  threadNumber      The thread number for this thread.
   * @param  searchConnection  The connection to use for the searches.
   * @param  bindConnection    The connection to use for the  binds.
   * @param  baseDN            The value pattern to use for the base DNs.
   * @param  scope             The scope to use for the searches.
   * @param  filter            The value pattern for the filters.
   * @param  attributes        The set of attributes to return.
   * @param  userPassword      The password to use for the bind operations.
   * @param  bindOnly          Indicates whether to only perform a bind without
   *                           first performing the initial search to find the
   *                           target user entry.
   * @param  authType          The type of authentication to perform.
   * @param  searchControls    The set of controls to include in search
   *                           requests.
   * @param  bindControls      The set of controls to include in bind requests.
   * @param  runningThreads    An atomic integer that will be incremented when
   *                           this thread starts, and decremented when it
   *                           completes.
   * @param  startBarrier      A barrier used to coordinate starting between all
   *                           of the threads.
   * @param  authCounter       A value that will be used to keep track of the
   *                           total number of authentications performed.
   * @param  authDurations     A value that will be used to keep track of the
   *                           total duration for all authentications.
   * @param  errorCounter      A value that will be used to keep track of the
   *                           number of errors encountered while searching.
   * @param  rcCounter         The result code counter to use for keeping track
   *                           of the result codes for failed operations.
   * @param  rateBarrier       The barrier to use for controlling the rate of
   *                           authorizations.  {@code null} if no rate-limiting
   *                           should be used.
   */
  AuthRateThread(@NotNull final AuthRate authRate, final int threadNumber,
                 @NotNull final LDAPConnection searchConnection,
                 @NotNull final LDAPConnection bindConnection,
                 @NotNull final ValuePattern baseDN,
                 @NotNull final SearchScope scope,
                 @Nullable final ValuePattern filter,
                 @NotNull final String[] attributes,
                 @NotNull final String userPassword,
                 final boolean bindOnly,
                 @NotNull final String authType,
                 @NotNull final List searchControls,
                 @NotNull final List bindControls,
                 @NotNull final AtomicInteger runningThreads,
                 @NotNull final CyclicBarrier startBarrier,
                 @NotNull final AtomicLong authCounter,
                 @NotNull final AtomicLong authDurations,
                 @NotNull final AtomicLong errorCounter,
                 @NotNull final ResultCodeCounter rcCounter,
                 @Nullable final FixedRateBarrier rateBarrier)
  {
    setName("AuthRate Thread " + threadNumber);
    setDaemon(true);

    this.authRate         = authRate;
    this.searchConnection = searchConnection;
    this.bindConnection   = bindConnection;
    this.baseDN           = baseDN;
    this.filter           = filter;
    this.userPassword     = userPassword;
    this.bindOnly         = bindOnly;
    this.authCounter      = authCounter;
    this.authDurations    = authDurations;
    this.errorCounter     = errorCounter;
    this.rcCounter        = rcCounter;
    this.runningThreads   = runningThreads;
    this.startBarrier     = startBarrier;
    fixedRateBarrier      = rateBarrier;

    searchConnection.setConnectionName("search-" + threadNumber);
    bindConnection.setConnectionName("bind-" + threadNumber);

    if (authType.equalsIgnoreCase("cram-md5"))
    {
      this.authType = AUTH_TYPE_CRAM_MD5;
    }
    else if (authType.equalsIgnoreCase("digest-md5"))
    {
      this.authType = AUTH_TYPE_DIGEST_MD5;
    }
    else if (authType.equalsIgnoreCase("plain"))
    {
      this.authType = AUTH_TYPE_PLAIN;
    }
    else
    {
      this.authType = AUTH_TYPE_SIMPLE;
    }

    resultCode    = new AtomicReference<>(null);
    authThread    = new AtomicReference<>(null);
    stopRequested = new AtomicBoolean(false);

    if (bindOnly)
    {
      searchRequest = null;
    }
    else
    {
      searchRequest = new SearchRequest("", scope,
           Filter.createPresenceFilter("objectClass"), attributes);
      searchRequest.setControls(searchControls);
    }

    if (bindControls.isEmpty())
    {
      this.bindControls = StaticUtils.NO_CONTROLS;
    }
    else
    {
      this.bindControls =
           bindControls.toArray(new Control[bindControls.size()]);
    }
  }



  /**
   * Performs all search processing for this thread.
   */
  @Override()
  public void run()
  {
    try
    {
      authThread.set(currentThread());
      runningThreads.incrementAndGet();

      try
      {
        startBarrier.await();
      }
      catch (final Exception e)
      {
        Debug.debugException(e);
      }

      while (! stopRequested.get())
      {
        if (searchConnection == null)
        {
          try
          {
            searchConnection = authRate.getConnection();
          }
          catch (final LDAPException le)
          {
            Debug.debugException(le);

            errorCounter.incrementAndGet();

            final ResultCode rc = le.getResultCode();
            rcCounter.increment(rc);
            resultCode.compareAndSet(null, rc);

            if (fixedRateBarrier != null)
            {
              fixedRateBarrier.await();
            }

            continue;
          }
        }

        if (bindConnection == null)
        {
          try
          {
            bindConnection = authRate.getConnection();
          }
          catch (final LDAPException le)
          {
            Debug.debugException(le);

            errorCounter.incrementAndGet();

            final ResultCode rc = le.getResultCode();
            rcCounter.increment(rc);
            resultCode.compareAndSet(null, rc);

            if (fixedRateBarrier != null)
            {
              fixedRateBarrier.await();
            }

            continue;
          }
        }

        if (! bindOnly)
        {
          try
          {
            searchRequest.setBaseDN(baseDN.nextValue());
            searchRequest.setFilter(filter.nextValue());
          }
          catch (final LDAPException le)
          {
            Debug.debugException(le);
            errorCounter.incrementAndGet();

            final ResultCode rc = le.getResultCode();
            rcCounter.increment(rc);
            resultCode.compareAndSet(null, rc);
            continue;
          }
        }

        // If we're trying for a specific target rate, then we might need to
        // wait until starting the next authorization.
        if (fixedRateBarrier != null)
        {
          fixedRateBarrier.await();
        }

        final long startTime = System.nanoTime();

        try
        {
          final String bindDN;
          if (bindOnly)
          {
            bindDN = baseDN.nextValue();
          }
          else
          {
            final SearchResult r = searchConnection.search(searchRequest);
            switch (r.getEntryCount())
            {
              case 0:
                errorCounter.incrementAndGet();
                rcCounter.increment(ResultCode.NO_RESULTS_RETURNED);
                resultCode.compareAndSet(null, ResultCode.NO_RESULTS_RETURNED);
                continue;

              case 1:
                // This is acceptable, and we can continue processing.
                bindDN = r.getSearchEntries().get(0).getDN();
                break;

              default:
                errorCounter.incrementAndGet();
                rcCounter.increment(ResultCode.MORE_RESULTS_TO_RETURN);
                resultCode.compareAndSet(null,
                     ResultCode.MORE_RESULTS_TO_RETURN);
                continue;
            }
          }

          BindRequest bindRequest = null;
          switch (authType)
          {
            case AUTH_TYPE_SIMPLE:
              bindRequest =
                   new SimpleBindRequest(bindDN, userPassword, bindControls);
              break;

            case AUTH_TYPE_CRAM_MD5:
              bindRequest = new CRAMMD5BindRequest("dn:" + bindDN, userPassword,
                   bindControls);
              break;

            case AUTH_TYPE_DIGEST_MD5:
              bindRequest = new DIGESTMD5BindRequest("dn:" + bindDN, null,
                   userPassword, null, bindControls);
              break;

            case AUTH_TYPE_PLAIN:
              bindRequest = new PLAINBindRequest("dn:" + bindDN, userPassword,
                   bindControls);
              break;
          }

          bindConnection.bind(bindRequest);
        }
        catch (final LDAPException le)
        {
          Debug.debugException(le);
          errorCounter.incrementAndGet();

          final ResultCode rc = le.getResultCode();
          rcCounter.increment(rc);
          resultCode.compareAndSet(null, rc);

          if (! le.getResultCode().isConnectionUsable())
          {
            searchConnection.close();
            searchConnection = null;

            bindConnection.close();
            bindConnection = null;
          }
        }
        finally
        {
          authCounter.incrementAndGet();
          authDurations.addAndGet(System.nanoTime() - startTime);
        }
      }
    }
    finally
    {
      if (searchConnection != null)
      {
        searchConnection.close();
      }

      if (bindConnection != null)
      {
        bindConnection.close();
      }

      authThread.set(null);
      runningThreads.decrementAndGet();
    }
  }



  /**
   * Indicates that this thread should stop running.
   *
   * @return  A result code that provides information about whether any errors
   *          were encountered during processing.
   */
  @NotNull()
  public ResultCode stopRunning()
  {
    stopRequested.set(true);

    if (fixedRateBarrier != null)
    {
      fixedRateBarrier.shutdownRequested();
    }

    final Thread t = authThread.get();
    if (t != null)
    {
      try
      {
        t.join();
      }
      catch (final Exception e)
      {
        Debug.debugException(e);

        if (e instanceof InterruptedException)
        {
          Thread.currentThread().interrupt();
        }
      }
    }

    resultCode.compareAndSet(null, ResultCode.SUCCESS);
    return resultCode.get();
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy