/******************************************************************************* * Copyright (c) 2008 Cambridge Semantics Incorporated. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * File: $Source$ * Created by: Matthew Roy ( <a href="mailto:mroy@cambridgesemantics.com">mroy@cambridgesemantics.com </a>) * Created on: Jul 15, 2008 * Revision: $Id$ * * Contributors: * Cambridge Semantics Incorporated - initial API and implementation *******************************************************************************/ package org.openanzo.security.ldap; import java.io.UnsupportedEncodingException; import java.security.Security; import java.text.MessageFormat; import java.util.Dictionary; import java.util.Properties; import java.util.Set; import org.openanzo.cache.CachedAuthenticationService; import org.openanzo.cache.ICacheProvider; import org.openanzo.exceptions.AnzoException; import org.openanzo.exceptions.AnzoRuntimeException; import org.openanzo.exceptions.ExceptionConstants; import org.openanzo.exceptions.LogUtils; import org.openanzo.osgi.ConfiguredServiceActivator; import org.openanzo.osgi.GenericObjectClassDef; import org.openanzo.osgi.IServiceTrackerListener; import org.openanzo.osgi.OsgiConfigurationUtils; import org.openanzo.osgi.OsgiServiceTracker; import org.openanzo.osgi.ServiceLifecycleState; import org.openanzo.osgi.attributes.LDAPAttributes; import org.openanzo.osgi.attributes.ServicesAttributes; import org.openanzo.rdf.Constants.OSGI; import org.openanzo.security.keystore.KeyStoreDictionary; import org.openanzo.security.ldap.attributes.LDAPAuthAttributes; import org.openanzo.services.IAuthenticationService; import org.openanzo.services.IUserRolesExtender; import org.openanzo.services.LDAPDictionary; import org.openanzo.services.ServicesDictionary; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceReference; import org.osgi.framework.ServiceRegistration; import org.osgi.service.cm.ConfigurationException; import org.osgi.service.event.EventConstants; import org.osgi.service.event.EventHandler; import org.osgi.service.metatype.AttributeDefinition; import org.osgi.service.metatype.ObjectClassDefinition; import org.osgi.util.tracker.ServiceTracker; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.novell.ldap.LDAPConnection; import com.novell.ldap.LDAPEntry; import com.novell.ldap.LDAPException; import com.novell.ldap.LDAPJSSESecureSocketFactory; import com.novell.ldap.LDAPReferralException; import com.novell.ldap.LDAPSearchResults; /** * Activator for LDAP Authentication provider * * @author Matthew Roy ( <a href="mailto:mroy@cambridgesemantics.com">mroy@cambridgesemantics.com</a>) * */ public class LdapAuthenticatorActivator extends ConfiguredServiceActivator { private static final Logger log = LoggerFactory.getLogger(LdapAuthenticatorActivator.class); private LdapAuthenticationProvider authProvider = null; private OsgiServiceTracker<IUserRolesExtender> extenderTracker; /** Service PID for LDAP Authentication Provider */ public static final String SERVICE_PID = "org.openanzo.security.ldap.LdapAuthentication"; public static final GenericObjectClassDef classDef = new GenericObjectClassDef(SERVICE_PID, "LDAP Authentication Provider", "Authentication provider that binds to an embedded or external LDAP server for authentication and user's roles.", new AttributeDefinition[] { ServicesAttributes.Enabled, LDAPAuthAttributes.UseEmbeddedServer, LDAPAttributes.Host, LDAPAttributes.Port, LDAPAuthAttributes.RoleBaseDN, LDAPAuthAttributes.UserBaseDN, LDAPAuthAttributes.SysadminRole, LDAPAuthAttributes.UserSearch, LDAPAuthAttributes.RolesSearch, LDAPAuthAttributes.RoleObjectClass, LDAPAuthAttributes.UserObjectClass }, new AttributeDefinition[] { LDAPAttributes.UseSSL, LDAPAuthAttributes.UserIdAttribute, LDAPAttributes.LdapServerUser, LDAPAttributes.LdapServerPassword, LDAPAuthAttributes.RoleSearchFilter, LDAPAuthAttributes.UserSearchFilter }) { @SuppressWarnings("unchecked") @Override public boolean validateConfiguration(BundleContext context, Dictionary config) throws AnzoException { String host = LDAPDictionary.getHost(config, "localhost"); Integer port = LDAPDictionary.getPort(config, 10389); String ldapAdminDN = LDAPDictionary.getLdapServerUser(config); String ldapAdminPassword = LDAPDictionary.getLdapServerPassword(config); String userSearch = LDAPAuthDictionary.getUserSearch(config); String rolesSearchTemplateFormat = LDAPAuthDictionary.getRolesSearch(config); String userId = (String) config.get("org.openanzo.ldap.testUserId"); String testPassword = (String) config.get("org.openanzo.ldap.testPassword"); String baseDN = LDAPAuthDictionary.getUserBaseDN(config); String roleBaseDN = LDAPAuthDictionary.getRoleBaseDN(config); String trustStorePassword = KeyStoreDictionary.getClientTrustPassword(config); String truststoreType = KeyStoreDictionary.getClientTruststoreType(config); String truststoreFile = OsgiConfigurationUtils.preprocessString(KeyStoreDictionary.getClientTrustFileLocation(config), context); String keyStorePassword = KeyStoreDictionary.getKeyPassword(config); String keystoreType = KeyStoreDictionary.getKeystoreType(config); String keystoreFile = OsgiConfigurationUtils.preprocessString(KeyStoreDictionary.getKeyFileLocation(config), context); boolean usessl = false; Boolean useSSL = LDAPDictionary.getUseSSL(config); if (useSSL != null) { usessl = useSSL.booleanValue(); } try { userSearch = (new MessageFormat(userSearch)).format(new String[] { userId }); } catch (java.lang.IllegalArgumentException e) { throw new AnzoException(ExceptionConstants.SERVER.INSTALL_LDAP_INVALID_CONFIGURATION, e); } if (userId == null || userId.trim().length() == 0) { throw new AnzoException(ExceptionConstants.CORE.NULL_PARAMETER, "org.openanzo.ldap.testUserId"); } if (testPassword == null || testPassword.trim().length() == 0) { throw new AnzoException(ExceptionConstants.CORE.NULL_PARAMETER, "org.openanzo.ldap.testPassword"); } LDAPConnection ldapConnection = null; try { if (usessl) { //String anzoHome = context.getProperty("ANZO_HOME"); Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider()); ldapConnection = new LDAPConnection(new LDAPJSSESecureSocketFactory(Utils.getSSLSocketFactory(keystoreFile, keyStorePassword, keystoreType, truststoreFile, trustStorePassword, truststoreType))); } else { ldapConnection = new LDAPConnection(); } try { ldapConnection.connect(host, port); try { ldapConnection.bind(LDAPConnection.LDAP_V3, ldapAdminDN, ldapAdminPassword.getBytes("UTF8")); } catch (LDAPException e) { throw new AnzoException(ExceptionConstants.SERVER.INSTALL_LDAP_ADMIN_BIND, e, ldapAdminDN); } catch (UnsupportedEncodingException e) { throw new AnzoException(ExceptionConstants.IO.ENCODING_ERROR, e); } LDAPSearchResults results = null; try { results = ldapConnection.search(baseDN, LDAPConnection.SCOPE_SUB, userSearch, null, false); } catch (LDAPException e) { throw new AnzoException(ExceptionConstants.SERVER.INSTALL_LDAP_ADMIN_SEARCH, e); } try { if (results.hasMore()) { LDAPEntry entry = results.next(); if (entry != null) { String dn = entry.getDN(); LDAPConnection userConnection = null; try { if (usessl) { //String anzoHome = context.getProperty("ANZO_HOME"); Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider()); userConnection = new LDAPConnection(new LDAPJSSESecureSocketFactory(Utils.getSSLSocketFactory(keystoreFile, keyStorePassword, keystoreType, truststoreFile, trustStorePassword, truststoreType))); } else { userConnection = new LDAPConnection(); } userConnection.connect(host, port); try { userConnection.bind(LDAPConnection.LDAP_V3, dn, testPassword.getBytes("UTF8")); } catch (LDAPException e) { throw new AnzoException(ExceptionConstants.SERVER.INSTALL_LDAP_USER_BIND, e, userId); } catch (UnsupportedEncodingException e) { throw new AnzoException(ExceptionConstants.IO.ENCODING_ERROR, e); } rolesSearchTemplateFormat = (new MessageFormat(rolesSearchTemplateFormat)).format(new String[] { dn }); LDAPSearchResults rolesSearch = userConnection.search(roleBaseDN, LDAPConnection.SCOPE_SUB, rolesSearchTemplateFormat, null, false); try { if (rolesSearch.hasMore()) { String roleDN = Utils.encodeLdapUri("ldap://" + rolesSearch.next().getDN()); org.openanzo.rdf.Constants.valueFactory.createURI(roleDN); } } catch (LDAPReferralException rfe) { log.debug("Error dereferencing referrel", rfe); } } finally { if (userConnection != null) { userConnection.disconnect(); } } } else { throw new AnzoException(ExceptionConstants.SERVER.INSTALL_LDAP_FAILED_TEST_SEARCH); } } else { throw new AnzoException(ExceptionConstants.SERVER.INSTALL_LDAP_FAILED_TEST_SEARCH); } } catch (LDAPReferralException rfe) { log.debug("Error dereferencing referrel", rfe); throw new AnzoException(ExceptionConstants.SERVER.INSTALL_LDAP_FAILED_TEST_SEARCH); } } catch (LDAPException e) { throw new AnzoException(ExceptionConstants.SERVER.INSTALL_LDAP_INVALID_CONFIGURATION, e); } catch (AnzoRuntimeException e) { throw new AnzoException(ExceptionConstants.SERVER.INSTALL_LDAP_INVALID_CONFIGURATION, e); } return true; } finally { if (ldapConnection != null) { try { ldapConnection.disconnect(); } catch (LDAPException e) { log.error(LogUtils.SERVER_INTERNAL_MARKER, "Error disconnecting ldap connection", e); } } } } }; private ServiceRegistration serviceRegistration = null; private boolean useEmbedded = true; private boolean embeddedServerOk = false; private ServiceTracker tracker = null; @Override public String getServicePid() { return SERVICE_PID; } @Override public String[] getDependencies() { return new String[] { ICacheProvider.class.getName() }; } @Override public boolean isInitialized() { return super.isInitialized() && (!useEmbedded || embeddedServerOk); } @Override public String getExtraStatus(boolean html) { StringBuilder sb = new StringBuilder(super.getExtraStatus(html)); if (html) { sb.append("<br/>EmbeddedLdapServerOk:" + embeddedServerOk); } else { sb.append("\n EmbeddedLdapServerOk=" + embeddedServerOk); } return sb.toString(); } @Override public void configurationPropertiesSet(Set<String> changedProperties) throws ConfigurationException { if (configProperties != null) { String host = context.getProperty(LDAPDictionary.KEY_LDAP_HOST); String port = context.getProperty(LDAPDictionary.KEY_LDAP_PORT); if (host != null) { LDAPDictionary.setHost(configProperties, host); } if (port != null) { LDAPDictionary.setPort(configProperties, Integer.valueOf(port)); } Boolean useEmbedded = LDAPAuthDictionary.getUseEmbeddedServer(configProperties); if (useEmbedded != null) { this.useEmbedded = useEmbedded.booleanValue(); } if (useEmbedded && tracker == null) { tracker = new ServiceTracker(context, "org.openanzo.ldap.internal.LdapServer", null) { @Override public Object addingService(ServiceReference reference) { Object service = context.getService(reference); embeddedServerOk = true; if (isInitialized()) { startLocked(); } return service; } @Override public void removedService(ServiceReference reference, Object serviceObject) { context.ungetService(reference); embeddedServerOk = false; stopLocked(false); } }; tracker.open(); } } } @Override public void start() throws AnzoException { boolean enabled = ServicesDictionary.getEnabled(configProperties); if (enabled) { authProvider = new LdapAuthenticationProvider(configProperties); authProvider.start(); String[] topics = new String[] { CachedAuthenticationService.USER_CREDENTIALS_CHANGED_TOPIC, CachedAuthenticationService.USER_ROLES_CHANGED_TOPIC, OSGI.RESET_TOPIC }; Properties props = new Properties(); props.put(EventConstants.EVENT_TOPIC, topics); extenderTracker = new OsgiServiceTracker<IUserRolesExtender>(new IServiceTrackerListener<IUserRolesExtender>() { public void unregisterService(IUserRolesExtender extender) { if (authProvider != null) { authProvider.unregisterRoleExtender(extender); } } public void registerService(IUserRolesExtender extender) { if (authProvider != null) { authProvider.registerRoleExtender(extender); } } public Class<IUserRolesExtender> getComponentType() { return IUserRolesExtender.class; } }, context); extenderTracker.open(); serviceRegistration = context.registerService(new String[] { IAuthenticationService.class.getName(), EventHandler.class.getName() }, new CachedAuthenticationService(authProvider, getDependency(ICacheProvider.class)), props); } else { state = ServiceLifecycleState.NOT_ENABLED; } } @Override public void stop(boolean bundleStopping) { if (extenderTracker != null) { extenderTracker.close(); extenderTracker = null; } if (authProvider != null) { try { authProvider.close(); } catch (AnzoException ae) { log.error(LogUtils.LIFECYCLE_MARKER, "Error stopping ldap authentication provider", ae); } } if (!bundleStopping && serviceRegistration != null) { serviceRegistration.unregister(); serviceRegistration = null; } if (tracker != null) { tracker.close(); } } public ObjectClassDefinition getObjectClassDefinition(String id, String locale) { return classDef; } }