/*
* Copyright 2010 DTO Labs, Inc. (http://dtolabs.com)
*
* 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.
*/
package com.dtolabs.rundeck.jetty.jaas;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.text.DecimalFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.LoginException;
import org.eclipse.jetty.plus.jaas.callback.ObjectCallback;
import org.eclipse.jetty.plus.jaas.spi.AbstractLoginModule;
import org.eclipse.jetty.plus.jaas.spi.UserInfo;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.security.Credential;
/**
*
* A LdapLoginModule for use with JAAS setups
*
* The jvm should be started with the following parameter: <br>
* <br>
* <code>
* -Djava.security.auth.login.config=etc/ldap-loginModule.conf
* </code> <br>
* <br>
* and an example of the ldap-loginModule.conf would be: <br>
* <br>
*
* <pre>
* ldaploginmodule {
* com.dtolabs.rundeck.jetty.jaas.JettyCachingLdapLoginModule required
* debug="true"
* contextFactory="com.sun.jndi.ldap.LdapCtxFactory"
* hostname="ldap.example.com"
* port="389"
* bindDn="cn=Directory Manager"
* bindPassword="directory"
* authenticationMethod="simple"
* forceBindingLogin="false"
* forceBindingLoginUseRootContextForRoles="false"
* userBaseDn="ou=people,dc=alcatel"
* userRdnAttribute="uid"
* userIdAttribute="uid"
* userPasswordAttribute="userPassword"
* userObjectClass="inetOrgPerson"
* roleBaseDn="ou=groups,dc=example,dc=com"
* roleNameAttribute="cn"
* roleMemberAttribute="uniqueMember"
* roleUsernameMemberAttribute="memberUid"
* roleObjectClass="groupOfUniqueNames"
* rolePrefix="rundeck"
* cacheDurationMillis="500"
* reportStatistics="true"
* nestedGroups="false";
* };
* </pre>
*
* @author Jesse McConnell <jesse@codehaus.org>
* @author Frederic Nizery <frederic.nizery@alcatel-lucent.fr>
* @author Trygve Laugstol <trygvis@codehaus.org>
* @author Noah Campbell <noahcampbell@gmail.com>
*/
public class JettyCachingLdapLoginModule extends AbstractLoginModule {
private static final Logger LOG = Log.getLogger(JettyCachingLdapLoginModule.class);
private static final Pattern rolePattern = Pattern.compile("^cn=([^,]+)");
protected final String _roleMemberFilter = "member=*";
/**
* Provider URL
*/
protected String _providerUrl;
/**
* Role prefix to remove from ldap group name.
*/
protected String _rolePrefix = "";
/**
* Duration of storing the user in memory.
*/
protected int _cacheDuration = 0;
/**
* hostname of the ldap server
*/
protected String _hostname;
/**
* port of the ldap server
*/
protected int _port = 389;
/**
* Context.SECURITY_AUTHENTICATION
*/
protected String _authenticationMethod;
/**
* Context.INITIAL_CONTEXT_FACTORY
*/
protected String _contextFactory;
/**
* root DN used to connect to
*/
protected String _bindDn;
/**
* password used to connect to the root ldap context
*/
protected String _bindPassword;
/**
* object class of a user
*/
protected String _userObjectClass = "inetOrgPerson";
/**
* attribute that the principal is located
*/
protected String _userRdnAttribute = "uid";
/**
* attribute that the principal is located
*/
protected String _userIdAttribute = "cn";
/**
* name of the attribute that a users password is stored under
* <p/>
* NOTE: not always accessible, see force binding login
*/
protected String _userPasswordAttribute = "userPassword";
/**
* base DN where users are to be searched from
*/
protected String _userBaseDn;
/**
* base DN where role membership is to be searched from
*/
protected String _roleBaseDn;
/**
* object class of roles
*/
protected String _roleObjectClass = "groupOfUniqueNames";
/**
* name of the attribute that a user DN would be under a role class
*/
protected String _roleMemberAttribute = "uniqueMember";
/**
* name of the attribute that a username would be under a role class
*/
protected String _roleUsernameMemberAttribute=null;
/**
* the name of the attribute that a role would be stored under
*/
protected String _roleNameAttribute = "roleName";
protected boolean _debug;
/**
* if the getUserInfo can pull a password off of the user then password
* comparison is an option for authn, to force binding login checks, set
* this to true
*/
protected boolean _forceBindingLogin = false;
/**
* if _forceFindingLogin is true, and _forceBindingLoginUseRootContextForRoles
* is true, then role memberships are obtained using _rootContext
*/
protected boolean _forceBindingLoginUseRootContextForRoles = false;
protected DirContext _rootContext;
protected boolean _reportStatistics;
/**
* List of supplemental roles provided in config file that get added to
* all users.
*/
private List<String> _supplementalRoles;
protected boolean _nestedGroups;
protected static final ConcurrentHashMap<String, CachedUserInfo> USERINFOCACHE =
new ConcurrentHashMap<String, CachedUserInfo>();
/**
* The number of cache hits for UserInfo objects.
*/
protected static long userInfoCacheHits;
/**
* The number of login attempts for this particular module.
*/
protected static long loginAttempts;
private static ConcurrentHashMap<String, List<String>> roleMemberOfMap;
private static long roleMemberOfMapExpires = 0;
/**
* get the available information about the user
* <p/>
* for this LoginModule, the credential can be null which will result in a
* binding ldap authentication scenario
* <p/>
* roles are also an optional concept if required
*
* @param username
* @return
* @throws Exception
*/
@SuppressWarnings("unchecked")
public UserInfo getUserInfo(String username) throws Exception {
String pwdCredential = getUserCredentials(username);
if (pwdCredential == null) {
return null;
}
pwdCredential = convertCredentialLdapToJetty(pwdCredential);
Credential credential = Credential.getCredential(pwdCredential);
List roles = getUserRoles(_rootContext, username);
return new UserInfo(username, credential, roles);
}
protected String doRFC2254Encoding(String inputString) {
StringBuffer buf = new StringBuffer(inputString.length());
for (int i = 0; i < inputString.length(); i++) {
char c = inputString.charAt(i);
switch (c) {
case '\\':
buf.append("\\5c");
break;
case '*':
buf.append("\\2a");
break;
case '(':
buf.append("\\28");
break;
case ')':
buf.append("\\29");
break;
case '\0':
buf.append("\\00");
break;
default:
buf.append(c);
break;
}
}
return buf.toString();
}
/**
* attempts to get the users credentials from the users context
* <p/>
* NOTE: this is not an user authenticated operation
*
* @param username
* @return
* @throws LoginException
*/
@SuppressWarnings("unchecked")
private String getUserCredentials(String username) throws LoginException {
String ldapCredential = null;
SearchControls ctls = new SearchControls();
ctls.setCountLimit(1);
ctls.setDerefLinkFlag(true);
ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
String filter = "(&(objectClass={0})({1}={2}))";
try {
Object[] filterArguments = { _userObjectClass, _userIdAttribute, username };
NamingEnumeration results = _rootContext.search(_userBaseDn, filter, filterArguments,
ctls);
LOG.debug("Found user?: " + results.hasMoreElements());
if (!results.hasMoreElements()) {
throw new LoginException("User not found.");
}
SearchResult result = findUser(username);
Attributes attributes = result.getAttributes();
Attribute attribute = attributes.get(_userPasswordAttribute);
if (attribute != null) {
try {
byte[] value = (byte[]) attribute.get();
ldapCredential = new String(value);
} catch (NamingException e) {
LOG.debug("no password available under attribute: " + _userPasswordAttribute);
}
}
} catch (NamingException e) {
throw new LoginException("Root context binding failure.");
}
LOG.debug("user cred is: " + ldapCredential);
return ldapCredential;
}
/**
* attempts to get the users roles from the root context
* <p/>
* NOTE: this is not an user authenticated operation
*
* @param dirContext
* @param username
* @return
* @throws LoginException
*/
@SuppressWarnings("unchecked")
private List getUserRoles(DirContext dirContext, String username) throws LoginException,
NamingException {
String userDn = _userRdnAttribute + "=" + username + "," + _userBaseDn;
return getUserRolesByDn(dirContext, userDn, username);
}
@SuppressWarnings("unchecked")
private List getUserRolesByDn(DirContext dirContext, String userDn, String username) throws LoginException,
NamingException {
List<String> roleList = new ArrayList<String>();
if (dirContext == null || _roleBaseDn == null || (_roleMemberAttribute == null
&& _roleUsernameMemberAttribute == null)
|| _roleObjectClass == null) {
LOG.warn("JettyCachingLdapLoginModule: No user roles found: roleBaseDn, roleObjectClass and roleMemberAttribute or roleUsernameMemberAttribute must be specified.");
return roleList;
}
SearchControls ctls = new SearchControls();
ctls.setDerefLinkFlag(true);
ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
String filter = "(&(objectClass={0})({1}={2}))";
final NamingEnumeration results;
if(null!=_roleUsernameMemberAttribute){
Object[] filterArguments = { _roleObjectClass, _roleUsernameMemberAttribute, username };
results = dirContext.search(_roleBaseDn, filter, filterArguments, ctls);
}else{
Object[] filterArguments = { _roleObjectClass, _roleMemberAttribute, userDn };
results = dirContext.search(_roleBaseDn, filter, filterArguments, ctls);
}
while (results.hasMoreElements()) {
SearchResult result = (SearchResult) results.nextElement();
Attributes attributes = result.getAttributes();
if (attributes == null) {
continue;
}
Attribute roleAttribute = attributes.get(_roleNameAttribute);
if (roleAttribute == null) {
continue;
}
NamingEnumeration roles = roleAttribute.getAll();
while (roles.hasMore()) {
if (_rolePrefix != null && !"".equalsIgnoreCase(_rolePrefix)) {
String role = (String) roles.next();
roleList.add(role.replace(_rolePrefix, ""));
} else {
roleList.add((String) roles.next());
}
}
}
if (null != _supplementalRoles) {
for (String supplementalRole : _supplementalRoles) {
if(null!=supplementalRole&& !"".equals(supplementalRole.trim())){
roleList.add(supplementalRole.trim());
}
}
}
if (roleList.size() < 1) {
LOG.warn("JettyCachingLdapLoginModule: User '" + username + "' has no role membership; role query configuration may be incorrect");
}else{
LOG.debug("JettyCachingLdapLoginModule: User '" + username + "' has roles: " + roleList);
}
if(_nestedGroups) {
roleList = getNestedRoles(dirContext, roleList);
}
return roleList;
}
private List<String> getNestedRoles(DirContext dirContext, List<String> roleList) {
HashMap<String, List<String>> roleMemberOfMap = new HashMap<String, List<String>>();
roleMemberOfMap.putAll(getRoleMemberOfMap(dirContext));
List<String> mergedList = mergeRoles(roleList, roleMemberOfMap);
return mergedList;
}
private List<String> mergeRoles(List<String> roles, HashMap<String, List<String>> roleMemberOfMap) {
List<String> newRoles = new ArrayList<String>();
List<String> mergedRoles = new ArrayList<String>();
mergedRoles.addAll(roles);
for(String role : roles) {
if(roleMemberOfMap.containsKey(role)) {
for(String newRole : roleMemberOfMap.get(role)) {
if (!roles.contains(newRole)) {
newRoles.add(newRole);
}
}
roleMemberOfMap.remove(role);
}
}
if(!newRoles.isEmpty()) {
mergedRoles.addAll(mergeRoles(newRoles, roleMemberOfMap));
}
return mergedRoles;
}
private ConcurrentHashMap<String, List<String>> getRoleMemberOfMap(DirContext dirContext) {
if (_cacheDuration == 0 || System.currentTimeMillis() > roleMemberOfMapExpires) { // only worry about caching if there is a cacheDuration set.
roleMemberOfMap = buildRoleMemberOfMap(dirContext);
roleMemberOfMapExpires = System.currentTimeMillis() + _cacheDuration;
}
return roleMemberOfMap;
}
private ConcurrentHashMap<String, List<String>> buildRoleMemberOfMap(DirContext dirContext) {
Object[] filterArguments = { _roleObjectClass };
SearchControls ctls = new SearchControls();
ctls.setDerefLinkFlag(true);
ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
ConcurrentHashMap<String, List<String>> roleMemberOfMap = new ConcurrentHashMap<String, List<String>>();
try {
NamingEnumeration<SearchResult> results = dirContext.search(_roleBaseDn, _roleMemberFilter, ctls);
while (results.hasMoreElements()) {
SearchResult result = results.nextElement();
Attributes attributes = result.getAttributes();
if (attributes == null) {
continue;
}
Attribute roleAttribute = attributes.get(_roleNameAttribute);
Attribute memberAttribute = attributes.get(_roleMemberAttribute);
if (roleAttribute == null || memberAttribute == null) {
continue;
}
NamingEnumeration role = roleAttribute.getAll();
NamingEnumeration members = memberAttribute.getAll();
if(!role.hasMore() || !members.hasMore()) {
continue;
}
String roleName = (String) role.next();
if (_rolePrefix != null && !"".equalsIgnoreCase(_rolePrefix)) {
roleName = roleName.replace(_rolePrefix, "");
}
while(members.hasMore()) {
String member = (String) members.next();
Matcher roleMatcher = rolePattern.matcher(member);
if(!roleMatcher.find()) {
continue;
}
String roleMember = roleMatcher.group(1);
List<String> memberOf;
if(roleMemberOfMap.containsKey(roleMember)) {
memberOf = roleMemberOfMap.get(roleMember);
} else {
memberOf = new ArrayList<String>();
}
memberOf.add(roleName);
roleMemberOfMap.put(roleMember, memberOf);
}
}
} catch (NamingException e) {
e.printStackTrace();
}
return roleMemberOfMap;
}
/**
* since ldap uses a context bind for valid authentication checking, we
* override login()
* <p/>
* if credentials are not available from the users context or if we are
* forcing the binding check then we try a binding authentication check,
* otherwise if we have the users encoded password then we can try
* authentication via that mechanic
*
* @return
* @throws LoginException
*/
public boolean login() throws LoginException {
try {
if (getCallbackHandler() == null) {
throw new LoginException("No callback handler");
}
Callback[] callbacks = configureCallbacks();
getCallbackHandler().handle(callbacks);
String webUserName = ((NameCallback) callbacks[0]).getName();
Object webCredential = ((ObjectCallback) callbacks[1]).getObject();
if (webUserName == null || webCredential == null) {
setAuthenticated(false);
return isAuthenticated();
}
loginAttempts++;
if(_reportStatistics)
{
DecimalFormat percentHit = new DecimalFormat("#.##");
LOG.info("Login attempts: " + loginAttempts + ", Hits: " + userInfoCacheHits +
", Ratio: " + percentHit.format((double)userInfoCacheHits / loginAttempts * 100f) + "%.");
}
if (_forceBindingLogin) {
return bindingLogin(webUserName, webCredential);
}
// This sets read and the credential
UserInfo userInfo = getUserInfo(webUserName);
if (userInfo == null) {
setAuthenticated(false);
return false;
}
setCurrentUser(new JAASUserInfo(userInfo));
if (webCredential instanceof String) {
return credentialLogin(Credential.getCredential((String) webCredential));
}
return credentialLogin(webCredential);
} catch (UnsupportedCallbackException e) {
throw new LoginException("Error obtaining callback information.");
} catch (IOException e) {
if (_debug) {
e.printStackTrace();
}
throw new LoginException("IO Error performing login.");
} catch (Exception e) {
if (_debug) {
e.printStackTrace();
}
throw new LoginException("Error obtaining user info.");
}
}
/**
* password supplied authentication check
*
* @param webCredential
* @return
* @throws LoginException
*/
protected boolean credentialLogin(Object webCredential) throws LoginException {
setAuthenticated(getCurrentUser().checkCredential(webCredential));
return isAuthenticated();
}
/**
* binding authentication check This methode of authentication works only if
* the user branch of the DIT (ldap tree) has an ACI (acces control
* instruction) that allow the access to any user or at least for the user
* that logs in.
*
* @param username
* @param password
* @return
* @throws LoginException
*/
@SuppressWarnings("unchecked")
protected boolean bindingLogin(String username, Object password) throws LoginException,
NamingException {
final String cacheToken = Credential.MD5.digest(username + ":" + password.toString());
if (_cacheDuration > 0) { // only worry about caching if there is a cacheDuration set.
CachedUserInfo cached = USERINFOCACHE.get(cacheToken);
if (cached != null) {
if (System.currentTimeMillis() < cached.expires) {
LOG.debug("Cache Hit for " + username + ".");
userInfoCacheHits++;
setCurrentUser(new JAASUserInfo(cached.userInfo));
setAuthenticated(true);
return true;
} else {
LOG.info("Cache Eviction for " + username + ".");
USERINFOCACHE.remove(cacheToken);
}
} else {
LOG.debug("Cache Miss for " + username + ".");
}
}
SearchResult searchResult = findUser(username);
String userDn = searchResult.getNameInNamespace();
LOG.info("Attempting authentication: " + userDn);
Hashtable environment = getEnvironment();
environment.put(Context.SECURITY_PRINCIPAL, userDn);
environment.put(Context.SECURITY_CREDENTIALS, password);
DirContext dirContext = new InitialDirContext(environment);
// use _rootContext to find roles, if configured to doso
if ( _forceBindingLoginUseRootContextForRoles ) {
dirContext = _rootContext;
LOG.debug("Using _rootContext for role lookup.");
}
List roles = getUserRolesByDn(dirContext, userDn, username);
UserInfo userInfo = new UserInfo(username, null, roles);
if (_cacheDuration > 0) {
USERINFOCACHE.put(cacheToken,
new CachedUserInfo(userInfo,
System.currentTimeMillis() + _cacheDuration));
LOG.debug("Adding " + username + " set to expire: " + System.currentTimeMillis() + _cacheDuration);
}
setCurrentUser(new JAASUserInfo(userInfo));
setAuthenticated(true);
return true;
}
@SuppressWarnings("unchecked")
private SearchResult findUser(String username) throws NamingException, LoginException {
SearchControls ctls = new SearchControls();
ctls.setCountLimit(1);
ctls.setDerefLinkFlag(true);
ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
String filter = "(&(objectClass={0})({1}={2}))";
LOG.debug("Searching for users with filter: \'" + filter + "\'" + " from base dn: "
+ _userBaseDn);
Object[] filterArguments = new Object[] { _userObjectClass, _userIdAttribute, username };
NamingEnumeration results = _rootContext.search(_userBaseDn, filter, filterArguments, ctls);
LOG.debug("Found user?: " + results.hasMoreElements());
if (!results.hasMoreElements()) {
throw new LoginException("User not found.");
}
return (SearchResult) results.nextElement();
}
@SuppressWarnings("unchecked")
public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState,
Map options) {
super.initialize(subject, callbackHandler, sharedState, options);
_hostname = (String) options.get("hostname");
if(options.containsKey("port")) {
_port = Integer.parseInt((String) options.get("port"));
}
_providerUrl = (String) options.get("providerUrl");
_contextFactory = (String) options.get("contextFactory");
_bindDn = (String) options.get("bindDn");
_bindPassword = (String) options.get("bindPassword");
_authenticationMethod = (String) options.get("authenticationMethod");
_userBaseDn = (String) options.get("userBaseDn");
_roleBaseDn = (String) options.get("roleBaseDn");
if (options.containsKey("forceBindingLogin")) {
_forceBindingLogin = Boolean.parseBoolean((String) options.get("forceBindingLogin"));
}
if (options.containsKey("nestedGroups")) {
_nestedGroups = Boolean.parseBoolean((String) options.get("nestedGroups"));
}
if (options.containsKey("forceBindingLoginUseRootContextForRoles")) {
_forceBindingLoginUseRootContextForRoles = Boolean.parseBoolean((String) options.get("forceBindingLoginUseRootContextForRoles"));
}
_userObjectClass = getOption(options, "userObjectClass", _userObjectClass);
_userRdnAttribute = getOption(options, "userRdnAttribute", _userRdnAttribute);
_userIdAttribute = getOption(options, "userIdAttribute", _userIdAttribute);
_userPasswordAttribute = getOption(options, "userPasswordAttribute", _userPasswordAttribute);
_roleObjectClass = getOption(options, "roleObjectClass", _roleObjectClass);
_roleMemberAttribute = getOption(options, "roleMemberAttribute", _roleMemberAttribute);
_roleUsernameMemberAttribute = getOption(options, "roleUsernameMemberAttribute", _roleUsernameMemberAttribute);
_roleNameAttribute = getOption(options, "roleNameAttribute", _roleNameAttribute);
_debug = Boolean.parseBoolean(String.valueOf(getOption(options, "debug", Boolean
.toString(_debug))));
_rolePrefix = (String) options.get("rolePrefix");
_reportStatistics = Boolean.parseBoolean(String.valueOf(getOption(options, "reportStatistics", Boolean
.toString(_reportStatistics))));
Object supplementalRoles = options.get("supplementalRoles");
if (null != supplementalRoles) {
this._supplementalRoles = new ArrayList<String>();
this._supplementalRoles.addAll(Arrays.asList(supplementalRoles.toString().split(", *")));
}
String cacheDurationSetting = (String) options.get("cacheDurationMillis");
if (cacheDurationSetting != null) {
try {
_cacheDuration = Integer.parseInt(cacheDurationSetting);
} catch (NumberFormatException e) {
LOG.warn("Unable to parse cacheDurationMillis to a number: " + cacheDurationSetting,
". Using default: " + _cacheDuration, e);
}
}
try {
_rootContext = new InitialDirContext(getEnvironment());
} catch (NamingException ex) {
throw new IllegalStateException("Unable to establish root context", ex);
}
}
public boolean commit() throws LoginException {
try {
_rootContext.close();
} catch (NamingException e) {
throw new LoginException("error closing root context: " + e.getMessage());
}
return super.commit();
}
public boolean abort() throws LoginException {
try {
_rootContext.close();
} catch (NamingException e) {
throw new LoginException("error closing root context: " + e.getMessage());
}
return super.abort();
}
@SuppressWarnings("unchecked")
private String getOption(Map options, String key, String defaultValue) {
Object value = options.get(key);
if (value == null) {
return defaultValue;
}
return (String) value;
}
/**
* get the context for connection
*
* @return
*/
@SuppressWarnings("unchecked")
public Hashtable getEnvironment() {
Properties env = new Properties();
env.put(Context.INITIAL_CONTEXT_FACTORY, _contextFactory);
String url = null;
if(_providerUrl != null) {
url = _providerUrl;
} else {
if (_hostname != null) {
url = "ldap://" + _hostname + "/";
if (_port != 0) {
url += ":" + _port + "/";
}
LOG.warn("Using hostname and port. Use providerUrl instead: " + url);
}
}
env.put(Context.PROVIDER_URL, url);
if (_authenticationMethod != null) {
env.put(Context.SECURITY_AUTHENTICATION, _authenticationMethod);
}
if (_bindDn != null) {
env.put(Context.SECURITY_PRINCIPAL, _bindDn);
}
if (_bindPassword != null) {
env.put(Context.SECURITY_CREDENTIALS, _bindPassword);
}
// Set the SSLContextFactory to implementation that validates cert subject
if (url != null && url.startsWith("ldaps")) {
try {
URI uri = new URI(url);
HostnameVerifyingSSLSocketFactory.setTargetHost(uri.getHost());
env.put("java.naming.ldap.factory.socket",
"com.dtolabs.rundeck.jetty.jaas.HostnameVerifyingSSLSocketFactory");
}
catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}
return env;
}
private static String convertCredentialLdapToJetty(String encryptedPassword) {
if (encryptedPassword == null) {
return encryptedPassword;
}
if (encryptedPassword.toUpperCase().startsWith("{MD5}")) {
return "MD5:"
+ encryptedPassword.substring("{MD5}".length(), encryptedPassword.length());
}
if (encryptedPassword.toUpperCase().startsWith("{CRYPT}")) {
return "CRYPT:"
+ encryptedPassword.substring("{CRYPT}".length(), encryptedPassword.length());
}
return encryptedPassword;
}
private static final class CachedUserInfo {
public final long expires;
public final UserInfo userInfo;
public CachedUserInfo(UserInfo userInfo, long expires) {
this.userInfo = userInfo;
this.expires = expires;
}
}
}