| /* |
| * 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 |
| * |
| * 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. |
| * under the License. |
| */ |
| package org.apache.karaf.jaas.modules.ldap; |
| |
| import javax.naming.Context; |
| import javax.naming.NamingException; |
| import javax.net.ssl.SSLSocketFactory; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Hashtable; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.apache.karaf.jaas.config.KeystoreManager; |
| import org.osgi.framework.BundleContext; |
| import org.osgi.framework.FrameworkUtil; |
| import org.osgi.framework.ServiceReference; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| public class LDAPOptions { |
| |
| public static final String CONNECTION_URL = "connection.url"; |
| public static final String CONNECTION_USERNAME = "connection.username"; |
| public static final String CONNECTION_PASSWORD = "connection.password"; |
| public static final String USER_BASE_DN = "user.base.dn"; |
| public static final String USER_FILTER = "user.filter"; |
| public static final String USER_SEARCH_SUBTREE = "user.search.subtree"; |
| public static final String ROLE_BASE_DN = "role.base.dn"; |
| public static final String ROLE_FILTER = "role.filter"; |
| public static final String ROLE_NAME_ATTRIBUTE = "role.name.attribute"; |
| public static final String ROLE_SEARCH_SUBTREE = "role.search.subtree"; |
| public static final String ROLE_MAPPING = "role.mapping"; |
| public static final String AUTHENTICATION = "authentication"; |
| public static final String ALLOW_EMPTY_PASSWORDS = "allowEmptyPasswords"; |
| public static final String INITIAL_CONTEXT_FACTORY = "initial.context.factory"; |
| public static final String CONTEXT_PREFIX = "context."; |
| public static final String SSL = "ssl"; |
| public static final String SSL_PROVIDER = "ssl.provider"; |
| public static final String SSL_PROTOCOL = "ssl.protocol"; |
| public static final String SSL_ALGORITHM = "ssl.algorithm"; |
| public static final String SSL_KEYSTORE = "ssl.keystore"; |
| public static final String SSL_KEYALIAS = "ssl.keyalias"; |
| public static final String SSL_TRUSTSTORE = "ssl.truststore"; |
| public static final String SSL_TIMEOUT = "ssl.timeout"; |
| public static final String DEFAULT_INITIAL_CONTEXT_FACTORY = "com.sun.jndi.ldap.LdapCtxFactory"; |
| public static final String DEFAULT_AUTHENTICATION = "simple"; |
| public static final int DEFAULT_SSL_TIMEOUT = 10; |
| |
| private static Logger LOGGER = LoggerFactory.getLogger(LDAPLoginModule.class); |
| |
| private final Map<String, ?> options; |
| |
| public LDAPOptions(Map<String, ?> options) { |
| this.options = new HashMap<>(options); |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if (this == o) return true; |
| if (o == null || getClass() != o.getClass()) return false; |
| LDAPOptions that = (LDAPOptions) o; |
| return options.equals(that.options); |
| |
| } |
| |
| @Override |
| public int hashCode() { |
| return options.hashCode(); |
| } |
| |
| public String getUserFilter() { |
| return (String) options.get(USER_FILTER); |
| } |
| |
| public String getUserBaseDn() { |
| return (String) options.get(USER_BASE_DN); |
| } |
| |
| public boolean getUserSearchSubtree() { |
| return Boolean.parseBoolean((String) options.get(USER_SEARCH_SUBTREE)); |
| } |
| |
| public String getRoleFilter() { |
| return (String) options.get(ROLE_FILTER); |
| } |
| |
| public String getRoleBaseDn() { |
| return (String) options.get(ROLE_BASE_DN); |
| } |
| |
| public boolean getRoleSearchSubtree() { |
| return Boolean.parseBoolean((String) options.get(ROLE_SEARCH_SUBTREE)); |
| } |
| |
| public String getRoleNameAttribute() { |
| return (String) options.get(ROLE_NAME_ATTRIBUTE); |
| } |
| |
| public Map<String, Set<String>> getRoleMapping() { |
| return parseRoleMapping((String) options.get(ROLE_MAPPING)); |
| } |
| |
| private Map<String, Set<String>> parseRoleMapping(String option) { |
| Map<String, Set<String>> roleMapping = new HashMap<String, Set<String>>(); |
| if (option != null) { |
| LOGGER.debug("Parse role mapping {}", option); |
| String[] mappings = option.split(";"); |
| for (String mapping : mappings) { |
| String[] map = mapping.split("=", 2); |
| String ldapRole = map[0].trim(); |
| String[] karafRoles = map[1].split(","); |
| if (roleMapping.get(ldapRole) == null) { |
| roleMapping.put(ldapRole, new HashSet<String>()); |
| } |
| final Set<String> karafRolesSet = roleMapping.get(ldapRole); |
| for (String karafRole : karafRoles) { |
| karafRolesSet.add(karafRole.trim()); |
| } |
| } |
| } |
| return roleMapping; |
| } |
| |
| public Hashtable<String, Object> getEnv() throws NamingException { |
| final Hashtable<String, Object> env = new Hashtable<>(); |
| for (String key : options.keySet()) { |
| if (key.startsWith(CONTEXT_PREFIX)) { |
| env.put(key.substring(CONTEXT_PREFIX.length()), options.get(key)); |
| } |
| } |
| env.put(Context.INITIAL_CONTEXT_FACTORY, getInitialContextFactory()); |
| env.put(Context.PROVIDER_URL, getConnectionURL()); |
| if (getConnectionUsername() != null && getConnectionUsername().trim().length() > 0) { |
| String auth = getAuthentication(); |
| if (auth == null) { |
| auth = DEFAULT_AUTHENTICATION; |
| } |
| env.put(Context.SECURITY_AUTHENTICATION, auth); |
| env.put(Context.SECURITY_PRINCIPAL, getConnectionUsername()); |
| env.put(Context.SECURITY_CREDENTIALS, getConnectionPassword()); |
| } else if (getAuthentication() != null) { |
| env.put(Context.SECURITY_AUTHENTICATION, getAuthentication()); |
| } |
| if (getSsl()) { |
| setupSsl(env); |
| } |
| return env; |
| } |
| |
| protected void setupSsl(Hashtable<String, Object> env) throws NamingException { |
| BundleContext bundleContext = FrameworkUtil.getBundle(LDAPOptions.class).getBundleContext(); |
| ServiceReference ref = null; |
| try { |
| LOGGER.debug("Setting up SSL"); |
| env.put(Context.SECURITY_PROTOCOL, "ssl"); |
| env.put("java.naming.ldap.factory.socket", ManagedSSLSocketFactory.class.getName()); |
| ref = bundleContext.getServiceReference(KeystoreManager.class.getName()); |
| KeystoreManager manager = (KeystoreManager) bundleContext.getService(ref); |
| SSLSocketFactory factory = manager.createSSLFactory( |
| getSslProvider(), getSslProtocol(), getSslAlgorithm(), getSslKeystore(), |
| getSslKeyAlias(), getSslTrustStore(), getSslTimeout()); |
| ManagedSSLSocketFactory.setSocketFactory(new ManagedSSLSocketFactory(factory)); |
| Thread.currentThread().setContextClassLoader(ManagedSSLSocketFactory.class.getClassLoader()); |
| } catch (Exception e) { |
| throw new NamingException("Unable to setup SSL support for LDAP: " + e.getMessage()); |
| } finally { |
| bundleContext.ungetService(ref); |
| } |
| } |
| |
| public Object getInitialContextFactory() { |
| String initialContextFactory = (String) options.get(INITIAL_CONTEXT_FACTORY); |
| if (initialContextFactory == null) { |
| initialContextFactory = DEFAULT_INITIAL_CONTEXT_FACTORY; |
| } |
| return initialContextFactory; |
| } |
| |
| public String getConnectionURL() { |
| String connectionURL = (String) options.get(CONNECTION_URL); |
| if (connectionURL == null || connectionURL.trim().length() == 0) { |
| LOGGER.error("No LDAP URL specified."); |
| } else if (!connectionURL.startsWith("ldap:") && !connectionURL.startsWith("ldaps:")) { |
| LOGGER.error("Invalid LDAP URL: " + connectionURL); |
| } |
| return connectionURL; |
| } |
| |
| public String getConnectionUsername() { |
| return (String) options.get(CONNECTION_USERNAME); |
| } |
| |
| public String getConnectionPassword() { |
| return (String) options.get(CONNECTION_PASSWORD); |
| } |
| |
| public String getAuthentication() { |
| return (String) options.get(AUTHENTICATION); |
| } |
| |
| public boolean getSsl() { |
| Object val = options.get(SSL); |
| if (val instanceof Boolean) { |
| return (Boolean) val; |
| } else if (val != null) { |
| return Boolean.parseBoolean(val.toString()); |
| } else { |
| return getConnectionURL().startsWith("ldaps:"); |
| } |
| } |
| |
| public String getSslProvider() { |
| return (String) options.get(SSL_PROVIDER); |
| } |
| |
| public String getSslProtocol() { |
| return (String) options.get(SSL_PROTOCOL); |
| } |
| |
| public String getSslAlgorithm() { |
| return (String) options.get(SSL_ALGORITHM); |
| } |
| |
| public String getSslKeystore() { |
| return (String) options.get(SSL_KEYSTORE); |
| } |
| |
| public String getSslKeyAlias() { |
| return (String) options.get(SSL_KEYALIAS); |
| } |
| |
| public String getSslTrustStore() { |
| return (String) options.get(SSL_TRUSTSTORE); |
| } |
| |
| public int getSslTimeout() { |
| Object val = options.get(SSL_TIMEOUT); |
| if (val instanceof Number) { |
| return ((Number) val).intValue(); |
| } else if (val != null) { |
| return Integer.parseInt(val.toString()); |
| } else { |
| return DEFAULT_SSL_TIMEOUT; |
| } |
| } |
| |
| public boolean getAllowEmptyPasswords() { |
| return Boolean.parseBoolean((String) options.get(ALLOW_EMPTY_PASSWORDS)); |
| } |
| } |