/** * Licensed to The Apereo Foundation under one or more contributor license * agreements. See the NOTICE file distributed with this work for additional * information regarding copyright ownership. * * * The Apereo Foundation licenses this file to you 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://opensource.org/licenses/ecl2.txt * * 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.Organization; import org.opencastproject.security.api.OrganizationDirectoryService; import org.opencastproject.security.api.UserProvider; import org.opencastproject.util.NotFoundException; import org.apache.commons.lang3.StringUtils; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceRegistration; import org.osgi.service.cm.ConfigurationException; import org.osgi.service.cm.ManagedServiceFactory; import org.osgi.service.component.ComponentContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.lang.management.ManagementFactory; import java.util.Dictionary; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import javax.management.MalformedObjectNameException; import javax.management.ObjectName; /** * LDAP implementation of the spring UserDetailsService, taking configuration information from the component context. */ public class LdapUserProviderFactory implements ManagedServiceFactory { /** The logger */ protected static final Logger logger = LoggerFactory.getLogger(LdapUserProviderFactory.class); /** This service factory's PID */ public static final String PID = "org.opencastproject.userdirectory.ldap"; /** The key to look up the ldap search filter in the service configuration properties */ private static final String SEARCH_FILTER_KEY = "org.opencastproject.userdirectory.ldap.searchfilter"; /** The key to look up the ldap search base in the service configuration properties */ private static final String SEARCH_BASE_KEY = "org.opencastproject.userdirectory.ldap.searchbase"; /** The key to look up the ldap server URL in the service configuration properties */ private static final String LDAP_URL_KEY = "org.opencastproject.userdirectory.ldap.url"; /** The key to look up the role attributes in the service configuration properties */ private static final String ROLE_ATTRIBUTES_KEY = "org.opencastproject.userdirectory.ldap.roleattributes"; /** The key to look up the organization identifer in the service configuration properties */ private static final String ORGANIZATION_KEY = "org.opencastproject.userdirectory.ldap.org"; /** The key to look up the user DN to use for performing searches. */ private static final String SEARCH_USER_DN = "org.opencastproject.userdirectory.ldap.userDn"; /** The key to look up the password to use for performing searches */ private static final String SEARCH_PASSWORD = "org.opencastproject.userdirectory.ldap.password"; /** The key to look up the number of user records to cache */ private static final String CACHE_SIZE = "org.opencastproject.userdirectory.ldap.cache.size"; /** The key to look up the number of minutes to cache users */ private static final String CACHE_EXPIRATION = "org.opencastproject.userdirectory.ldap.cache.expiration"; /** The key to indicate a prefix that will be added to every role read from the LDAP */ private static final String ROLE_PREFIX_KEY = "org.opencastproject.userdirectory.ldap.roleprefix"; /** A map of pid to ldap user provider instance */ private Map<String, ServiceRegistration> providerRegistrations = new ConcurrentHashMap<String, ServiceRegistration>(); /** The OSGI bundle context */ protected BundleContext bundleContext = null; /** The organization directory service */ private OrganizationDirectoryService orgDirectory; /** OSGi callback for setting the organization directory service. */ public void setOrgDirectory(OrganizationDirectoryService orgDirectory) { this.orgDirectory = orgDirectory; } /** * Callback for activation of this component. * * @param cc * the component context */ public void activate(ComponentContext cc) { logger.debug("Activate LdapUserProviderFactory"); this.bundleContext = cc.getBundleContext(); } /** * {@inheritDoc} * * @see org.osgi.service.cm.ManagedServiceFactory#getName() */ @Override public String getName() { return PID; } /** * {@inheritDoc} * * @see org.osgi.service.cm.ManagedServiceFactory#updated(java.lang.String, java.util.Dictionary) */ @Override public void updated(String pid, Dictionary properties) throws ConfigurationException { logger.debug("Updating LdapUserProviderFactory"); String organization = (String) properties.get(ORGANIZATION_KEY); if (StringUtils.isBlank(organization)) throw new ConfigurationException(ORGANIZATION_KEY, "is not set"); String searchBase = (String) properties.get(SEARCH_BASE_KEY); if (StringUtils.isBlank(searchBase)) throw new ConfigurationException(SEARCH_BASE_KEY, "is not set"); String searchFilter = (String) properties.get(SEARCH_FILTER_KEY); if (StringUtils.isBlank(searchFilter)) throw new ConfigurationException(SEARCH_FILTER_KEY, "is not set"); String url = (String) properties.get(LDAP_URL_KEY); if (StringUtils.isBlank(url)) throw new ConfigurationException(LDAP_URL_KEY, "is not set"); String userDn = (String) properties.get(SEARCH_USER_DN); String password = (String) properties.get(SEARCH_PASSWORD); String roleAttributesGlob = (String) properties.get(ROLE_ATTRIBUTES_KEY); String rolePrefix = (String) properties.get(ROLE_PREFIX_KEY); int cacheSize = 1000; logger.debug("Using cache size " + properties.get(CACHE_SIZE) + " for " + LdapUserProviderFactory.class.getName()); try { if (properties.get(CACHE_SIZE) != null) { Integer configuredCacheSize = Integer.parseInt(properties.get(CACHE_SIZE).toString()); if (configuredCacheSize != null) { cacheSize = configuredCacheSize.intValue(); } } } catch (Exception e) { logger.warn("{} could not be loaded, default value is used: {}", CACHE_SIZE, cacheSize); } int cacheExpiration = 1; try { if (properties.get(CACHE_EXPIRATION) != null) { Integer configuredCacheExpiration = Integer.parseInt(properties.get(CACHE_EXPIRATION).toString()); if (configuredCacheExpiration != null) { cacheExpiration = configuredCacheExpiration.intValue(); } } } catch (Exception e) { logger.warn("{} could not be loaded, default value is used: {}", CACHE_EXPIRATION, cacheExpiration); } // Now that we have everything we need, go ahead and activate a new provider, removing an old one if necessary ServiceRegistration existingRegistration = providerRegistrations.remove(pid); if (existingRegistration != null) { existingRegistration.unregister(); } Organization org; try { org = orgDirectory.getOrganization(organization); } catch (NotFoundException e) { logger.warn("Organization {} not found!", organization); throw new ConfigurationException(ORGANIZATION_KEY, "not found"); } LdapUserProviderInstance provider = new LdapUserProviderInstance(pid, org, searchBase, searchFilter, url, userDn, password, roleAttributesGlob, rolePrefix, cacheSize, cacheExpiration); providerRegistrations.put(pid, bundleContext.registerService(UserProvider.class.getName(), provider, null)); } /** * {@inheritDoc} * * @see org.osgi.service.cm.ManagedServiceFactory#deleted(java.lang.String) */ @Override public void deleted(String pid) { ServiceRegistration registration = providerRegistrations.remove(pid); if (registration != null) { registration.unregister(); try { ManagementFactory.getPlatformMBeanServer().unregisterMBean(LdapUserProviderFactory.getObjectName(pid)); } catch (Exception e) { logger.warn("Unable to unregister mbean for pid='{}': {}", pid, e.getMessage()); } } } /** * Builds a JMX object name for a given PID * * @param pid * the PID * @return the object name * @throws NullPointerException * @throws MalformedObjectNameException */ public static final ObjectName getObjectName(String pid) throws MalformedObjectNameException, NullPointerException { return new ObjectName(pid + ":type=LDAPRequests"); } }