blob: 1c259e8a66798c7dcb64d2f505fa09281bc74261 [file] [log] [blame]
/* $Id$ */
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with this
* work for additional information regarding copyright ownership. The ASF
* licenses this file to You 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 org.apache.manifoldcf.authorities.authorities.ldap;
import java.io.*;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.naming.*;
import javax.naming.directory.*;
import javax.naming.ldap.*;
import org.apache.manifoldcf.authorities.interfaces.*;
import org.apache.manifoldcf.authorities.system.ManifoldCF;
import org.apache.manifoldcf.authorities.system.Logging;
import org.apache.manifoldcf.core.interfaces.*;
import org.apache.manifoldcf.connectorcommon.interfaces.*;
import org.apache.manifoldcf.ui.util.Encoder;
import org.apache.manifoldcf.core.common.LDAPSSLSocketFactory;
/**
* This is the Active Directory implementation of the IAuthorityConnector
* interface. Access tokens for this connector are simple SIDs, except for the
* "global deny" token, which is designed to allow the authority to shut off
* access to all authorized documents when the user is unrecognized or the
* domain controller does not respond.
*/
public class LDAPAuthority extends org.apache.manifoldcf.authorities.authorities.BaseAuthorityConnector {
public static final String _rcsid = "@(#)$Id$";
/**
* Session information for all DC's we talk with.
*/
private LdapContext session = null;
private StartTlsResponse tls = null;
private long sessionExpirationTime = -1L;
//private ConfigParams parameters;
private String bindUser;
private String bindPass;
private String serverProtocol;
private String serverName;
private String serverPort;
private String serverBase;
private String userBase;
private String userSearch;
private String groupBase;
private String groupSearch;
private String groupNameAttr;
private boolean groupMemberDN;
private boolean addUserRecord;
private List<String> forcedTokens;
private String userNameAttr;
private String sslKeystoreData;
private IKeystoreManager sslKeystore;
private long responseLifetime = 60000L; //60sec
private int LRUsize = 1000;
/**
* Cache manager.
*/
private ICacheManager cacheManager = null;
/**
* Constructor.
*/
public LDAPAuthority() {
}
/**
* Set thread context.
*/
@Override
public void setThreadContext(IThreadContext tc)
throws ManifoldCFException {
super.setThreadContext(tc);
cacheManager = CacheManagerFactory.make(tc);
}
/**
* Connect. The configuration parameters are included.
*
* @param configParams are the configuration parameters for this connection.
*/
@Override
public void connect(ConfigParams configParams) {
super.connect(configParams);
//parameters = configParams;
// Credentials
bindUser = configParams.getParameter("ldapBindUser");
bindPass = configParams.getObfuscatedParameter("ldapBindPass");
// We get the parameters here, so we can check them in case they are missing
serverProtocol = configParams.getParameter("ldapProtocol");
serverName = configParams.getParameter("ldapServerName");
serverPort = configParams.getParameter("ldapServerPort");
serverBase = configParams.getParameter("ldapServerBase");
sslKeystoreData = configParams.getParameter("sslKeystore");
userBase = configParams.getParameter("ldapUserBase");
userSearch = configParams.getParameter("ldapUserSearch");
groupBase = configParams.getParameter("ldapGroupBase");
groupSearch = configParams.getParameter("ldapGroupSearch");
groupNameAttr = configParams.getParameter("ldapGroupNameAttr");
userNameAttr = configParams.getParameter("ldapUserNameAttr");
groupMemberDN = "1".equals(getParam(configParams, "ldapGroupMemberDn", ""));
addUserRecord = "1".equals(getParam(configParams, "ldapAddUserRecord", ""));
forcedTokens = new ArrayList<String>();
int i = 0;
while (i < configParams.getChildCount()) {
ConfigNode sn = configParams.getChild(i++);
if (sn.getType().equals("access")) {
String token = "" + sn.getAttributeValue("token");
forcedTokens.add(token);
}
}
}
// All methods below this line will ONLY be called if a connect() call succeeded
// on this instance!
/**
* Session setup. Anything that might need to throw an exception should go
* here.
*/
protected LdapContext getSession()
throws ManifoldCFException {
try {
if (session == null) {
if (serverName == null || serverName.length() == 0) {
throw new ManifoldCFException("Server name parameter missing but required");
}
if (serverPort == null || serverPort.length() == 0) {
throw new ManifoldCFException("Server port parameter missing but required");
}
if (serverBase == null) {
throw new ManifoldCFException("Server base parameter missing but required");
}
if (userBase == null) {
throw new ManifoldCFException("User base parameter missing but required");
}
if (userSearch == null || userSearch.length() == 0) {
throw new ManifoldCFException("User search expression missing but required");
}
if (groupBase == null) {
throw new ManifoldCFException("Group base parameter missing but required");
}
if (groupSearch == null || groupSearch.length() == 0) {
throw new ManifoldCFException("Group search expression missing but required");
}
if (groupNameAttr == null || groupNameAttr.length() == 0) {
throw new ManifoldCFException("Group name attribute missing but required");
}
if (userNameAttr == null || userNameAttr.length() == 0) {
throw new ManifoldCFException("User name attribute missing but required");
}
if (sslKeystoreData != null) {
sslKeystore = KeystoreManagerFactory.make("", sslKeystoreData);
} else {
sslKeystore = KeystoreManagerFactory.make("");
}
// Set thread local for keystore stuff
LDAPSSLSocketFactory.setSocketFactoryProducer(sslKeystore);
final String protocolToUse;
final boolean useTls;
if (serverProtocol == null || serverProtocol.length() == 0) {
protocolToUse = "ldap";
useTls = false;
} else {
int plusIndex = serverProtocol.indexOf("+");
if (plusIndex == -1) {
plusIndex = serverProtocol.length();
useTls = false;
} else {
useTls = true;
}
protocolToUse = serverProtocol.substring(0,plusIndex);
}
final Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldap://" + serverName + ":" + serverPort + "/" + serverBase);
if (protocolToUse.equals("ldaps"))
env.put(Context.SECURITY_PROTOCOL, "ssl");
env.put("java.naming.ldap.factory.socket", "org.apache.manifoldcf.core.common.LDAPSSLSocketFactory");
if (bindUser != null && !bindUser.isEmpty()) {
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, bindUser);
env.put(Context.SECURITY_CREDENTIALS, bindPass);
}
session = new InitialLdapContext(env, null);
if (useTls) {
// Start TLS
StartTlsResponse tls = (StartTlsResponse) session.extendedOperation(new StartTlsRequest());
tls.negotiate(sslKeystore.getSecureSocketFactory());
}
} else {
// Set thread local for keystore stuff
LDAPSSLSocketFactory.setSocketFactoryProducer(sslKeystore);
session.reconnect(null);
}
sessionExpirationTime = System.currentTimeMillis() + 300000L;
return session;
} catch (AuthenticationException e) {
session = null;
sessionExpirationTime = -1L;
throw new ManifoldCFException("Authentication error: " + e.getMessage() + ", explanation: " + e.getExplanation(), e);
} catch (CommunicationException e) {
session = null;
sessionExpirationTime = -1L;
throw new ManifoldCFException("Communication error: " + e.getMessage(), e);
} catch (NamingException e) {
session = null;
sessionExpirationTime = -1L;
throw new ManifoldCFException("Naming error: " + e.getMessage(), e);
} catch (InterruptedIOException e) {
session = null;
sessionExpirationTime = -1L;
throw new ManifoldCFException(e.getMessage(), ManifoldCFException.INTERRUPTED);
} catch (IOException e) {
session = null;
sessionExpirationTime = -1L;
throw new ManifoldCFException("IO error: " + e.getMessage(), e);
}
}
/**
* Check connection for sanity.
*/
@Override
public String check()
throws ManifoldCFException {
disconnectSession();
getSession();
// MHL for a real check of all the search etc.
return super.check();
}
/** This method is called to assess whether to count this connector instance should
* actually be counted as being connected.
*@return true if the connector instance is actually connected.
*/
@Override
public boolean isConnected()
{
return session != null;
}
/**
* Poll. The connection should be closed if it has been idle for too long.
*/
@Override
public void poll()
throws ManifoldCFException {
if (session != null && System.currentTimeMillis() > sessionExpirationTime) {
disconnectSession();
}
super.poll();
}
/**
* Disconnect a session.
*/
protected void disconnectSession() {
if (session != null) {
try {
if (tls != null)
tls.close();
session.close();
} catch (NamingException e) {
// Eat this error
} catch (IOException e) {
// Eat this error
}
tls = null;
session = null;
sessionExpirationTime = -1L;
}
}
/**
* Close the connection. Call this before discarding the repository connector.
*/
@Override
public void disconnect()
throws ManifoldCFException {
disconnectSession();
super.disconnect();
// Zero out all the stuff that we want to be sure we don't use again
serverName = null;
serverPort = null;
serverBase = null;
userBase = null;
userSearch = null;
groupBase = null;
groupSearch = null;
groupNameAttr = null;
userNameAttr = null;
forcedTokens = null;
sslKeystoreData = null;
sslKeystore = null;
}
protected String createCacheConnectionString() {
StringBuilder sb = new StringBuilder();
sb.append(serverName).append(":").append(serverPort).append("/").append(serverBase);
return sb.toString();
}
protected String createUserSearchString() {
StringBuilder sb = new StringBuilder();
sb.append(userBase).append("|").append(userSearch).append("|").append(userNameAttr).append("|").append(addUserRecord ? 'Y' : 'N');
return sb.toString();
}
protected String createGroupSearchString() {
StringBuilder sb = new StringBuilder();
sb.append(groupBase).append("|").append(groupSearch).append("|").append(groupNameAttr).append("|").append(groupMemberDN ? 'Y' : 'N');
return sb.toString();
}
/**
* Obtain the access tokens for a given user name.
*
* @param userName is the user name or identifier.
* @return the response tokens (according to the current authority). (Should
* throws an exception only when a condition cannot be properly described
* within the authorization response object.)
*/
@Override
public AuthorizationResponse getAuthorizationResponse(String userName)
throws ManifoldCFException {
getSession();
// Construct a cache description object
ICacheDescription objectDescription = new LdapAuthorizationResponseDescription(userName,
createCacheConnectionString(), createUserSearchString(), createGroupSearchString(), this.responseLifetime, this.LRUsize);
// Enter the cache
ICacheHandle ch = cacheManager.enterCache(new ICacheDescription[]{objectDescription}, null, null);
try {
ICacheCreateHandle createHandle = cacheManager.enterCreateSection(ch);
try {
// Lookup the object
AuthorizationResponse response = (AuthorizationResponse) cacheManager.lookupObject(createHandle, objectDescription);
if (response != null) {
return response;
}
// Create the object.
response = getAuthorizationResponseUncached(userName);
// Save it in the cache
cacheManager.saveObject(createHandle, objectDescription, response);
// And return it...
return response;
} finally {
cacheManager.leaveCreateSection(createHandle);
}
} finally {
cacheManager.leaveCache(ch);
}
}
protected AuthorizationResponse getAuthorizationResponseUncached(String userName)
throws ManifoldCFException {
getSession();
try {
//find user in LDAP tree
SearchResult usrRecord = getUserEntry(session, userName);
if (usrRecord == null) {
return RESPONSE_USERNOTFOUND;
}
ArrayList theGroups = new ArrayList();
theGroups.addAll(forcedTokens);
String usrName = userName.split("@")[0];
if (userNameAttr != null && !"".equals(userNameAttr)) {
if (usrRecord.getAttributes() != null) {
Attribute attr = usrRecord.getAttributes().get(userNameAttr);
if (attr != null) {
usrName = attr.get().toString();
if (addUserRecord) {
NamingEnumeration values = attr.getAll();
while (values.hasMore()) {
theGroups.add(values.next().toString());
}
}
}
}
}
if (groupSearch != null && !groupSearch.isEmpty()) {
//specify the LDAP search filter
String searchFilter = groupSearch.replaceAll("\\{0\\}", escapeLDAPSearchFilter(groupMemberDN ? usrRecord.getNameInNamespace() : usrName));
SearchControls searchCtls = new SearchControls();
searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
String returnedAtts[] = {groupNameAttr};
searchCtls.setReturningAttributes(returnedAtts);
NamingEnumeration answer = session.search(groupBase, searchFilter, searchCtls);
while (answer.hasMoreElements()) {
SearchResult sr = (SearchResult) answer.next();
Attributes attrs = sr.getAttributes();
if (attrs != null) {
NamingEnumeration values = attrs.get(groupNameAttr).getAll();
while (values.hasMore()) {
theGroups.add(values.next().toString());
}
}
}
}
String[] tokens = new String[theGroups.size()];
int k = 0;
while (k < tokens.length) {
tokens[k] = (String) theGroups.get(k);
k++;
}
return new AuthorizationResponse(tokens, AuthorizationResponse.RESPONSE_OK);
} catch (NameNotFoundException e) {
// This means that the user doesn't exist
return RESPONSE_USERNOTFOUND;
} catch (NamingException e) {
// Unreachable
return RESPONSE_UNREACHABLE;
}
}
/**
* Obtain the default access tokens for a given user name.
*
* @param userName is the user name or identifier.
* @return the default response tokens, presuming that the connect method
* fails.
*/
@Override
public AuthorizationResponse getDefaultAuthorizationResponse(String userName) {
// The default response if the getConnection method fails
return RESPONSE_UNREACHABLE;
}
// UI support methods.
//
// These support methods are involved in setting up authority connection configuration information. The configuration methods cannot assume that the
// current authority object is connected. That is why they receive a thread context argument.
/**
* Output the configuration header section. This method is called in the head
* section of the connector's configuration page. Its purpose is to add the
* required tabs to the list, and to output any javascript methods that might
* be needed by the configuration editing HTML.
*
* @param threadContext is the local thread context.
* @param out is the output to which any HTML should be sent.
* @param parameters are the configuration parameters, as they currently
* exist, for this connection being configured.
* @param tabsArray is an array of tab names. Add to this array any tab names
* that are specific to the connector.
*/
@Override
public void outputConfigurationHeader(IThreadContext threadContext, IHTTPOutput out, Locale locale, ConfigParams parameters, List<String> tabsArray)
throws ManifoldCFException, IOException {
tabsArray.add(Messages.getString(locale, "LDAP.LDAP"));
tabsArray.add(Messages.getString(locale, "LDAP.ForcedTokens"));
final Map<String,Object> paramMap = new HashMap<String,Object>();
fillInLDAPTab(paramMap, out, parameters);
fillInForcedTokensTab(paramMap, out, parameters);
Messages.outputResourceWithVelocity(out, locale, "editConfiguration.js", paramMap);
}
/**
* Output the configuration body section. This method is called in the body
* section of the authority connector's configuration page. Its purpose is to
* present the required form elements for editing. The coder can presume that
* the HTML that is output from this configuration will be within appropriate
* <html>, <body>, and <form> tags. The name of the form is "editconnection".
*
* @param threadContext is the local thread context.
* @param out is the output to which any HTML should be sent.
* @param parameters are the configuration parameters, as they currently
* exist, for this connection being configured.
* @param tabName is the current tab name.
*/
@Override
public void outputConfigurationBody(IThreadContext threadContext, IHTTPOutput out, Locale locale, ConfigParams parameters, String tabName)
throws ManifoldCFException, IOException {
final Map<String,Object> paramMap = new HashMap<String,Object>();
paramMap.put("TabName",tabName);
fillInLDAPTab(paramMap, out, parameters);
fillInForcedTokensTab(paramMap, out, parameters);
Messages.outputResourceWithVelocity(out, locale, "editConfiguration_LDAP.html", paramMap);
Messages.outputResourceWithVelocity(out, locale, "editConfiguration_ForcedTokens.html", paramMap);
}
private boolean copyParam(IPostParameters variableContext, ConfigParams parameters, String name) {
String val = variableContext.getParameter(name);
if (val == null) {
return false;
}
parameters.setParameter(name, val);
return true;
}
private void copyParam(IPostParameters variableContext, ConfigParams parameters, String name, String def) {
String val = variableContext.getParameter(name);
if (val == null) {
val = def;
}
parameters.setParameter(name, val);
}
/**
* Process a configuration post. This method is called at the start of the
* authority connector's configuration page, whenever there is a possibility
* that form data for a connection has been posted. Its purpose is to gather
* form information and modify the configuration parameters accordingly. The
* name of the posted form is "editconnection".
*
* @param threadContext is the local thread context.
* @param variableContext is the set of variables available from the post,
* including binary file post information.
* @param parameters are the configuration parameters, as they currently
* exist, for this connection being configured.
* @return null if all is well, or a string error message if there is an error
* that should prevent saving of the connection (and cause a redirection to an
* error page).
*/
@Override
public String processConfigurationPost(IThreadContext threadContext, IPostParameters variableContext, Locale locale, ConfigParams parameters)
throws ManifoldCFException {
copyParam(variableContext, parameters, "ldapProtocol");
copyParam(variableContext, parameters, "ldapServerName");
copyParam(variableContext, parameters, "ldapServerPort");
copyParam(variableContext, parameters, "ldapServerBase");
copyParam(variableContext, parameters, "ldapUserBase");
copyParam(variableContext, parameters, "ldapUserSearch");
copyParam(variableContext, parameters, "ldapUserNameAttr");
copyParam(variableContext, parameters, "ldapGroupBase");
copyParam(variableContext, parameters, "ldapGroupSearch");
copyParam(variableContext, parameters, "ldapGroupNameAttr");
copyParam(variableContext, parameters, "ldapGroupMemberDn", "0"); //checkbox boolean value
copyParam(variableContext, parameters, "ldapAddUserRecord", "0"); //checkbox boolean value
copyParam(variableContext, parameters, "ldapBindUser");
final String bindPass = variableContext.getParameter("ldapBindPass");
if (bindPass != null) {
parameters.setObfuscatedParameter("ldapBindPass", variableContext.mapKeyToPassword(bindPass));
}
final String xc = variableContext.getParameter("tokencount");
if (xc != null) {
// Delete all tokens first
int i = 0;
while (i < parameters.getChildCount()) {
ConfigNode sn = parameters.getChild(i);
if (sn.getType().equals("access")) {
parameters.removeChild(i);
} else {
i++;
}
}
final int accessCount = Integer.parseInt(xc);
i = 0;
while (i < accessCount) {
final String accessDescription = "_" + Integer.toString(i);
final String accessOpName = "accessop" + accessDescription;
final String command = variableContext.getParameter(accessOpName);
if (command != null && command.equals("Delete")) {
// Next row
i++;
continue;
}
// Get the stuff we need
String accessSpec = variableContext.getParameter("spectoken" + accessDescription);
ConfigNode node = new ConfigNode("access");
node.setAttribute("token", accessSpec);
parameters.addChild(parameters.getChildCount(), node);
i++;
}
String op = variableContext.getParameter("accessop");
if (op != null && op.equals("Add")) {
String accessspec = variableContext.getParameter("spectoken");
ConfigNode node = new ConfigNode("access");
node.setAttribute("token", accessspec);
parameters.addChild(parameters.getChildCount(), node);
}
}
String sslKeystoreValue = variableContext.getParameter("sslkeystoredata");
final String sslConfigOp = variableContext.getParameter("sslconfigop");
if (sslConfigOp != null)
{
if (sslConfigOp.equals("Delete"))
{
final String alias = variableContext.getParameter("sslkeystorealias");
final IKeystoreManager mgr;
if (sslKeystoreValue != null)
mgr = KeystoreManagerFactory.make("",sslKeystoreValue);
else
mgr = KeystoreManagerFactory.make("");
mgr.remove(alias);
sslKeystoreValue = mgr.getString();
}
else if (sslConfigOp.equals("Add"))
{
String alias = IDFactory.make(threadContext);
byte[] certificateValue = variableContext.getBinaryBytes("sslcertificate");
final IKeystoreManager mgr;
if (sslKeystoreValue != null)
mgr = KeystoreManagerFactory.make("",sslKeystoreValue);
else
mgr = KeystoreManagerFactory.make("");
java.io.InputStream is = new java.io.ByteArrayInputStream(certificateValue);
String certError = null;
try
{
mgr.importCertificate(alias,is);
}
catch (Throwable e)
{
certError = e.getMessage();
}
finally
{
try
{
is.close();
}
catch (IOException e)
{
// Eat this exception
}
}
if (certError != null)
{
return "Illegal certificate: "+certError;
}
sslKeystoreValue = mgr.getString();
}
}
if (sslKeystoreValue != null)
parameters.setParameter("sslkeystore",sslKeystoreValue);
return null;
}
/**
* View configuration. This method is called in the body section of the
* authority connector's view configuration page. Its purpose is to present
* the connection information to the user. The coder can presume that the HTML
* that is output from this configuration will be within appropriate <html>
* and <body> tags.
*
* @param threadContext is the local thread context.
* @param out is the output to which any HTML should be sent.
* @param parameters are the configuration parameters, as they currently
* exist, for this connection being configured.
*/
@Override
public void viewConfiguration(IThreadContext threadContext, IHTTPOutput out, Locale locale, ConfigParams parameters)
throws ManifoldCFException, IOException {
final Map<String,Object> paramMap = new HashMap<String,Object>();
fillInLDAPTab(paramMap, out, parameters);
fillInForcedTokensTab(paramMap, out, parameters);
Messages.outputResourceWithVelocity(out, locale, "viewConfiguration.html", paramMap);
}
// Protected methods
private static String getParam(final ConfigParams parameters, final String name, final String def) {
String rval = parameters.getParameter(name);
return rval != null ? rval : def;
}
/** Fill in ForcedTokens tab */
protected static void fillInForcedTokensTab(Map<String,Object> velocityContext, IHTTPOutput out, ConfigParams parameters)
{
final List<String> forcedTokenList = new ArrayList<String>();
for (int i = 0; i < parameters.getChildCount(); i++) {
final ConfigNode sn = parameters.getChild(i);
if (sn.getType().equals("access")) {
forcedTokenList.add(sn.getAttributeValue("token"));
}
}
velocityContext.put("FORCEDTOKENS", forcedTokenList);
}
/** Fill in LDAP tab */
protected static void fillInLDAPTab(Map<String,Object> velocityContext, IHTTPOutput out, ConfigParams parameters)
{
velocityContext.put("FSERVERPROTOCOL", getParam(parameters, "ldapProtocol", "ldap"));
velocityContext.put("FSERVERNAME", getParam(parameters, "ldapServerName", ""));
velocityContext.put("FSERVERPORT", getParam(parameters, "ldapServerPort", "389"));
velocityContext.put("FSERVERBASE", getParam(parameters, "ldapServerBase", ""));
String sslKeystoreData = parameters.getParameter("sslkeystore");
if (sslKeystoreData != null)
velocityContext.put("SSLKEYSTOREDATA", sslKeystoreData);
velocityContext.put("FUSERBASE", getParam(parameters, "ldapUserBase", "ou=People"));
velocityContext.put("FUSERSEARCH", getParam(parameters, "ldapUserSearch", "(&(objectClass=inetOrgPerson)(uid={0}))"));
velocityContext.put("FUSERNAMEATTR", getParam(parameters, "ldapUserNameAttr", "uid"));
velocityContext.put("FADDUSERRECORD", getParam(parameters, "ldapAddUserRecord", ""));
velocityContext.put("FGROUPBASE", getParam(parameters, "ldapGroupBase", "ou=Groups"));
velocityContext.put("FGROUPSEARCH", getParam(parameters, "ldapGroupSearch", "(&(objectClass=groupOfNames)(member={0}))"));
velocityContext.put("FGROUPNAMEATTR", getParam(parameters, "ldapGroupNameAttr", "cn"));
velocityContext.put("FGROUPMEMBERDN", getParam(parameters, "ldapGroupMemberDn", ""));
velocityContext.put("FBINDUSER", getParam(parameters, "ldapBindUser", ""));
String fBindPass = parameters.getObfuscatedParameter("ldapBindPass");
if (fBindPass == null)
fBindPass = "";
else
fBindPass = out.mapPasswordToKey(fBindPass);
velocityContext.put("FBINDPASS", fBindPass);
Map<String,String> sslCertificatesMap = null;
String message = null;
try {
final IKeystoreManager localSslKeystore;
if (sslKeystoreData == null)
localSslKeystore = KeystoreManagerFactory.make("");
else
localSslKeystore = KeystoreManagerFactory.make("",sslKeystoreData);
// List the individual certificates in the store, with a delete button for each
final String[] contents = localSslKeystore.getContents();
if (contents.length > 0)
{
sslCertificatesMap = new HashMap<>();
for (final String alias : contents)
{
String description = localSslKeystore.getDescription(alias);
if (description.length() > 128)
description = description.substring(0,125) + "...";
sslCertificatesMap.put(alias, description);
}
}
} catch (ManifoldCFException e) {
message = e.getMessage();
org.apache.manifoldcf.authorities.system.Logging.authorityConnectors.warn(e);
}
if(sslCertificatesMap != null)
velocityContext.put("SSLCERTIFICATESMAP", sslCertificatesMap);
if(message != null)
velocityContext.put("MESSAGE", message);
}
/**
* Obtain the user LDAP record for a given user logon name.
*
* @param ctx is the ldap context to use.
* @param userName (Domain Logon Name) is the user name or identifier.
* @param searchBase (Full Domain Name for the search ie:
* DC=qa-ad-76,DC=metacarta,DC=com)
* @return SearchResult for given domain user logon name. (Should throws an
* exception if user is not found.)
*/
protected SearchResult getUserEntry(LdapContext ctx, String userName)
throws ManifoldCFException {
String searchFilter = userSearch.replaceAll("\\{0\\}", escapeDN(userName.split("@")[0]));
SearchControls searchCtls = new SearchControls();
searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
try {
NamingEnumeration answer = ctx.search(userBase, searchFilter, searchCtls);
if (answer.hasMoreElements()) {
return (SearchResult) answer.next();
}
return null;
} catch (Exception e) {
throw new ManifoldCFException(e.getMessage(), e);
}
}
/**
* LDAP escape a string.
*/
protected static String ldapEscape(String input) {
//Add escape sequence to all commas
StringBuilder sb = new StringBuilder();
int index = 0;
while (true) {
int oldIndex = index;
index = input.indexOf(",", oldIndex);
if (index == -1) {
sb.append(input.substring(oldIndex));
break;
}
sb.append(input.substring(oldIndex, index)).append("\\,");
index++;
}
return sb.toString();
}
public static String escapeDN(String name) {
StringBuilder sb = new StringBuilder(); // If using JDK >= 1.5 consider using StringBuilder
if ((name.length() > 0) && ((name.charAt(0) == ' ') || (name.charAt(0) == '#'))) {
sb.append('\\'); // add the leading backslash if needed
}
for (int i = 0; i < name.length(); i++) {
char curChar = name.charAt(i);
switch (curChar) {
case '\\':
sb.append("\\\\");
break;
case ',':
sb.append("\\,");
break;
case '+':
sb.append("\\+");
break;
case '"':
sb.append("\\\"");
break;
case '<':
sb.append("\\<");
break;
case '>':
sb.append("\\>");
break;
case ';':
sb.append("\\;");
break;
default:
sb.append(curChar);
}
}
if ((name.length() > 1) && (name.charAt(name.length() - 1) == ' ')) {
sb.insert(sb.length() - 1, '\\'); // add the trailing backslash if needed
}
return sb.toString();
}
public static String escapeLDAPSearchFilter(String filter) {
StringBuilder sb = new StringBuilder(); // If using JDK >= 1.5 consider using StringBuilder
for (int i = 0; i < filter.length(); i++) {
char curChar = filter.charAt(i);
switch (curChar) {
case '\\':
sb.append("\\5c");
break;
case '*':
sb.append("\\2a");
break;
case '(':
sb.append("\\28");
break;
case ')':
sb.append("\\29");
break;
case '\u0000':
sb.append("\\00");
break;
default:
sb.append(curChar);
}
}
return sb.toString();
}
protected static StringSet emptyStringSet = new StringSet();
/**
* This is the cache object descriptor for cached access tokens from this
* connector.
*/
protected class LdapAuthorizationResponseDescription extends org.apache.manifoldcf.core.cachemanager.BaseDescription {
/**
* The user name
*/
protected String userName;
/**
* LDAP connection string with server name and base DN
*/
protected String connectionString;
/**
* User search definition
*/
protected String userSearch;
/**
* Group search definition
*/
protected String groupSearch;
/**
* The response lifetime
*/
protected long responseLifetime;
/**
* The expiration time
*/
protected long expirationTime = -1;
/**
* Constructor.
*/
public LdapAuthorizationResponseDescription(String userName, String connectionString, String userSearch, String groupSearch, long responseLifetime, int LRUsize) {
super("LDAPAuthority", LRUsize);
this.userName = userName;
this.connectionString = connectionString;
this.userSearch = userSearch;
this.groupSearch = groupSearch;
this.responseLifetime = responseLifetime;
}
/**
* Return the invalidation keys for this object.
*/
public StringSet getObjectKeys() {
return emptyStringSet;
}
/**
* Get the critical section name, used for synchronizing the creation of the
* object
*/
public String getCriticalSectionName() {
StringBuilder sb = new StringBuilder(getClass().getName());
sb.append("-").append(userName).append("-").append(connectionString);
return sb.toString();
}
/**
* Return the object expiration interval
*/
@Override
public long getObjectExpirationTime(long currentTime) {
if (expirationTime == -1) {
expirationTime = currentTime + responseLifetime;
}
return expirationTime;
}
@Override
public int hashCode() {
return userName.hashCode() + connectionString.hashCode() + userSearch.hashCode() + groupSearch.hashCode();
}
@Override
public boolean equals(Object o) {
if (!(o instanceof LdapAuthorizationResponseDescription)) {
return false;
}
LdapAuthorizationResponseDescription ard = (LdapAuthorizationResponseDescription) o;
if (!ard.userName.equals(userName)) {
return false;
}
if (!ard.connectionString.equals(connectionString)) {
return false;
}
if (!ard.userSearch.equals(userSearch)) {
return false;
}
if (!ard.groupSearch.equals(groupSearch)) {
return false;
}
return true;
}
}
}