/* * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file * except in compliance with the License. * * You can obtain a copy of the License at * http://IdentityConnectors.dev.java.net/legal/license.txt * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== * "Portions Copyrighted 2014 ForgeRock AS" */ package org.identityconnectors.ldap.search; import static java.util.Collections.singletonList; import static org.identityconnectors.ldap.LdapUtil.isUnderContexts; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import javax.naming.directory.SearchControls; import javax.naming.directory.SearchResult; import javax.naming.ldap.LdapName; import org.identityconnectors.common.logging.Log; import org.identityconnectors.framework.common.exceptions.ConnectorException; import org.identityconnectors.framework.common.exceptions.UnknownUidException; import org.identityconnectors.framework.common.objects.Attribute; import org.identityconnectors.framework.common.objects.AttributeUtil; import org.identityconnectors.framework.common.objects.ConnectorObject; import org.identityconnectors.framework.common.objects.ObjectClass; import org.identityconnectors.framework.common.objects.OperationOptionsBuilder; import org.identityconnectors.framework.common.objects.ResultsHandler; import org.identityconnectors.framework.common.objects.Uid; import org.identityconnectors.framework.common.objects.filter.EqualsFilter; import org.identityconnectors.framework.common.objects.filter.FilterBuilder; import org.identityconnectors.ldap.LdapConnection; import org.identityconnectors.ldap.LdapEntry; /** * Helper methods for searching. The "get" methods throw an exception when * nothing is found; the "find" methods return null or an empty result. * * @author Andrei Badea */ public class LdapSearches { // TODO: when more than one base DN is specified in the configuration, // some searches could be faster by searching the entry under all naming // contexts on the server and then checking that the entry is really under one of the // configured base DNs. private static final Log log = Log.getLog(LdapSearches.class); private LdapSearches() { } /** * Returns the DN of the entry identified by the given Uid. Throws <code>UnknownUidException</code> * if such an entry does not exists. */ public static String getEntryDN(LdapConnection conn, ObjectClass oclass, Uid uid) { return findEntryDN(conn, oclass, uid, true); } /** * Returns the DN of the entry identified by the given Uid. May throw <code>UnknownUidException</code> * if such an entry does not exists, but not necessarily. */ public static String findEntryDN(LdapConnection conn, ObjectClass oclass, Uid uid) { return findEntryDN(conn, oclass, uid, false); } /** * Finds the DN of the entry corresponding to the given Uid. If the <code>check</code> * parameter is false, the method will take the quickest path to return the DN, but will not necessarily * check that an entry with the returned DN exists. If the <code>check</code> parameter is false, * the method will throw a <code>UnknownUidException</code> if the entry identified * by the Uid does not exist. */ private static String findEntryDN(LdapConnection conn, ObjectClass oclass, Uid uid, boolean check) { log.ok("Searching for object {0} of class {1}", uid.getUidValue(), oclass.getObjectClassValue()); LdapFilter ldapFilter = null; // If the Uid is actually the entry DN, we do not need to do a search do find the entry DN. String uidAttr = conn.getSchemaMapping().getLdapUidAttribute(oclass); if (LdapEntry.isDNAttribute(uidAttr)) { if (check) { // We'll do a search in order to check that the entry with that DN exists. ldapFilter = LdapFilter.forEntryDN(uid.getUidValue()); } else { // Short path. The Uid is the entry DN, and we do not need to check it, // so we can return it right away. return uid.getUidValue(); } } else { EqualsFilter filter = (EqualsFilter) FilterBuilder.equalTo(uid); ldapFilter = new LdapFilterTranslator(conn.getSchemaMapping(), oclass).createEqualsExpression(filter, false); } assert ldapFilter != null; OperationOptionsBuilder builder = new OperationOptionsBuilder(); builder.setAttributesToGet("entryDN"); LdapSearch search = new LdapSearch(conn, oclass, ldapFilter, null, builder.build()); ConnectorObject object = search.getSingleResult(); if (object != null) { return AttributeUtil.getStringValue(object.getAttributeByName("entryDN")); } throw new UnknownUidException(uid, oclass); } public static List<ConnectorObject> findObjects(LdapConnection conn, ObjectClass oclass, String baseDN, Attribute attr, String... attrsToGet) { log.ok("Searching for object with attribute {0} of class {1} in {2}", attr, oclass.getObjectClassValue(), baseDN); final List<ConnectorObject> result = new ArrayList<ConnectorObject>(); EqualsFilter filter = (EqualsFilter) FilterBuilder.equalTo(attr); LdapFilter ldapFilter = new LdapFilterTranslator(conn.getSchemaMapping(), oclass).createEqualsExpression(filter, false); OperationOptionsBuilder builder = new OperationOptionsBuilder(); builder.setAttributesToGet(attrsToGet); LdapSearch search = new LdapSearch(conn, oclass, ldapFilter, null, builder.build(), baseDN); search.execute(new ResultsHandler() { public boolean handle(ConnectorObject object) { result.add(object); return true; } }); return result; } public static ConnectorObject findObject(LdapConnection conn, ObjectClass oclass, LdapFilter filter, String... attrsToGet) { log.ok("Searching for object of class {0} with filter {1}", oclass.getObjectClassValue(), filter); OperationOptionsBuilder builder = new OperationOptionsBuilder(); builder.setAttributesToGet(attrsToGet); LdapSearch search = new LdapSearch(conn, oclass, filter, null, builder.build()); return search.getSingleResult(); } public static LdapEntry getEntry(LdapConnection conn, LdapName entryDN, String... ldapAttrsToGet) { log.ok("Searching for entry {0}", entryDN); final List<LdapEntry> result = new ArrayList<LdapEntry>(); if (!isUnderContexts(entryDN, conn.getConfiguration().getBaseContextsAsLdapNames())) { return null; } SearchControls controls = LdapInternalSearch.createDefaultSearchControls(); controls.setSearchScope(SearchControls.OBJECT_SCOPE); controls.setReturningAttributes(ldapAttrsToGet); LdapInternalSearch search = new LdapInternalSearch(conn, null, singletonList(entryDN.toString()), new DefaultSearchStrategy(true), controls); search.execute(new LdapSearchResultsHandler() { public boolean handle(String baseDN, SearchResult searchResult) { result.add(LdapEntry.create(baseDN, searchResult)); return false; } }); if (!result.isEmpty()) { return result.get(0); } throw new ConnectorException(conn.format("entryNotFound", null, entryDN)); } public static void findEntries(LdapSearchResultsHandler handler, LdapConnection conn, String filter, String... ldapAttrsToGet) { log.ok("Searching for entries matching {0}", filter); List<String> baseDNs = Arrays.asList(conn.getConfiguration().getBaseContexts()); SearchControls controls = LdapInternalSearch.createDefaultSearchControls(); controls.setSearchScope(SearchControls.SUBTREE_SCOPE); controls.setReturningAttributes(ldapAttrsToGet); LdapInternalSearch search = new LdapInternalSearch(conn, filter, baseDNs, new DefaultSearchStrategy(false), controls); search.execute(handler); } }