org.springframework.security.ldap.DefaultSpringSecurityContextSource.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.security.ldap.DefaultSpringSecurityContextSource.java

Source

/*
 * Copyright 2002-2016 the original author or authors.
 *
 * Licensed 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
 *
 *      https://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.springframework.security.ldap;

import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.StringTokenizer;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.ldap.core.support.DirContextAuthenticationStrategy;
import org.springframework.ldap.core.support.LdapContextSource;
import org.springframework.ldap.core.support.SimpleDirContextAuthenticationStrategy;
import org.springframework.util.Assert;

/**
 * ContextSource implementation which uses Spring LDAP's <tt>LdapContextSource</tt> as a
 * base class. Used internally by the Spring Security LDAP namespace configuration.
 * <p>
 * From Spring Security 3.0, Spring LDAP 1.3 is used and the <tt>ContextSource</tt>
 * interface provides support for binding with a username and password. As a result,
 * Spring LDAP <tt>ContextSource</tt> implementations such as <tt>LdapContextSource</tt>
 * may be used directly with Spring Security.
 * <p>
 * Spring LDAP 1.3 doesn't have JVM-level LDAP connection pooling enabled by default. This
 * class sets the <tt>pooled</tt> property to true, but customizes the
 * {@link DirContextAuthenticationStrategy} used to disable pooling when the <tt>DN</tt>
 * doesn't match the <tt>userDn</tt> property. This prevents pooling for calls to
 * {@link #getContext(String, String)} to authenticate as specific users.
 *
 * @author Luke Taylor
 * @since 2.0
 */
public class DefaultSpringSecurityContextSource extends LdapContextSource {
    protected final Log logger = LogFactory.getLog(getClass());

    private String rootDn;

    /**
     * Create and initialize an instance which will connect to the supplied LDAP URL. If
     * you want to use more than one server for fail-over, rather use the
     * {@link #DefaultSpringSecurityContextSource(List, String)} constructor.
     *
     * @param providerUrl an LDAP URL of the form
     * <code>ldap://localhost:389/base_dn</code>
     */
    public DefaultSpringSecurityContextSource(String providerUrl) {
        Assert.hasLength(providerUrl, "An LDAP connection URL must be supplied.");

        StringTokenizer st = new StringTokenizer(providerUrl);

        ArrayList<String> urls = new ArrayList<>();

        // Work out rootDn from the first URL and check that the other URLs (if any) match
        while (st.hasMoreTokens()) {
            String url = st.nextToken();
            String urlRootDn = LdapUtils.parseRootDnFromUrl(url);

            urls.add(url.substring(0, url.lastIndexOf(urlRootDn)));

            this.logger.info(" URL '" + url + "', root DN is '" + urlRootDn + "'");

            if (this.rootDn == null) {
                this.rootDn = urlRootDn;
            } else if (!this.rootDn.equals(urlRootDn)) {
                throw new IllegalArgumentException("Root DNs must be the same when using multiple URLs");
            }
        }

        setUrls(urls.toArray(new String[urls.size()]));
        setBase(this.rootDn);
        setPooled(true);
        setAuthenticationStrategy(new SimpleDirContextAuthenticationStrategy() {
            @Override
            @SuppressWarnings("rawtypes")
            public void setupEnvironment(Hashtable env, String dn, String password) {
                super.setupEnvironment(env, dn, password);
                // Remove the pooling flag unless we are authenticating as the 'manager'
                // user.
                if (!DefaultSpringSecurityContextSource.this.userDn.equals(dn)
                        && env.containsKey(SUN_LDAP_POOLING_FLAG)) {
                    DefaultSpringSecurityContextSource.this.logger.debug("Removing pooling flag for user " + dn);
                    env.remove(SUN_LDAP_POOLING_FLAG);
                }
            }
        });
    }

    /**
     * Create and initialize an instance which will connect of the LDAP Spring Security
     * Context Source. It will connect to any of the provided LDAP server URLs.
     *
     * @param urls A list of string values which are LDAP server URLs. An example would be
     * <code>ldap://ldap.company.com:389</code>. LDAPS URLs (SSL-secured) may be used as
     * well, given that Spring Security is able to connect to the server. Note that these
     * <b>URLs must not include the base DN</b>!
     * @param baseDn The common Base DN for all provided servers, e.g.
     *
     * <pre>
     * dc=company,dc=com
     * </pre>
     *
     * .
     */
    public DefaultSpringSecurityContextSource(List<String> urls, String baseDn) {
        this(buildProviderUrl(urls, baseDn));
    }

    /**
     * Builds a Spring LDAP-compliant Provider URL string, i.e. a space-separated list of
     * LDAP servers with their base DNs. As the base DN must be identical for all servers,
     * it needs to be supplied only once.
     *
     * @param urls A list of string values which are LDAP server URLs. An example would be
     *
     * <pre>
     * ldap://ldap.company.com:389
     * </pre>
     *
     * . LDAPS URLs may be used as well, given that Spring Security is able to connect to
     * the server.
     * @param baseDn The common Base DN for all provided servers, e.g.
     *
     * <pre>
     * dc=company,dc=com
     * </pre>
     *
     * .
     * @return A Spring Security/Spring LDAP-compliant Provider URL string.
     */
    private static String buildProviderUrl(List<String> urls, String baseDn) {
        Assert.notNull(baseDn, "The Base DN for the LDAP server must not be null.");
        Assert.notEmpty(urls, "At least one LDAP server URL must be provided.");

        String trimmedBaseDn = baseDn.trim();
        StringBuilder providerUrl = new StringBuilder();

        for (String serverUrl : urls) {
            String trimmedUrl = serverUrl.trim();
            if ("".equals(trimmedUrl)) {
                continue;
            }

            providerUrl.append(trimmedUrl);
            if (!trimmedUrl.endsWith("/")) {
                providerUrl.append("/");
            }
            providerUrl.append(trimmedBaseDn);
            providerUrl.append(" ");
        }

        return providerUrl.toString();

    }

}