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 legal-notices/CDDLv1_0.txt
010 * or http://forgerock.org/license/CDDLv1.0.html.
011 * See the License for the specific language governing permissions
012 * and limitations under the License.
013 *
014 * When distributing Covered Code, include this CDDL HEADER in each
015 * file and include the License file at legal-notices/CDDLv1_0.txt.
016 * If applicable, add the following below this CDDL HEADER, with the
017 * fields enclosed by brackets "[]" replaced with your own identifying
018 * information:
019 *      Portions Copyright [yyyy] [name of copyright owner]
020 *
021 * CDDL HEADER END
022 *
023 *
024 *      Copyright 2008-2009 Sun Microsystems, Inc.
025 *      Portions Copyright 2014-2015 ForgeRock AS
026 */
027package org.opends.server.extensions;
028
029import org.forgerock.i18n.LocalizableMessage;
030import org.forgerock.i18n.slf4j.LocalizedLogger;
031import org.forgerock.opendj.ldap.ResultCode;
032import org.forgerock.opendj.ldap.SearchScope;
033import org.opends.server.api.DirectoryThread;
034import org.opends.server.core.DirectoryServer;
035import org.opends.server.protocols.internal.InternalClientConnection;
036import org.opends.server.protocols.internal.InternalSearchListener;
037import org.opends.server.protocols.internal.InternalSearchOperation;
038import org.opends.server.protocols.internal.SearchRequest;
039import static org.opends.server.protocols.internal.Requests.*;
040import org.opends.server.types.DN;
041import org.opends.server.types.DirectoryException;
042import org.opends.server.types.LDAPURL;
043import org.opends.server.types.MembershipException;
044import org.opends.server.types.SearchFilter;
045import org.opends.server.types.SearchResultEntry;
046import org.opends.server.types.SearchResultReference;
047
048import static org.opends.messages.ExtensionMessages.*;
049import static org.opends.server.protocols.internal.InternalClientConnection.*;
050
051/**
052 * This class implements a Directory Server thread that will be used to perform
053 * a background search to retrieve all of the members of a dynamic group.
054 * <BR><BR>
055 */
056public class DynamicGroupSearchThread
057// FIXME -- Would it be better to implement this class using an Executor
058//          rather than always creating a custom thread?
059       extends DirectoryThread
060       implements InternalSearchListener
061{
062
063  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
064
065  /** The set of base DNs for the search requests. */
066  private final DN[] baseDNs;
067
068  /** The member list with which this search thread is associated. */
069  private final DynamicGroupMemberList memberList;
070
071  /** A counter used to keep track of which search is currently in progress. */
072  private int searchCounter;
073
074  /** The set of member URLs for determining whether entries match the criteria. */
075  private final LDAPURL[][] memberURLs;
076
077  /** The set of search filters for the search requests. */
078  private final SearchFilter[] searchFilters;
079
080
081
082  /**
083   * Creates a new dynamic group search thread that is associated with the
084   * provided member list and that will perform the search using the provided
085   * information.
086   *
087   * @param  memberList  The dynamic group member list with which this thread is
088   *                     associated.
089   * @param  baseDNs     The set of base DNs to use for the search requests.
090   * @param  filters     The set of search filters to use for the search
091   *                     requests.
092   * @param  memberURLs  The set of member URLs to use when determining if
093   *                     entries match the necessary group criteria.
094   */
095  public DynamicGroupSearchThread(DynamicGroupMemberList memberList,
096                                  DN[] baseDNs, SearchFilter[] filters,
097                                  LDAPURL[][] memberURLs)
098  {
099    super("Dynamic Group Search Thread " + memberList.getDynamicGroupDN());
100
101    this.memberList    = memberList;
102    this.baseDNs       = baseDNs;
103    this.searchFilters = filters;
104    this.memberURLs    = memberURLs;
105
106    searchCounter = 0;
107  }
108
109
110
111  /**
112   * Performs the set of searches and provides the results to the associated
113   * member list.
114   */
115  @Override
116  public void run()
117  {
118    InternalClientConnection conn = getRootConnection();
119    for (searchCounter = 0; searchCounter < baseDNs.length; searchCounter++)
120    {
121      DN baseDN = baseDNs[searchCounter];
122      SearchFilter filter = searchFilters[searchCounter];
123      // Include all the user attributes along with the ismemberof.
124      final SearchRequest request = newSearchRequest(baseDN, SearchScope.WHOLE_SUBTREE, filter)
125          .addAttribute("*", "ismemberof");
126      InternalSearchOperation searchOperation = conn.processSearch(request, this);
127
128      ResultCode resultCode = searchOperation.getResultCode();
129      if (resultCode != ResultCode.SUCCESS)
130      {
131        if (resultCode == ResultCode.NO_SUCH_OBJECT)
132        {
133          logger.warn(WARN_DYNAMICGROUP_NONEXISTENT_BASE_DN, baseDN,
134                  memberList.getDynamicGroupDN());
135          continue;
136        }
137        else
138        {
139          LocalizableMessage message =
140               ERR_DYNAMICGROUP_INTERNAL_SEARCH_FAILED.get(
141                       baseDN,
142                       filter,
143                       memberList.getDynamicGroupDN(),
144                       resultCode,
145                       searchOperation.getErrorMessage());
146          if (! memberList.addResult(
147                     new MembershipException(message, true)))
148          {
149            memberList.setSearchesCompleted();
150            return;
151          }
152        }
153      }
154    }
155
156    memberList.setSearchesCompleted();
157  }
158
159
160
161  /** {@inheritDoc} */
162  @Override
163  public void handleInternalSearchEntry(InternalSearchOperation searchOperation,
164                                        SearchResultEntry searchEntry)
165         throws DirectoryException
166  {
167    for (LDAPURL url : memberURLs[searchCounter])
168    {
169      if (url.matchesEntry(searchEntry))
170      {
171        if (! memberList.addResult(searchEntry))
172        {
173          LocalizableMessage message = ERR_DYNAMICGROUP_CANNOT_RETURN_ENTRY.
174              get(searchEntry.getName(), memberList.getDynamicGroupDN());
175          throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), message);
176        }
177
178        return;
179      }
180    }
181  }
182
183
184
185  /** {@inheritDoc} */
186  @Override
187  public void handleInternalSearchReference(
188                   InternalSearchOperation searchOperation,
189                   SearchResultReference searchReference)
190  {
191    // No implementation required.
192  }
193}
194