/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* See LICENSE.txt included in this distribution 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 LICENSE.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 (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
*/
package opengrok.auth.plugin.ldap;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.naming.CommunicationException;
import javax.naming.NameNotFoundException;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.SizeLimitExceededException;
import javax.naming.TimeLimitExceededException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import io.micrometer.core.instrument.Timer;
import opengrok.auth.plugin.configuration.Configuration;
import opengrok.auth.plugin.util.WebHook;
import opengrok.auth.plugin.util.WebHooks;
import org.opengrok.indexer.Metrics;
public class LdapFacade extends AbstractLdapProvider {
private static final Logger LOGGER = Logger.getLogger(LdapFacade.class.getName());
/**
* Default LDAP filter.
*/
private static final String LDAP_FILTER = "objectclass=*";
/**
* Default timeout for retrieving the results.
*/
private static final int LDAP_SEARCH_TIMEOUT = 5000; // ms
/**
* Default limit of result traversal.
*
* @see
* SearchControls
*
* basically it does not mean that the server must send at most this number
* of results, but that the program should not iterate more than this number
* over the results.
*/
private static final int LDAP_COUNT_LIMIT = 100;
/**
* When there is no active server in the pool, the facade waits this time
* interval (since the last failure) until it tries the servers again.
*
* This should avoid heavy load to the LDAP servers when they are all
* broken/not responding/down - pool waiting.
*
* Also each server uses this same interval since its last failure - per
* server waiting.
*/
private int interval = 10 * 1000; // ms
/**
* LDAP search base.
*/
private String searchBase;
/**
* Server pool.
*/
private List servers = new ArrayList<>();
/**
* server webHooks.
*/
private WebHooks webHooks;
private SearchControls controls;
private int actualServer = -1;
private long errorTimestamp = 0;
private boolean reported = false;
private final Timer ldapLookupTimer = Timer.builder("ldap.latency").
description("LDAP lookup latency").
register(Metrics.getRegistry());
/**
* Interface for converting LDAP results into user defined types.
*
* @param the type of the result
*/
private interface AttributeMapper {
T mapFromAttributes(Attributes attr) throws NamingException;
}
/**
* Transforms the attributes to the set of strings used for authorization.
*
* Currently this behaves like it get all records stored in
*/
private static class ContentAttributeMapper implements AttributeMapper