/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt * or http://forgerock.org/license/CDDLv1.0.html. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at legal-notices/CDDLv1_0.txt. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Copyright 2008-2009 Sun Microsystems, Inc. * Portions Copyright 2014-2015 ForgeRock AS */ package org.opends.server.extensions; import org.forgerock.i18n.LocalizableMessage; import org.forgerock.i18n.slf4j.LocalizedLogger; import org.forgerock.opendj.ldap.ResultCode; import org.forgerock.opendj.ldap.SearchScope; import org.opends.server.api.DirectoryThread; import org.opends.server.core.DirectoryServer; import org.opends.server.protocols.internal.InternalClientConnection; import org.opends.server.protocols.internal.InternalSearchListener; import org.opends.server.protocols.internal.InternalSearchOperation; import org.opends.server.protocols.internal.SearchRequest; import static org.opends.server.protocols.internal.Requests.*; import org.opends.server.types.DN; import org.opends.server.types.DirectoryException; import org.opends.server.types.LDAPURL; import org.opends.server.types.MembershipException; import org.opends.server.types.SearchFilter; import org.opends.server.types.SearchResultEntry; import org.opends.server.types.SearchResultReference; import static org.opends.messages.ExtensionMessages.*; import static org.opends.server.protocols.internal.InternalClientConnection.*; /** * This class implements a Directory Server thread that will be used to perform * a background search to retrieve all of the members of a dynamic group. * <BR><BR> */ public class DynamicGroupSearchThread // FIXME -- Would it be better to implement this class using an Executor // rather than always creating a custom thread? extends DirectoryThread implements InternalSearchListener { private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); /** The set of base DNs for the search requests. */ private final DN[] baseDNs; /** The member list with which this search thread is associated. */ private final DynamicGroupMemberList memberList; /** A counter used to keep track of which search is currently in progress. */ private int searchCounter; /** The set of member URLs for determining whether entries match the criteria. */ private final LDAPURL[][] memberURLs; /** The set of search filters for the search requests. */ private final SearchFilter[] searchFilters; /** * Creates a new dynamic group search thread that is associated with the * provided member list and that will perform the search using the provided * information. * * @param memberList The dynamic group member list with which this thread is * associated. * @param baseDNs The set of base DNs to use for the search requests. * @param filters The set of search filters to use for the search * requests. * @param memberURLs The set of member URLs to use when determining if * entries match the necessary group criteria. */ public DynamicGroupSearchThread(DynamicGroupMemberList memberList, DN[] baseDNs, SearchFilter[] filters, LDAPURL[][] memberURLs) { super("Dynamic Group Search Thread " + memberList.getDynamicGroupDN()); this.memberList = memberList; this.baseDNs = baseDNs; this.searchFilters = filters; this.memberURLs = memberURLs; searchCounter = 0; } /** * Performs the set of searches and provides the results to the associated * member list. */ @Override public void run() { InternalClientConnection conn = getRootConnection(); for (searchCounter = 0; searchCounter < baseDNs.length; searchCounter++) { DN baseDN = baseDNs[searchCounter]; SearchFilter filter = searchFilters[searchCounter]; // Include all the user attributes along with the ismemberof. final SearchRequest request = newSearchRequest(baseDN, SearchScope.WHOLE_SUBTREE, filter) .addAttribute("*", "ismemberof"); InternalSearchOperation searchOperation = conn.processSearch(request, this); ResultCode resultCode = searchOperation.getResultCode(); if (resultCode != ResultCode.SUCCESS) { if (resultCode == ResultCode.NO_SUCH_OBJECT) { logger.warn(WARN_DYNAMICGROUP_NONEXISTENT_BASE_DN, baseDN, memberList.getDynamicGroupDN()); continue; } else { LocalizableMessage message = ERR_DYNAMICGROUP_INTERNAL_SEARCH_FAILED.get( baseDN, filter, memberList.getDynamicGroupDN(), resultCode, searchOperation.getErrorMessage()); if (! memberList.addResult( new MembershipException(message, true))) { memberList.setSearchesCompleted(); return; } } } } memberList.setSearchesCompleted(); } /** {@inheritDoc} */ @Override public void handleInternalSearchEntry(InternalSearchOperation searchOperation, SearchResultEntry searchEntry) throws DirectoryException { for (LDAPURL url : memberURLs[searchCounter]) { if (url.matchesEntry(searchEntry)) { if (! memberList.addResult(searchEntry)) { LocalizableMessage message = ERR_DYNAMICGROUP_CANNOT_RETURN_ENTRY. get(searchEntry.getName(), memberList.getDynamicGroupDN()); throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), message); } return; } } } /** {@inheritDoc} */ @Override public void handleInternalSearchReference( InternalSearchOperation searchOperation, SearchResultReference searchReference) { // No implementation required. } }