blob: f2c9402e30937c608681efb45dcc94b7536db1e4 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
package org.apache.jackrabbit.oak.security.authentication.ldap.impl;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import org.apache.directory.api.util.Strings;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Property;
import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Configuration of the ldap provider.
*/
@Component(
label = "Apache Jackrabbit Oak LDAP Identity Provider",
name = "org.apache.jackrabbit.oak.security.authentication.ldap.impl.LdapIdentityProvider",
configurationFactory = true,
metatype = true,
ds = false
)
public class LdapProviderConfig {
/**
* @see #getName()
*/
public static final String PARAM_NAME_DEFAULT = "ldap";
/**
* @see #getName()
*/
@Property(
label = "LDAP Provider Name",
description = "Name of this LDAP provider configuration. This is used to reference this provider by the login modules.",
value = PARAM_NAME_DEFAULT
)
public static final String PARAM_NAME = "provider.name";
/**
* @see #getHostname()
*/
public static final String PARAM_LDAP_HOST_DEFAULT = "localhost";
/**
* @see #getHostname()
*/
@Property(
label = "LDAP Server Hostname",
description = "Hostname of the LDAP server",
value = PARAM_LDAP_HOST_DEFAULT
)
public static final String PARAM_LDAP_HOST = "host.name";
/**
* @see #getPort()
*/
public static final int PARAM_LDAP_PORT_DEFAULT = 389;
/**
* @see #getPort()
*/
@Property(
label = "LDAP Server Port",
description = "Port of the LDAP server",
intValue = PARAM_LDAP_PORT_DEFAULT
)
public static final String PARAM_LDAP_PORT = "host.port";
/**
* @see #useSSL()
*/
public static final boolean PARAM_USE_SSL_DEFAULT = false;
/**
* @see #useSSL()
*/
@Property(
label = "Use SSL",
description = "Indicates if an SSL (LDAPs) connection should be used.",
boolValue = PARAM_USE_SSL_DEFAULT
)
public static final String PARAM_USE_SSL = "host.ssl";
/**
* @see #useTLS()
*/
public static final boolean PARAM_USE_TLS_DEFAULT = false;
/**
* @see #useTLS()
*/
@Property(
label = "Use TLS",
description = "Indicates if TLS should be started on connections.",
boolValue = PARAM_USE_TLS_DEFAULT
)
public static final String PARAM_USE_TLS = "host.tls";
/**
* @see #noCertCheck()
*/
public static final boolean PARAM_NO_CERT_CHECK_DEFAULT = false;
/**
* @see #noCertCheck()
*/
@Property(
label = "Disable certificate checking",
description = "Indicates if server certificate validation should be disabled.",
boolValue = PARAM_NO_CERT_CHECK_DEFAULT
)
public static final String PARAM_NO_CERT_CHECK = "host.noCertCheck";
/**
* @see #getBindDN()
*/
public static final String PARAM_BIND_DN_DEFAULT = "";
/**
* @see #getBindDN()
*/
@Property(
label = "Bind DN",
description = "DN of the user for authentication. Leave empty for anonymous bind.",
value = PARAM_BIND_DN_DEFAULT
)
public static final String PARAM_BIND_DN = "bind.dn";
/**
* @see #getBindPassword()
*/
public static final String PARAM_BIND_PASSWORD_DEFAULT = "";
/**
* @see #getBindPassword()
*/
@Property(
label = "Bind Password",
description = "Password of the user for authentication.",
passwordValue = PARAM_BIND_PASSWORD_DEFAULT
)
public static final String PARAM_BIND_PASSWORD = "bind.password";
/**
* @see #getSearchTimeout()
*/
public static final String PARAM_SEARCH_TIMEOUT_DEFAULT = "60s";
/**
* @see #getSearchTimeout()
*/
@Property(
label = "Search Timeout",
description = "Time in until a search times out (eg: '1s' or '1m 30s').",
value = PARAM_SEARCH_TIMEOUT_DEFAULT
)
public static final String PARAM_SEARCH_TIMEOUT = "searchTimeout";
/**
* @see PoolConfig#getMaxActive()
*/
public static final int PARAM_ADMIN_POOL_MAX_ACTIVE_DEFAULT = 8;
/**
* @see PoolConfig#getMaxActive()
*/
@Property(
label = "Admin pool max active",
description = "The max active size of the admin connection pool.",
longValue = PARAM_ADMIN_POOL_MAX_ACTIVE_DEFAULT
)
public static final String PARAM_ADMIN_POOL_MAX_ACTIVE = "adminPool.maxActive";
/**
* @see PoolConfig#lookupOnValidate()
*/
public static final boolean PARAM_ADMIN_POOL_LOOKUP_ON_VALIDATE_DEFAULT = true;
/**
* @see PoolConfig#lookupOnValidate()
*/
@Property(
label = "Admin pool lookup on validate",
description = "Indicates an ROOT DSE lookup is performed to test if the connection is still valid when taking it out of the pool.",
boolValue = PARAM_ADMIN_POOL_LOOKUP_ON_VALIDATE_DEFAULT
)
public static final String PARAM_ADMIN_POOL_LOOKUP_ON_VALIDATE = "adminPool.lookupOnValidate";
/**
* @see PoolConfig#getMinEvictableIdleTimeMillis()
*/
public static final String PARAM_ADMIN_POOL_MIN_EVICTABLE_IDLE_TIME_DEFAULT = "-1";
/**
* @see PoolConfig#getMinEvictableIdleTimeMillis()
*/
@Property(
label = "Admin pool min evictable idle time",
description = "The minimum amount of time a connection from the admin pool must be idle before becoming eligible for eviction by the idle object evictor, if running (eg: '1m 30s'). When non-positive, no connections will be evicted from the pool due to idle time alone.",
value = PARAM_ADMIN_POOL_MIN_EVICTABLE_IDLE_TIME_DEFAULT
)
public static final String PARAM_ADMIN_POOL_MIN_EVICTABLE_IDLE_TIME = "adminPool.minEvictableIdleTime";
/**
* @see PoolConfig#getTimeBetweenEvictionRunsMillis()
*/
public static final String PARAM_ADMIN_POOL_TIME_BETWEEN_EVICTION_RUNS_DEFAULT = "-1";
/**
* @see PoolConfig#getTimeBetweenEvictionRunsMillis()
*/
@Property(
label = "Time interval to sleep between evictor runs for the admin pool",
description = "Time interval to sleep between runs of the idle object evictor thread for the admin pool (eg: '1m 30s'). When non-positive, no idle object evictor thread will be run.",
value = PARAM_ADMIN_POOL_TIME_BETWEEN_EVICTION_RUNS_DEFAULT
)
public static final String PARAM_ADMIN_POOL_TIME_BETWEEN_EVICTION_RUNS = "adminPool.timeBetweenEvictionRuns";
/**
* @see PoolConfig#getNumTestsPerEvictionRun()
*/
public static final int PARAM_ADMIN_POOL_NUM_TESTS_PER_EVICTION_RUN_DEFAULT = 3;
/**
* @see PoolConfig#getNumTestsPerEvictionRun()
*/
@Property(
label = "Max number of objects to be tested per run of the idle object evictor for the admin pool",
description = "The max number of objects to examine during each run of the idle object evictor thread for the admin pool (if any)",
intValue = PARAM_ADMIN_POOL_NUM_TESTS_PER_EVICTION_RUN_DEFAULT
)
public static final String PARAM_ADMIN_POOL_NUM_TESTS_PER_EVICTION_RUN = "adminPool.numTestsPerEvictionRun";
/**
* @see PoolConfig#getMaxActive()
*/
public static final int PARAM_USER_POOL_MAX_ACTIVE_DEFAULT = 8;
/**
* @see PoolConfig#getMaxActive()
*/
@Property(
label = "User pool max active",
description = "The max active size of the user connection pool.",
longValue = PARAM_USER_POOL_MAX_ACTIVE_DEFAULT
)
public static final String PARAM_USER_POOL_MAX_ACTIVE = "userPool.maxActive";
/**
* @see PoolConfig#lookupOnValidate()
*/
public static final boolean PARAM_USER_POOL_LOOKUP_ON_VALIDATE_DEFAULT = true;
/**
* @see PoolConfig#lookupOnValidate()
*/
@Property(
label = "User pool lookup on validate",
description = "Indicates an ROOT DSE lookup is performed to test if the connection is still valid when taking it out of the pool.",
boolValue = PARAM_USER_POOL_LOOKUP_ON_VALIDATE_DEFAULT
)
public static final String PARAM_USER_POOL_LOOKUP_ON_VALIDATE = "userPool.lookupOnValidate";
/**
* @see PoolConfig#getMinEvictableIdleTimeMillis()
*/
public static final String PARAM_USER_POOL_MIN_EVICTABLE_IDLE_TIME_DEFAULT = "-1";
/**
* @see PoolConfig#getMinEvictableIdleTimeMillis()
*/
@Property(
label = "User pool min evictable idle time",
description = "The minimum amount of time a connection from the user pool must be idle before becoming eligible for eviction by the idle object evictor, if running (eg: '1m 30s'). When non-positive, no connections will be evicted from the pool due to idle time alone.",
value = PARAM_USER_POOL_MIN_EVICTABLE_IDLE_TIME_DEFAULT
)
public static final String PARAM_USER_POOL_MIN_EVICTABLE_IDLE_TIME = "userPool.minEvictableIdleTime";
/**
* @see PoolConfig#getTimeBetweenEvictionRunsMillis()
*/
public static final String PARAM_USER_POOL_TIME_BETWEEN_EVICTION_RUNS_DEFAULT = "-1";
/**
* @see PoolConfig#getTimeBetweenEvictionRunsMillis()
*/
@Property(
label = "Time interval to sleep between evictor runs for the user pool",
description = "Time interval to sleep between runs of the idle object evictor thread for the user pool (eg: '1m 30s'). When non-positive, no idle object evictor thread will be run.",
value = PARAM_USER_POOL_TIME_BETWEEN_EVICTION_RUNS_DEFAULT
)
public static final String PARAM_USER_POOL_TIME_BETWEEN_EVICTION_RUNS = "userPool.timeBetweenEvictionRuns";
/**
* @see PoolConfig#getNumTestsPerEvictionRun()
*/
public static final int PARAM_USER_POOL_NUM_TESTS_PER_EVICTION_RUN_DEFAULT = 3;
/**
* @see PoolConfig#getNumTestsPerEvictionRun()
*/
@Property(
label = "Max number of objects to be tested per run of the idle object evictor for the user pool",
description = "The max number of objects to examine during each run of the idle object evictor thread for the user pool (if any)",
intValue = PARAM_USER_POOL_NUM_TESTS_PER_EVICTION_RUN_DEFAULT
)
public static final String PARAM_USER_POOL_NUM_TESTS_PER_EVICTION_RUN = "userPool.numTestsPerEvictionRun";
/**
* @see Identity#getBaseDN()
*/
public static final String PARAM_USER_BASE_DN_DEFAULT = "ou=people,o=example,dc=com";
/**
* @see Identity#getBaseDN()
*/
@Property(
label = "User base DN",
description = "The base DN for user searches.",
value = PARAM_USER_BASE_DN_DEFAULT
)
public static final String PARAM_USER_BASE_DN = "user.baseDN";
/**
* @see Identity#getObjectClasses()
*/
public static final String[] PARAM_USER_OBJECTCLASS_DEFAULT = {"person"};
/**
* @see Identity#getObjectClasses()
*/
@Property(
label = "User object classes",
description = "The list of object classes an user entry must contain.",
value = {"person"},
cardinality = Integer.MAX_VALUE
)
public static final String PARAM_USER_OBJECTCLASS = "user.objectclass";
/**
* @see Identity#getIdAttribute()
*/
public static final String PARAM_USER_ID_ATTRIBUTE_DEFAULT = "uid";
/**
* @see Identity#getIdAttribute()
*/
@Property(
label = "User id attribute",
description = "Name of the attribute that contains the user id.",
value = PARAM_USER_ID_ATTRIBUTE_DEFAULT
)
public static final String PARAM_USER_ID_ATTRIBUTE = "user.idAttribute";
/**
* @see Identity#getExtraFilter()
*/
public static final String PARAM_USER_EXTRA_FILTER_DEFAULT = "";
/**
* @see Identity#getExtraFilter()
*/
@Property(
label = "User extra filter",
description = "Extra LDAP filter to use when searching for users. The final filter is" +
"formatted like: '(&(<idAttr>=<userId>)(objectclass=<objectclass>)<extraFilter>)'",
value = PARAM_USER_EXTRA_FILTER_DEFAULT
)
public static final String PARAM_USER_EXTRA_FILTER = "user.extraFilter";
/**
* @see Identity#makeDnPath()
*/
public static final boolean PARAM_USER_MAKE_DN_PATH_DEFAULT = false;
/**
* @see Identity#makeDnPath()
*/
@Property(
label = "User DN paths",
description = "Controls if the DN should be used for calculating a portion of the intermediate path.",
boolValue = PARAM_USER_MAKE_DN_PATH_DEFAULT
)
public static final String PARAM_USER_MAKE_DN_PATH = "user.makeDnPath";
/**
* @see Identity#getBaseDN()
*/
public static final String PARAM_GROUP_BASE_DN_DEFAULT = "ou=groups,o=example,dc=com";
/**
* @see Identity#getBaseDN()
*/
@Property(
label = "Group base DN",
description = "The base DN for group searches.",
value = PARAM_GROUP_BASE_DN_DEFAULT
)
public static final String PARAM_GROUP_BASE_DN = "group.baseDN";
/**
* @see Identity#getObjectClasses()
*/
public static final String[] PARAM_GROUP_OBJECTCLASS_DEFAULT = {"groupOfUniqueNames"};
/**
* @see Identity#getObjectClasses()
*/
@Property(
label = "Group object classes",
description = "The list of object classes a group entry must contain.",
value = {"groupOfUniqueNames"},
cardinality = Integer.MAX_VALUE
)
public static final String PARAM_GROUP_OBJECTCLASS = "group.objectclass";
/**
* @see Identity#getIdAttribute()
*/
public static final String PARAM_GROUP_NAME_ATTRIBUTE_DEFAULT = "cn";
/**
* @see Identity#getIdAttribute()
*/
@Property(
label = "Group name attribute",
description = "Name of the attribute that contains the group name.",
value = PARAM_GROUP_NAME_ATTRIBUTE_DEFAULT
)
public static final String PARAM_GROUP_NAME_ATTRIBUTE = "group.nameAttribute";
/**
* @see Identity#getExtraFilter()
*/
public static final String PARAM_GROUP_EXTRA_FILTER_DEFAULT = "";
/**
* @see Identity#getExtraFilter()
*/
@Property(
label = "Group extra filter",
description = "Extra LDAP filter to use when searching for groups. The final filter is" +
"formatted like: '(&(<nameAttr>=<groupName>)(objectclass=<objectclass>)<extraFilter>)'",
value = PARAM_GROUP_EXTRA_FILTER_DEFAULT
)
public static final String PARAM_GROUP_EXTRA_FILTER = "group.extraFilter";
/**
* @see Identity#makeDnPath()
*/
public static final boolean PARAM_GROUP_MAKE_DN_PATH_DEFAULT = false;
/**
* @see Identity#makeDnPath()
*/
@Property(
label = "Group DN paths",
description = "Controls if the DN should be used for calculating a portion of the intermediate path.",
boolValue = PARAM_GROUP_MAKE_DN_PATH_DEFAULT
)
public static final String PARAM_GROUP_MAKE_DN_PATH = "group.makeDnPath";
/**
* @see #getGroupMemberAttribute()
*/
public static final String PARAM_GROUP_MEMBER_ATTRIBUTE_DEFAULT = "uniquemember";
/**
* @see #getGroupMemberAttribute()
*/
@Property(
label = "Group member attribute",
description = "Group attribute that contains the member(s) of a group.",
value = PARAM_GROUP_MEMBER_ATTRIBUTE_DEFAULT
)
public static final String PARAM_GROUP_MEMBER_ATTRIBUTE = "group.memberAttribute";
/**
* @see #getUseUidForExtId()
*/
public static final boolean PARAM_USE_UID_FOR_EXT_ID_DEFAULT = false;
/**
* @see #getUseUidForExtId()
*/
@Property(
label = "Use user id for external ids",
description = "If enabled, the value of the user id (resp. group name) attribute will be used to create external identifiers. Leave disabled to use the DN instead.",
boolValue = PARAM_USE_UID_FOR_EXT_ID_DEFAULT
)
public static final String PARAM_USE_UID_FOR_EXT_ID = "useUidForExtId";
/**
* @see Identity#getCustomAttributes()
*/
public static final String[] PARAM_CUSTOM_ATTRIBUTES_DEFAULT = {};
/**
* @see Identity#getCustomAttributes()
*/
@Property(
label = "Custom Attributes",
description = "Attributes retrieved when looking up LDAP entries. Leave empty to retrieve all attributes.",
value = {},
cardinality = Integer.MAX_VALUE
)
public static final String PARAM_CUSTOM_ATTRIBUTES = "customattributes";
/**
* Defines the configuration of an identity (user or group).
*/
public class Identity {
private String baseDN;
private String[] objectClasses;
private String idAttribute;
private String[] customAttributes = {};
private String extraFilter;
private String filterTemplate;
private boolean makeDnPath;
/**
* Configures the base DN for searches of this kind of identity
* @return the base DN
*/
@NotNull
public String getBaseDN() {
return baseDN;
}
/**
* Sets the base DN for search of this kind of identity.
* @param baseDN the DN as string.
* @return {@code this}
* @see #getBaseDN()
*/
@NotNull
public Identity setBaseDN(@NotNull String baseDN) {
this.baseDN = baseDN;
return this;
}
/**
* Configures the object classes of this kind of identity.
* @return an array of object classes
* @see #getSearchFilter(String) for more detail about searching and filtering
*/
@NotNull
public String[] getObjectClasses() {
return objectClasses;
}
/**
* Sets the object classes.
* @param objectClasses the object classes
* @return {@code this}
* @see #getObjectClasses()
*/
@NotNull
public Identity setObjectClasses(@NotNull String ... objectClasses) {
this.objectClasses = objectClasses;
filterTemplate = null;
memberOfFilterTemplate = null;
return this;
}
/**
* Configures the attribute that is used to identify this identity by id. For users this is the attribute that
* holds the user id, for groups this is the attribute that holds the group name.
*
* @return the id attribute name
* @see #getSearchFilter(String) for more detail about searching and filtering
*/
@NotNull
public String getIdAttribute() {
return idAttribute;
}
/**
* Sets the id attribute.
* @param idAttribute the id attribute name
* @return {@code this}
* @see #getIdAttribute()
*/
@NotNull
public Identity setIdAttribute(@NotNull String idAttribute) {
this.idAttribute = idAttribute;
filterTemplate = null;
memberOfFilterTemplate = null;
return this;
}
/**
* Configures the extra LDAP filter that is appended to the internally computed filter when searching for
* identities.
*
* @return the extra filter
*/
@Nullable
public String getExtraFilter() {
return extraFilter;
}
/**
* Sets the extra search filter.
* @param extraFilter the filter
* @return {@code this}
* @see #getExtraFilter()
*/
@NotNull
public Identity setExtraFilter(@Nullable String extraFilter) {
this.extraFilter = extraFilter;
filterTemplate = null;
memberOfFilterTemplate = null;
return this;
}
/**
* Configures if the identities DN should be used to generate a portion of the authorizables intermediate path.
* @return {@code true} if the DN is used a intermediate path.
*/
public boolean makeDnPath() {
return makeDnPath;
}
/**
* Sets the intermediate path flag.
* @param makeDnPath {@code true} to use the DN as intermediate path
* @return {@code this}
* @see #makeDnPath()
*/
@NotNull
public Identity setMakeDnPath(boolean makeDnPath) {
this.makeDnPath = makeDnPath;
return this;
}
/**
* Returns the LDAP filter that is used when searching this type of identity. The filter is based on the
* configuration and has the following format:
*
* <pre>{@code
* (&(${idAttr}=${id})(objectclass=${objectclass})${extraFilter})
* }</pre>
*
* Note that the objectclass part is repeated according to the specified objectclasses in {@link #getObjectClasses()}.
*
* @param id the id value
* @return the search filter
*/
@NotNull
public String getSearchFilter(@NotNull String id) {
if (filterTemplate == null) {
StringBuilder filter = new StringBuilder("(&(")
.append(idAttribute)
.append("=%s)");
for (String objectClass: objectClasses) {
filter.append("(objectclass=")
.append(encodeFilterValue(objectClass))
.append(')');
}
if (extraFilter != null && extraFilter.length() > 0) {
filter.append(extraFilter);
}
filter.append(')');
filterTemplate = filter.toString();
}
return String.format(filterTemplate, encodeFilterValue(id));
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("Identity{");
sb.append("baseDN='").append(baseDN).append('\'');
sb.append(", objectClasses=").append(Arrays.toString(objectClasses));
sb.append(", idAttribute='").append(idAttribute).append('\'');
sb.append(", userAttributes='").append(Arrays.toString(customAttributes));
sb.append(", extraFilter='").append(extraFilter).append('\'');
sb.append(", filterTemplate='").append(filterTemplate).append('\'');
sb.append(", makeDnPath=").append(makeDnPath);
sb.append('}');
return sb.toString();
}
}
/**
* Defines the configuration of a connection pool. Currently we do not support all
* available configuration options of the pool implementation.
* (documentation copied from {@link org.apache.commons.pool2.impl.GenericObjectPool})
*/
public static class PoolConfig {
private int maxActiveSize;
private boolean lookupOnValidate;
private long minEvictableIdleTimeMillis;
private long timeBetweenEvictionRunsMillis;
private int numTestsPerEvictionRun;
/**
* Returns the maximum number of objects that can be allocated by the pool
* (checked out to clients, or idle awaiting checkout) at a given time.
* When non-positive, there is no limit to the number of objects that can
* be managed by the pool at one time. A value of 0 disables this pool.
*
* @return the cap on the total number of object instances managed by the pool.
* @see #setMaxActive
*/
public int getMaxActive() {
return maxActiveSize;
}
/**
* Sets the cap on the number of objects that can be allocated by the pool.
*
* @see #getMaxActive
* @param maxActive the new upper limit of the pool size
* @return this
*/
@NotNull
public PoolConfig setMaxActive(int maxActive) {
this.maxActiveSize = maxActive;
return this;
}
/**
* Defines if the lookup on validate flag is enabled. If enable a connection that taken from the
* pool are validated before used. currently this is done by performing a lookup to the ROOT DSE, which
* might not be allowed on all LDAP servers.
*
* @return {@code true} if the flag is enabled.
*/
public boolean lookupOnValidate() {
return lookupOnValidate;
}
/**
* Sets the lookup on validate flag.
*
* @see #lookupOnValidate()
* @param lookupOnValidate the new value of the lookup on validate flag
* @return this
*/
@NotNull
public PoolConfig setLookupOnValidate(boolean lookupOnValidate) {
this.lookupOnValidate = lookupOnValidate;
return this;
}
/**
* Returns the minimum amount of time a connection may sit idle in the pool
* before it is eligible for eviction by the idle object evictor
* (if running). When non-positive, no connections will be evicted from the pool due to idle time alone.
*
* @return minimum amount of time a connection may sit idle in the pool before it is eligible for eviction.
*/
public long getMinEvictableIdleTimeMillis() { return minEvictableIdleTimeMillis; }
/**
* Sets the minimum amount of time a connection may sit idle in the pool
* before it is eligible for eviction by the idle object evictor
* (if any).
* When non-positive, no connections will be evicted from the pool
* due to idle time alone.
*
* @param minEvictableIdleTimeMillis minimum amount of time a connection may sit idle in the pool before
* it is eligible for eviction.
* @return this
*/
public PoolConfig setMinEvictableIdleTimeMillis(long minEvictableIdleTimeMillis) {
this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
return this;
}
/**
* Returns the number of milliseconds to sleep between runs of the
* idle object evictor thread.
* When non-positive, no idle object evictor thread will be
* run.
*
* @return number of milliseconds to sleep between evictor runs.
*/
public long getTimeBetweenEvictionRunsMillis() { return timeBetweenEvictionRunsMillis; }
/**
* Sets the number of milliseconds to sleep between runs of the
* idle object evictor thread.
* When non-positive, no idle object evictor thread will be
* run.
*
* @param timeBetweenEvictionRunsMillis number of milliseconds to sleep between evictor runs.
* @return this
*/
public PoolConfig setTimeBetweenEvictionRunsMillis(long timeBetweenEvictionRunsMillis) {
this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
return this;
}
/**
* Returns the max number of objects to examine during each run of the
* idle object evictor thread (if any).
*
* @return max number of objects to examine during each evictor run.
* @see #setNumTestsPerEvictionRun
* @see #setTimeBetweenEvictionRunsMillis
*/
public int getNumTestsPerEvictionRun() { return numTestsPerEvictionRun; }
/**
* Sets the max number of objects to examine during each run of the
* idle object evictor thread (if any).
* <p>
* When a negative value is supplied, <code>ceil(number of idle objects)/abs({@link #getNumTestsPerEvictionRun})</code>
* tests will be run. That is, when the value is <i>-n</i>, roughly one <i>n</i>th of the
* idle objects will be tested per run. When the value is positive, the number of tests
* actually performed in each run will be the minimum of this value and the number of instances
* idle in the pool.
*
* @param numTestsPerEvictionRun max number of objects to examine during each evictor run.
* @see #getNumTestsPerEvictionRun
* @see #setTimeBetweenEvictionRunsMillis
* @return this
*/
public PoolConfig setNumTestsPerEvictionRun(int numTestsPerEvictionRun) {
this.numTestsPerEvictionRun = numTestsPerEvictionRun;
return this;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("PoolConfig{");
sb.append("maxActiveSize=").append(maxActiveSize);
sb.append(", lookupOnValidate=").append(lookupOnValidate);
sb.append(", minEvictableIdleTimeMillis=").append(minEvictableIdleTimeMillis);
sb.append(", timeBetweenEvictionRunsMillis=").append(timeBetweenEvictionRunsMillis);
sb.append(", numTestsPerEvictionRun=").append(numTestsPerEvictionRun);
sb.append('}');
return sb.toString();
}
}
/**
* Creates a new LDAP provider configuration based on the properties store in the given parameters.
* @param params the configuration parameters.
* @return the config
*/
public static LdapProviderConfig of(ConfigurationParameters params) {
LdapProviderConfig cfg = new LdapProviderConfig()
.setName(params.getConfigValue(PARAM_NAME, PARAM_NAME_DEFAULT))
.setHostname(params.getConfigValue(PARAM_LDAP_HOST, PARAM_LDAP_HOST_DEFAULT))
.setPort(params.getConfigValue(PARAM_LDAP_PORT, PARAM_LDAP_PORT_DEFAULT))
.setUseSSL(params.getConfigValue(PARAM_USE_SSL, PARAM_USE_SSL_DEFAULT))
.setUseTLS(params.getConfigValue(PARAM_USE_TLS, PARAM_USE_TLS_DEFAULT))
.setNoCertCheck(params.getConfigValue(PARAM_NO_CERT_CHECK, PARAM_NO_CERT_CHECK_DEFAULT))
.setBindDN(params.getConfigValue(PARAM_BIND_DN, PARAM_BIND_DN_DEFAULT))
.setBindPassword(params.getConfigValue(PARAM_BIND_PASSWORD, PARAM_BIND_PASSWORD_DEFAULT))
.setGroupMemberAttribute(params.getConfigValue(PARAM_GROUP_MEMBER_ATTRIBUTE, PARAM_GROUP_MEMBER_ATTRIBUTE_DEFAULT))
.setCustomAttributes(params.getConfigValue(PARAM_CUSTOM_ATTRIBUTES, PARAM_CUSTOM_ATTRIBUTES_DEFAULT))
.setUseUidForExtId(params.getConfigValue(PARAM_USE_UID_FOR_EXT_ID, PARAM_USE_UID_FOR_EXT_ID_DEFAULT));
ConfigurationParameters.Milliseconds ms = ConfigurationParameters.Milliseconds.of(params.getConfigValue(PARAM_SEARCH_TIMEOUT, PARAM_SEARCH_TIMEOUT_DEFAULT));
if (ms != null) {
cfg.setSearchTimeout(ms.value);
}
cfg.getUserConfig()
.setBaseDN(params.getConfigValue(PARAM_USER_BASE_DN, PARAM_USER_BASE_DN))
.setIdAttribute(params.getConfigValue(PARAM_USER_ID_ATTRIBUTE, PARAM_USER_ID_ATTRIBUTE_DEFAULT))
.setExtraFilter(params.getConfigValue(PARAM_USER_EXTRA_FILTER, PARAM_USER_EXTRA_FILTER_DEFAULT))
.setObjectClasses(params.getConfigValue(PARAM_USER_OBJECTCLASS, PARAM_USER_OBJECTCLASS_DEFAULT))
.setMakeDnPath(params.getConfigValue(PARAM_USER_MAKE_DN_PATH, PARAM_USER_MAKE_DN_PATH_DEFAULT));
cfg.getGroupConfig()
.setBaseDN(params.getConfigValue(PARAM_GROUP_BASE_DN, PARAM_GROUP_BASE_DN))
.setIdAttribute(params.getConfigValue(PARAM_GROUP_NAME_ATTRIBUTE, PARAM_GROUP_NAME_ATTRIBUTE_DEFAULT))
.setExtraFilter(params.getConfigValue(PARAM_GROUP_EXTRA_FILTER, PARAM_GROUP_EXTRA_FILTER_DEFAULT))
.setObjectClasses(params.getConfigValue(PARAM_GROUP_OBJECTCLASS, PARAM_GROUP_OBJECTCLASS_DEFAULT))
.setMakeDnPath(params.getConfigValue(PARAM_GROUP_MAKE_DN_PATH, PARAM_GROUP_MAKE_DN_PATH_DEFAULT));
ConfigurationParameters.Milliseconds msMeitAdmin = ConfigurationParameters.Milliseconds.of(params.getConfigValue(PARAM_ADMIN_POOL_MIN_EVICTABLE_IDLE_TIME, PARAM_ADMIN_POOL_MIN_EVICTABLE_IDLE_TIME_DEFAULT));
ConfigurationParameters.Milliseconds msTberAdmin = ConfigurationParameters.Milliseconds.of(params.getConfigValue(PARAM_ADMIN_POOL_TIME_BETWEEN_EVICTION_RUNS, PARAM_ADMIN_POOL_TIME_BETWEEN_EVICTION_RUNS_DEFAULT));
cfg.getAdminPoolConfig()
.setLookupOnValidate(params.getConfigValue(PARAM_ADMIN_POOL_LOOKUP_ON_VALIDATE, PARAM_ADMIN_POOL_LOOKUP_ON_VALIDATE_DEFAULT))
.setMaxActive(params.getConfigValue(PARAM_ADMIN_POOL_MAX_ACTIVE, PARAM_ADMIN_POOL_MAX_ACTIVE_DEFAULT))
.setNumTestsPerEvictionRun(params.getConfigValue(PARAM_ADMIN_POOL_NUM_TESTS_PER_EVICTION_RUN, PARAM_ADMIN_POOL_NUM_TESTS_PER_EVICTION_RUN_DEFAULT));
if (msMeitAdmin != null) {
cfg.getAdminPoolConfig().setMinEvictableIdleTimeMillis(msMeitAdmin.value);
}
if (msTberAdmin != null) {
cfg.getAdminPoolConfig().setTimeBetweenEvictionRunsMillis(msTberAdmin.value);
}
ConfigurationParameters.Milliseconds msMeitUser = ConfigurationParameters.Milliseconds.of(params.getConfigValue(PARAM_USER_POOL_MIN_EVICTABLE_IDLE_TIME, PARAM_USER_POOL_MIN_EVICTABLE_IDLE_TIME_DEFAULT));
ConfigurationParameters.Milliseconds msTberUser = ConfigurationParameters.Milliseconds.of(params.getConfigValue(PARAM_USER_POOL_TIME_BETWEEN_EVICTION_RUNS, PARAM_USER_POOL_TIME_BETWEEN_EVICTION_RUNS_DEFAULT));
cfg.getUserPoolConfig()
.setLookupOnValidate(params.getConfigValue(PARAM_USER_POOL_LOOKUP_ON_VALIDATE, PARAM_USER_POOL_LOOKUP_ON_VALIDATE_DEFAULT))
.setMaxActive(params.getConfigValue(PARAM_USER_POOL_MAX_ACTIVE, PARAM_USER_POOL_MAX_ACTIVE_DEFAULT))
.setNumTestsPerEvictionRun(params.getConfigValue(PARAM_USER_POOL_NUM_TESTS_PER_EVICTION_RUN, PARAM_USER_POOL_NUM_TESTS_PER_EVICTION_RUN_DEFAULT));
if (msMeitUser != null) {
cfg.getUserPoolConfig().setMinEvictableIdleTimeMillis(msMeitUser.value);
}
if (msTberUser != null) {
cfg.getUserPoolConfig().setTimeBetweenEvictionRunsMillis(msTberUser.value);
}
return cfg;
}
private String name = PARAM_NAME_DEFAULT;
private String hostname = PARAM_LDAP_HOST_DEFAULT;
private int port = PARAM_LDAP_PORT_DEFAULT;
private boolean useSSL = PARAM_USE_SSL_DEFAULT;
private boolean useTLS = PARAM_USE_TLS_DEFAULT;
private boolean noCertCheck = PARAM_NO_CERT_CHECK_DEFAULT;
private String bindDN = PARAM_BIND_DN_DEFAULT;
private String bindPassword = PARAM_BIND_PASSWORD_DEFAULT;
private long searchTimeout = ConfigurationParameters.Milliseconds.of(PARAM_SEARCH_TIMEOUT_DEFAULT).value;
private String groupMemberAttribute = PARAM_GROUP_MEMBER_ATTRIBUTE;
private boolean useUidForExtId = PARAM_USE_UID_FOR_EXT_ID_DEFAULT;
private String memberOfFilterTemplate;
private String[] customAttributes = PARAM_CUSTOM_ATTRIBUTES_DEFAULT;
private final PoolConfig adminPoolConfig = new PoolConfig()
.setMaxActive(PARAM_ADMIN_POOL_MAX_ACTIVE_DEFAULT);
private final PoolConfig userPoolConfig = new PoolConfig()
.setMaxActive(PARAM_USER_POOL_MAX_ACTIVE_DEFAULT);
private final Identity userConfig = new Identity()
.setBaseDN(PARAM_USER_BASE_DN_DEFAULT)
.setExtraFilter(PARAM_USER_EXTRA_FILTER_DEFAULT)
.setIdAttribute(PARAM_USER_ID_ATTRIBUTE_DEFAULT)
.setMakeDnPath(PARAM_USER_MAKE_DN_PATH_DEFAULT)
.setObjectClasses(PARAM_USER_OBJECTCLASS_DEFAULT);
private final Identity groupConfig = new Identity()
.setBaseDN(PARAM_GROUP_BASE_DN_DEFAULT)
.setExtraFilter(PARAM_GROUP_EXTRA_FILTER_DEFAULT)
.setIdAttribute(PARAM_GROUP_NAME_ATTRIBUTE_DEFAULT)
.setMakeDnPath(PARAM_GROUP_MAKE_DN_PATH_DEFAULT)
.setObjectClasses(PARAM_GROUP_OBJECTCLASS_DEFAULT);
/**
* Returns the name of this provider configuration.
* The default is {@value #PARAM_NAME_DEFAULT}
*
* @return the name.
*/
@NotNull
public String getName() {
return name;
}
/**
* Sets the name of this provider.
* @param name the name
* @return {@code this}
* @see #getName()
*/
@NotNull
public LdapProviderConfig setName(@NotNull String name) {
this.name = name;
return this;
}
/**
* Configures the hostname of the LDAP server.
* The default is {@value #PARAM_LDAP_HOST_DEFAULT}
*
* @return the hostname
*/
@NotNull
public String getHostname() {
return hostname;
}
/**
* Sets the hostname.
* @param hostname the hostname
* @return {@code this}
* @see #getHostname()
*/
@NotNull
public LdapProviderConfig setHostname(@NotNull String hostname) {
this.hostname = hostname;
return this;
}
/**
* Configures the port of the LDAP server.
* The default is {@value #PARAM_LDAP_PORT_DEFAULT}
*
* @return the port
*/
public int getPort() {
return port;
}
/**
* Sets the port.
* @param port the port
* @return {@code this}
* @see #getPort()
*/
@NotNull
public LdapProviderConfig setPort(int port) {
this.port = port;
return this;
}
/**
* Configures whether SSL connections should be used.
* The default is {@value #PARAM_USE_SSL_DEFAULT}.
*
* @return {@code true} if SSL should be used.
*/
public boolean useSSL() {
return useSSL;
}
/**
* Enables SSL connections.
* @param useSSL {@code true} to enable SSL
* @return {@code this}
* @see #useSSL()
*/
@NotNull
public LdapProviderConfig setUseSSL(boolean useSSL) {
this.useSSL = useSSL;
return this;
}
/**
* Configures whether TLS connections should be used.
* The default is {@value #PARAM_USE_TLS_DEFAULT}.
*
* @return {@code true} if TLS should be used.
*/
public boolean useTLS() {
return useTLS;
}
/**
* Enables TLS connections.
* @param useTLS {@code true} to enable TLS
* @return {@code this}
* @see #useTLS()
*/
@NotNull
public LdapProviderConfig setUseTLS(boolean useTLS) {
this.useTLS = useTLS;
return this;
}
/**
* Configures whether certificates on SSL/TLS connections should be validated.
* The default is {@value #PARAM_NO_CERT_CHECK_DEFAULT}.
*
* @return {@code true} if certificates should not be validated
*/
public boolean noCertCheck() {
return noCertCheck;
}
/**
* Disables certificate validation.
* @param noCertCheck {@code true} to disable certificate validation
* @return {@code this}
* @see #noCertCheck()
*/
@NotNull
public LdapProviderConfig setNoCertCheck(boolean noCertCheck) {
this.noCertCheck = noCertCheck;
return this;
}
/**
* Configures the DN that is used to bind to the LDAP server. If this value is {@code null} or an empty string,
* anonymous connections are used.
* @return the bind DN or {@code null}.
*/
@Nullable
public String getBindDN() {
return bindDN;
}
/**
* Sets the bind DN.
* @param bindDN the DN
* @return {@code this}
* @see #getBindDN()
*/
@NotNull
public LdapProviderConfig setBindDN(@Nullable String bindDN) {
this.bindDN = bindDN;
return this;
}
/**
* Configures the password that is used to bind to the LDAP server. This value is not used for anonymous binds.
* @return the password.
*/
@Nullable
public String getBindPassword() {
return bindPassword;
}
/**
* Sets the bind password
* @param bindPassword the password
* @return {@code this}
* @see #getBindPassword()
*/
@NotNull
public LdapProviderConfig setBindPassword(@Nullable String bindPassword) {
this.bindPassword = bindPassword;
return this;
}
/**
* Configures the timeout in milliseconds that is used for all LDAP searches.
* The default is {@value #PARAM_SEARCH_TIMEOUT_DEFAULT}.
*
* @return the timeout in milliseconds.
*/
public long getSearchTimeout() {
return searchTimeout;
}
/**
* Sets the search timeout.
* @param searchTimeout the timeout in milliseconds
* @return {@code this}
* @see #getSearchTimeout()
*/
@NotNull
public LdapProviderConfig setSearchTimeout(long searchTimeout) {
this.searchTimeout = searchTimeout;
return this;
}
/**
* Configures the attribute that stores the members of a group.
* Default is {@value #PARAM_GROUP_MEMBER_ATTRIBUTE_DEFAULT}
*
* @return the group member attribute
*/
@NotNull
public String getGroupMemberAttribute() {
return groupMemberAttribute;
}
/**
* Sets the group member attribute.
* @param groupMemberAttribute the attribute name
* @return {@code this}
* @see #getGroupMemberAttribute()
*/
@NotNull
public LdapProviderConfig setGroupMemberAttribute(@NotNull String groupMemberAttribute) {
this.groupMemberAttribute = groupMemberAttribute;
return this;
}
/**
* If true, the value of the user id (resp. group name) attribute will be used to create external identifiers. Otherwise the DN will be used, which is the default.
*
* @return true iff the value of the user id (resp. group name) attribute will be used to create external identifiers
*/
public boolean getUseUidForExtId() {
return useUidForExtId;
}
/**
* Sets the flag that controls if the user id (resp. gruop name) will be used instead of the DN to create external ids.
*
* @see #getUseUidForExtId()
* @param useUidForExtId the new value of #useUidForExtId
* @return {@code this}
*/
@NotNull
public LdapProviderConfig setUseUidForExtId(boolean useUidForExtId) {
this.useUidForExtId = useUidForExtId;
return this;
}
/**
* Optionally configures an array of attribute names that will be retrieved when looking up LDAP entries.
* Defaults to the empty array indicating that all attributes will be retrieved.
*
* @return an array of attribute names. The empty array indicates that all attributes will be retrieved.
*/
@NotNull
public String[] getCustomAttributes() {
return customAttributes;
}
/**
* Sets the attribute names to be retrieved when looking up LDAP entries. The empty array indicates that all attributes will be retrieved.
*
* @param customAttributes an array of attribute names
* @return the Identity instance
*/
@NotNull
public LdapProviderConfig setCustomAttributes(@NotNull String[] customAttributes) {
this.customAttributes = this.removeEmptyStrings(customAttributes);
return this;
}
/**
* Returns the LDAP filter that is used when searching for groups where an identity is member of.
* The filter is based on the configuration and has the following format:
*
* <pre>{@code
* (&(${memberAttribute}=${dn})(objectclass=${objectclass})${extraFilter})
* }</pre>
*
* Note that the objectclass part is repeated according to the specified objectclasses in
* {@link Identity#getObjectClasses()} of the group configuration.
*
* @param dn the dn of the identity to search for
* @return the search filter
*/
public String getMemberOfSearchFilter(@NotNull String dn) {
if (memberOfFilterTemplate == null) {
StringBuilder filter = new StringBuilder("(&(")
.append(groupMemberAttribute)
.append("=%s)");
for (String objectClass: groupConfig.objectClasses) {
filter.append("(objectclass=")
.append(encodeFilterValue(objectClass))
.append(')');
}
if (groupConfig.extraFilter != null && groupConfig.extraFilter.length() > 0) {
filter.append(groupConfig.extraFilter);
}
filter.append(')');
memberOfFilterTemplate = filter.toString();
}
return String.format(memberOfFilterTemplate, encodeFilterValue(dn));
}
/**
* Returns the user specific configuration.
* @return the user config.
*/
@NotNull
public Identity getUserConfig() {
return userConfig;
}
/**
* Returns the group specific configuration.
* @return the groups config.
*/
@NotNull
public Identity getGroupConfig() {
return groupConfig;
}
/**
* Returns the admin connection pool configuration.
* @return admin pool config
*/
@NotNull
public PoolConfig getAdminPoolConfig() {
return adminPoolConfig;
}
/**
* Returns the user connection pool configuration.
* @return user pool config
*/
@NotNull
public PoolConfig getUserPoolConfig() {
return userPoolConfig;
}
/**
* Copied from org.apache.directory.api.ldap.model.filter.FilterEncoder#encodeFilterValue(java.lang.String)
* in order to keep this configuration LDAP client independent.
*
* Handles encoding of special characters in LDAP search filter assertion values using the
* &lt;valueencoding&gt; rule as described in <a href="http://www.ietf.org/rfc/rfc4515.txt">RFC 4515</a>.
*
* @param value Right hand side of "attrId=value" assertion occurring in an LDAP search filter.
* @return Escaped version of {@code value}
*/
public static String encodeFilterValue(String value) {
StringBuilder sb = null;
for (int i = 0; i < value.length(); i++) {
char ch = value.charAt(i);
String replace;
switch (ch) {
case '*':
replace = "\\2A";
break;
case '(':
replace = "\\28";
break;
case ')':
replace = "\\29";
break;
case '\\':
replace = "\\5C";
break;
case '\0':
replace = "\\00";
break;
default:
replace = null;
}
if (replace != null) {
if (sb == null) {
sb = new StringBuilder(value.length() * 2);
sb.append(value.substring(0, i));
}
sb.append(replace);
} else if (sb != null) {
sb.append(ch);
}
}
return (sb == null ? value : sb.toString());
}
//OAK-5490
private String[] removeEmptyStrings(@NotNull String[] params) {
List<String> list = Arrays.asList(params);
if (!list.contains(Strings.EMPTY_STRING)) {
return params;
}
List<String> resultList = new LinkedList<>(list);
while (resultList.contains(Strings.EMPTY_STRING)) {
resultList.remove(Strings.EMPTY_STRING);
}
String[] result = new String[resultList.size()];
return resultList.toArray(result);
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("LdapProviderConfig{");
sb.append("name='").append(name).append('\'');
sb.append(", hostname='").append(hostname).append('\'');
sb.append(", port=").append(port);
sb.append(", useSSL=").append(useSSL);
sb.append(", useTLS=").append(useTLS);
sb.append(", noCertCheck=").append(noCertCheck);
sb.append(", bindDN='").append(bindDN).append('\'');
sb.append(", bindPassword='***'");
sb.append(", searchTimeout=").append(searchTimeout);
sb.append(", groupMemberAttribute='").append(groupMemberAttribute).append('\'');
sb.append(", useUidForExtId='").append(useUidForExtId).append('\'');
sb.append(", memberOfFilterTemplate='").append(memberOfFilterTemplate).append('\'');
sb.append(", adminPoolConfig=").append(adminPoolConfig);
sb.append(", userPoolConfig=").append(userPoolConfig);
sb.append(", userConfig=").append(userConfig);
sb.append(", groupConfig=").append(groupConfig);
sb.append('}');
return sb.toString();
}
}