org.opencastproject.userdirectory.ldap.LdapUserProviderInstance.java Source code

Java tutorial

Introduction

Here is the source code for org.opencastproject.userdirectory.ldap.LdapUserProviderInstance.java

Source

/**
 *  Copyright 2009, 2010 The Regents of the University of California
 *  Licensed under the Educational Community 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.osedu.org/licenses/ECL-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.opencastproject.userdirectory.ldap;

import org.opencastproject.security.api.CachingUserProviderMXBean;
import org.opencastproject.security.api.User;
import org.opencastproject.security.api.UserProvider;

import com.google.common.base.Function;
import com.google.common.collect.MapMaker;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
import org.springframework.security.ldap.search.FilterBasedLdapUserSearch;
import org.springframework.security.ldap.userdetails.LdapUserDetailsMapper;
import org.springframework.security.ldap.userdetails.LdapUserDetailsService;

import java.lang.management.ManagementFactory;
import java.util.Collection;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

import javax.management.InstanceNotFoundException;
import javax.management.MBeanServer;
import javax.management.ObjectName;

/**
 * A UserProvider that reads user roles from LDAP entries.
 */
public class LdapUserProviderInstance implements UserProvider, CachingUserProviderMXBean {

    /** The logger */
    private static final Logger logger = LoggerFactory.getLogger(LdapUserProviderInstance.class);

    /** The spring ldap userdetails service delegate */
    private LdapUserDetailsService delegate = null;

    /** The organization id */
    private String organization = null;

    /** Total number of requests made to load users */
    private AtomicLong requests = null;

    /** The number of requests made to ldap */
    private AtomicLong ldapLoads = null;

    /** A cache of users, which lightens the load on the LDAP server */
    private ConcurrentMap<String, Object> cache = null;

    /** A token to store in the miss cache */
    protected Object nullToken = new Object();

    /**
     * Constructs an ldap user provider with the needed settings.
     * 
     * @param pid
     *          the pid of this service
     * @param organization
     *          the organization
     * @param searchBase
     *          the ldap search base
     * @param searchFilter
     *          the ldap search filter
     * @param url
     *          the url of the ldap server
     * @param userDn
     *          the user to authenticate as
     * @param password
     *          the user credentials
     * @param roleAttributesGlob
     *          the comma separate list of ldap attributes to treat as roles
     * @param cacheSize
     *          the number of users to cache
     * @param cacheExpiration
     *          the number of minutes to cache users
     */
    // CHECKSTYLE:OFF
    LdapUserProviderInstance(String pid, String organization, String searchBase, String searchFilter, String url,
            String userDn, String password, String roleAttributesGlob, int cacheSize, int cacheExpiration) {
        // CHECKSTYLE:ON
        this.organization = organization;
        logger.debug("Creating LdapUserProvider instance with pid=" + pid + ", and organization=" + organization
                + ", to LDAP server at url:  " + url);

        DefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource(url);
        if (StringUtils.isNotBlank(userDn)) {
            contextSource.setPassword(password);
            contextSource.setUserDn(userDn);
            // Required so that authentication will actually be used
            contextSource.setAnonymousReadOnly(false);
        } else {
            // No password set so try to connect anonymously. 
            contextSource.setAnonymousReadOnly(true);
        }

        try {
            contextSource.afterPropertiesSet();
        } catch (Exception e) {
            throw new org.opencastproject.util.ConfigurationException("Unable to create a spring context source",
                    e);
        }
        FilterBasedLdapUserSearch userSearch = new FilterBasedLdapUserSearch(searchBase, searchFilter,
                contextSource);
        userSearch.setReturningAttributes(roleAttributesGlob.split(","));
        this.delegate = new LdapUserDetailsService(userSearch);

        if (StringUtils.isNotBlank(roleAttributesGlob)) {
            LdapUserDetailsMapper mapper = new LdapUserDetailsMapper();
            mapper.setRoleAttributes(roleAttributesGlob.split(","));
            this.delegate.setUserDetailsMapper(mapper);
        }

        // Setup the caches
        cache = new MapMaker().maximumSize(cacheSize).expireAfterWrite(cacheExpiration, TimeUnit.MINUTES)
                .makeComputingMap(new Function<String, Object>() {
                    public Object apply(String id) {
                        User user = loadUserFromLdap(id);
                        return user == null ? nullToken : user;
                    }
                });

        registerMBean(pid);
    }

    /**
     * Registers an MXBean.
     */
    protected void registerMBean(String pid) {
        // register with jmx
        requests = new AtomicLong();
        ldapLoads = new AtomicLong();
        try {
            ObjectName name;
            name = LdapUserProviderFactory.getObjectName(pid);
            Object mbean = this;
            MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
            try {
                mbs.unregisterMBean(name);
            } catch (InstanceNotFoundException e) {
                logger.debug(name + " was not registered");
            }
            mbs.registerMBean(mbean, name);
        } catch (Exception e) {
            logger.warn("Unable to register {} as an mbean: {}", this, e);
        }
    }

    /**
     * {@inheritDoc}
     * 
     * @see org.opencastproject.security.api.UserProvider#getOrganization()
     */
    @Override
    public String getOrganization() {
        return organization;
    }

    /**
     * {@inheritDoc}
     * 
     * @see org.opencastproject.security.api.UserProvider#loadUser(java.lang.String)
     */
    @Override
    public User loadUser(String userName) {
        logger.debug("LdapUserProvider is loading user " + userName);
        requests.incrementAndGet();
        try {
            Object user = cache.get(userName);
            if (user == nullToken) {
                return null;
            } else {
                return (User) user;
            }
        } catch (NullPointerException e) {
            logger.debug("This map throws NPE rather than returning null.  Swallowing that exception here.");
            return null;
        }
    }

    /**
     * Loads a user from LDAP.
     * 
     * @param userName
     *          the username
     * @return the user
     */
    protected User loadUserFromLdap(String userName) {
        if (delegate == null || cache == null) {
            throw new IllegalStateException("The LDAP user detail service has not yet been configured");
        }
        ldapLoads.incrementAndGet();
        UserDetails userDetails = null;

        Thread currentThread = Thread.currentThread();
        ClassLoader originalClassloader = currentThread.getContextClassLoader();
        try {
            currentThread.setContextClassLoader(LdapUserProviderFactory.class.getClassLoader());
            try {
                userDetails = delegate.loadUserByUsername(userName);
            } catch (UsernameNotFoundException e) {
                cache.put(userName, nullToken);
                return null;
            }

            Collection<? extends GrantedAuthority> authorities = userDetails.getAuthorities();
            String[] roles = null;
            if (authorities != null) {
                int i = 0;
                roles = new String[authorities.size()];
                for (GrantedAuthority authority : authorities) {
                    String role = authority.getAuthority();
                    roles[i++] = role;
                }
            }
            User user = new User(userDetails.getUsername(), getOrganization(), roles);
            cache.put(userName, user);
            return user;
        } finally {
            currentThread.setContextClassLoader(originalClassloader);
        }
    }

    /**
     * {@inheritDoc}
     * 
     * @see org.opencastproject.security.api.CachingUserProviderMXBean#getCacheHitRatio()
     */
    public float getCacheHitRatio() {
        if (requests.get() == 0) {
            return 0;
        }
        return (float) (requests.get() - ldapLoads.get()) / requests.get();
    }

}