blob: 7d1ff89a5d3a51ef4f9857d026cf25c57e6eed1f [file] [log] [blame]
/*
* Copyright 2012 The Apache Software Foundation.
*
* 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 org.apache.manifoldcf.authorities.authorities.jdbc;
import java.io.IOException;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.manifoldcf.agents.interfaces.ServiceInterruption;
import org.apache.manifoldcf.authorities.authorities.BaseAuthorityConnector;
import org.apache.manifoldcf.authorities.interfaces.AuthorizationResponse;
import org.apache.manifoldcf.core.cachemanager.BaseDescription;
import org.apache.manifoldcf.core.interfaces.BinaryInput;
import org.apache.manifoldcf.core.interfaces.CacheManagerFactory;
import org.apache.manifoldcf.core.interfaces.ConfigParams;
import org.apache.manifoldcf.core.interfaces.ICacheCreateHandle;
import org.apache.manifoldcf.core.interfaces.ICacheDescription;
import org.apache.manifoldcf.core.interfaces.ICacheHandle;
import org.apache.manifoldcf.core.interfaces.ICacheManager;
import org.apache.manifoldcf.core.interfaces.IHTTPOutput;
import org.apache.manifoldcf.connectorcommon.interfaces.IKeystoreManager;
import org.apache.manifoldcf.core.interfaces.IPostParameters;
import org.apache.manifoldcf.core.interfaces.IThreadContext;
import org.apache.manifoldcf.connectorcommon.interfaces.KeystoreManagerFactory;
import org.apache.manifoldcf.core.interfaces.ManifoldCFException;
import org.apache.manifoldcf.core.interfaces.StringSet;
import org.apache.manifoldcf.core.interfaces.TimeMarker;
import org.apache.manifoldcf.core.interfaces.IResultRow;
import org.apache.manifoldcf.jdbc.JDBCConnection;
import org.apache.manifoldcf.jdbc.JDBCConstants;
import org.apache.manifoldcf.jdbc.IDynamicResultSet;
import org.apache.manifoldcf.jdbc.IDynamicResultRow;
import org.apache.manifoldcf.authorities.system.Logging;
/**
*
* @author krycek
*/
public class JDBCAuthority extends BaseAuthorityConnector {
public static final String _rcsid = "@(#)$Id: JDBCAuthority.java $";
protected JDBCConnection connection = null;
protected String jdbcProvider = null;
protected String accessMethod = null;
protected String host = null;
protected String databaseName = null;
protected String rawDriverString = null;
protected String userName = null;
protected String password = null;
protected String idQuery = null;
protected String tokenQuery = null;
private long responseLifetime = 60000L; //60sec
private int LRUsize = 1000;
/**
* Cache manager.
*/
private ICacheManager cacheManager = null;
/**
* 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);
jdbcProvider = configParams.getParameter(JDBCConstants.providerParameter);
accessMethod = configParams.getParameter(JDBCConstants.methodParameter);
host = configParams.getParameter(JDBCConstants.hostParameter);
databaseName = configParams.getParameter(JDBCConstants.databaseNameParameter);
rawDriverString = configParams.getParameter(JDBCConstants.driverStringParameter);
userName = configParams.getParameter(JDBCConstants.databaseUserName);
password = configParams.getObfuscatedParameter(JDBCConstants.databasePassword);
idQuery = configParams.getParameter(JDBCConstants.databaseUserIdQuery);
tokenQuery = configParams.getParameter(JDBCConstants.databaseTokensQuery);
}
/**
* Check status of connection.
*/
@Override
public String check()
throws ManifoldCFException {
try {
getSession();
// Attempt to fetch a connection; if this succeeds we pass
connection.testConnection();
return super.check();
} catch (ServiceInterruption e) {
if (Logging.authorityConnectors.isDebugEnabled()) {
Logging.authorityConnectors.debug("Service interruption in check(): " + e.getMessage(), e);
}
return "Transient error: " + e.getMessage();
}
}
/**
* Close the connection. Call this before discarding the repository connector.
*/
@Override
public void disconnect()
throws ManifoldCFException {
connection = null;
host = null;
jdbcProvider = null;
accessMethod = null;
databaseName = null;
rawDriverString = null;
userName = null;
password = null;
super.disconnect();
}
/**
* Set up a session
*/
protected void getSession()
throws ManifoldCFException, ServiceInterruption {
if (connection == null) {
if (jdbcProvider == null || jdbcProvider.length() == 0) {
throw new ManifoldCFException("Missing parameter '" + JDBCConstants.providerParameter + "'");
}
if ((host == null || host.length() == 0) && (rawDriverString == null || rawDriverString.length() == 0))
throw new ManifoldCFException("Missing parameter '"+JDBCConstants.hostParameter+"' or '"+JDBCConstants.driverStringParameter+"'");
connection = new JDBCConnection(jdbcProvider,(accessMethod==null || accessMethod.equals("name")),host,databaseName,rawDriverString,userName,password);
}
}
private String createCacheConnectionString() {
StringBuilder sb = new StringBuilder();
sb.append(jdbcProvider).append("|")
.append((host==null)?"":host).append("|")
.append((databaseName==null)?"":databaseName).append("|")
.append((rawDriverString==null)?"":rawDriverString).append("|")
.append(userName);
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 {
// Construct a cache description object
ICacheDescription objectDescription = new JdbcAuthorizationResponseDescription(userName, createCacheConnectionString(), idQuery, tokenQuery, 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);
}
}
public AuthorizationResponse getAuthorizationResponseUncached(String userName)
throws ManifoldCFException {
try
{
getSession();
VariableMap vm = new VariableMap();
addConstant(vm, JDBCConstants.idReturnVariable, JDBCConstants.idReturnColumnName);
addVariable(vm, JDBCConstants.userNameVariable, userName);
// Find user id
ArrayList paramList = new ArrayList();
StringBuilder sb = new StringBuilder();
substituteQuery(idQuery, vm, sb, paramList);
IDynamicResultSet idSet;
try {
idSet = connection.executeUncachedQuery(sb.toString(),paramList,-1);
}
catch (ServiceInterruption e)
{
return RESPONSE_UNREACHABLE;
}
catch (ManifoldCFException e)
{
throw e;
}
String uid;
try {
IDynamicResultRow row = idSet.getNextRow();
if (row == null)
return RESPONSE_USERNOTFOUND;
try
{
Object oUid = row.getValue(JDBCConstants.idReturnColumnName);
if (oUid == null)
throw new ManifoldCFException("Bad id query; doesn't return $(IDCOLUMN) column. Try using quotes around $(IDCOLUMN) variable, e.g. \"$(IDCOLUMN)\".");
uid = JDBCConnection.readAsString(oUid);
}
finally
{
row.close();
}
} finally {
idSet.close();
}
if (uid.isEmpty()) {
return RESPONSE_USERNOTFOUND;
}
// now check tokens
vm = new VariableMap();
addConstant(vm, JDBCConstants.tokenReturnVariable, JDBCConstants.tokenReturnColumnName);
addVariable(vm, JDBCConstants.userNameVariable, userName);
addVariable(vm, JDBCConstants.userIDVariable, uid);
sb = new StringBuilder();
paramList = new ArrayList();
substituteQuery(tokenQuery, vm, sb, paramList);
try {
idSet = connection.executeUncachedQuery(sb.toString(),paramList,-1);
}
catch (ServiceInterruption e)
{
return RESPONSE_UNREACHABLE;
}
catch (ManifoldCFException e)
{
throw e;
}
ArrayList<String> tokenArray = new ArrayList<String>();
try {
while (true)
{
IDynamicResultRow row = idSet.getNextRow();
if (row == null)
break;
try
{
Object oToken = row.getValue(JDBCConstants.tokenReturnColumnName);
if (oToken == null)
throw new ManifoldCFException("Bad token query; doesn't return $(TOKENCOLUMN) column. Try using quotes around $(TOKENCOLUMN) variable, e.g. \"$(TOKENCOLUMN)\".");
String token = JDBCConnection.readAsString(oToken);
if (!token.isEmpty()) {
tokenArray.add(token);
}
}
finally
{
row.close();
}
}
} finally {
idSet.close();
}
return new AuthorizationResponse(tokenArray.toArray(new String[0]), AuthorizationResponse.RESPONSE_OK);
}
catch (ServiceInterruption e)
{
Logging.authorityConnectors.warn("JDBCAuthority: Service interruption: "+e.getMessage(),e);
return RESPONSE_UNREACHABLE;
}
}
// UI support methods.
//
// These support methods come in two varieties. The first bunch is involved in setting up connection configuration information. The second bunch
// is involved in presenting and editing document specification information for a job. The two kinds of methods are accordingly treated differently,
// in that the first bunch cannot assume that the current connector object is connected, while the second bunch can. That is why the first bunch
// receives a thread context argument for all UI methods, while the second bunch does not need one (since it has already been applied via the connect()
// method, above).
/**
* 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, "JDBCAuthority.DatabaseType"));
tabsArray.add(Messages.getString(locale, "JDBCAuthority.Server"));
tabsArray.add(Messages.getString(locale, "JDBCAuthority.Credentials"));
tabsArray.add(Messages.getString(locale, "JDBCAuthority.Queries"));
out.print(
"<script type=\"text/javascript\">\n"+
"<!--\n"+
"function checkConfigForSave()\n"+
"{\n"+
" if (editconnection.databasehost.value == \"\" && editconnection.rawjdbcstring.value == \"\")\n"+
" {\n"+
" alert(\"" + Messages.getBodyJavascriptString(locale, "JDBCAuthority.PleaseFillInADatabaseServerName") + "\");\n"+
" SelectTab(\"" + Messages.getBodyJavascriptString(locale, "JDBCAuthority.Server") + "\");\n"+
" editconnection.databasehost.focus();\n"+
" return false;\n"+
" }\n"+
" if (editconnection.databasename.value == \"\" && editconnection.rawjdbcstring.value == \"\")\n"+
" {\n"+
" alert(\"" + Messages.getBodyJavascriptString(locale, "JDBCAuthority.PleaseFillInTheNameOfTheDatabase") + "\");\n"+
" SelectTab(\"" + Messages.getBodyJavascriptString(locale, "JDBCAuthority.Server") + "\");\n"+
" editconnection.databasename.focus();\n"+
" return false;\n"+
" }\n"+
" if (editconnection.username.value == \"\")\n"+
" {\n"+
" alert(\"" + Messages.getBodyJavascriptString(locale, "JDBCAuthority.PleaseSupplyTheDatabaseUsernameForThisConnection") + "\");\n"+
" SelectTab(\"" + Messages.getBodyJavascriptString(locale, "JDBCAuthority.Credentials") + "\");\n"+
" editconnection.username.focus();\n"+
" return false;\n"+
" }\n"+
" return true;\n"+
"}\n"+
"\n"+
"//-->\n"+
"</script>\n"
);
}
/**
* Output the configuration body section. This method is called in the body
* section of the 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 &lt;html&gt;,
* &lt;body&gt;, and &lt;form&gt; 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 {
String lJdbcProvider = parameters.getParameter(JDBCConstants.providerParameter);
if (lJdbcProvider == null) {
lJdbcProvider = "oracle:thin:@";
}
String lAccessMethod = parameters.getParameter(JDBCConstants.methodParameter);
if (lAccessMethod == null)
lAccessMethod = "name";
String lHost = parameters.getParameter(JDBCConstants.hostParameter);
if (lHost == null) {
lHost = "localhost";
}
String lDatabaseName = parameters.getParameter(JDBCConstants.databaseNameParameter);
if (lDatabaseName == null) {
lDatabaseName = "database";
}
String rawJDBCString = parameters.getParameter(JDBCConstants.driverStringParameter);
if (rawJDBCString == null)
rawJDBCString = "";
String databaseUser = parameters.getParameter(JDBCConstants.databaseUserName);
if (databaseUser == null) {
databaseUser = "";
}
String databasePassword = parameters.getObfuscatedParameter(JDBCConstants.databasePassword);
if (databasePassword == null) {
databasePassword = "";
} else {
databasePassword = out.mapPasswordToKey(databasePassword);
}
String lIdQuery = parameters.getParameter(JDBCConstants.databaseUserIdQuery);
if (lIdQuery == null) {
lIdQuery = "SELECT idfield AS $(IDCOLUMN) FROM usertable WHERE login = $(USERNAME)";
}
String lTokenQuery = parameters.getParameter(JDBCConstants.databaseTokensQuery);
if (lTokenQuery == null) {
lTokenQuery = "SELECT groupnamefield AS $(TOKENCOLUMN) FROM grouptable WHERE user_id = $(UID) OR login = $(USERNAME)";
}
// "Database Type" tab
if (tabName.equals(Messages.getString(locale, "JDBCAuthority.DatabaseType"))) {
out.print(
"<table class=\"displaytable\">\n"+
" <tr><td class=\"separator\" colspan=\"2\"><hr/></td></tr>\n"+
" <tr>\n"+
" <td class=\"description\"><nobr>" + Messages.getBodyString(locale, "JDBCAuthority.DatabaseType2") + "</nobr></td><td class=\"value\">\n"+
" <select multiple=\"false\" name=\"databasetype\" size=\"2\">\n"+
" <option value=\"oracle:thin:@\" " + (lJdbcProvider.equals("oracle:thin:@") ? "selected=\"selected\"" : "") + ">Oracle</option>\n"+
" <option value=\"postgresql://\" " + (lJdbcProvider.equals("postgresql:") ? "selected=\"selected\"" : "") + ">Postgres SQL</option>\n"+
" <option value=\"jtds:sqlserver://\" " + (lJdbcProvider.equals("jtds:sqlserver:") ? "selected=\"selected\"" : "") + ">MS SQL Server (&gt; V6.5)</option>\n"+
" <option value=\"jtds:sybase://\" " + (lJdbcProvider.equals("jtds:sybase:") ? "selected=\"selected\"" : "") + ">Sybase (&gt;= V10)</option>\n"+
" <option value=\"mysql://\" " + (lJdbcProvider.equals("mysql:") ? "selected=\"selected\"" : "") + ">MySQL (&gt;= V5)</option>\n"+
" <option value=\"mariadb://\" " + (lJdbcProvider.equals("mariadb:") ? "selected=\"selected\"" : "") + ">MariaDB</option>\n"+
" <option value=\"xbib:csv:\" "+(jdbcProvider.equals("xbib:csv:")?"selected=\"selected\"":"")+">CSV</option>\n"+
" </select>\n"+
" </td>\n"+
" </tr>\n"+
" <tr><td class=\"separator\" colspan=\"2\"><hr/></td></tr>\n"+
" <tr>\n"+
" <td class=\"description\"><nobr>" + Messages.getBodyString(locale,"JDBCAuthority.AccessMethod") + "</nobr></td><td class=\"value\">\n"+
" <select multiple=\"false\" name=\"accessmethod\" size=\"2\">\n"+
" <option value=\"name\" "+(lAccessMethod.equals("name")?"selected=\"selected\"":"")+">"+Messages.getBodyString(locale,"JDBCAuthority.ByName")+"</option>\n"+
" <option value=\"label\" "+(lAccessMethod.equals("label")?"selected=\"selected\"":"")+">"+Messages.getBodyString(locale,"JDBCAuthority.ByLabel")+"</option>\n"+
" </select>\n"+
" </td>\n"+
" </tr>\n"+
"</table>\n");
} else {
out.print(
"<input type=\"hidden\" name=\"databasetype\" value=\"" + lJdbcProvider + "\"/>\n"+
"<input type=\"hidden\" name=\"accessmethod\" value=\""+lAccessMethod+"\"/>\n"
);
}
// "Server" tab
if (tabName.equals(Messages.getString(locale, "JDBCAuthority.Server"))) {
out.print(
"<table class=\"displaytable\">\n"+
" <tr><td class=\"separator\" colspan=\"2\"><hr/></td></tr>\n"+
" <tr>\n"+
" <td class=\"description\"><nobr>" + Messages.getBodyString(locale, "JDBCAuthority.DatabaseHostAndPort") + "</nobr></td><td class=\"value\"><input type=\"text\" size=\"64\" name=\"databasehost\" value=\"" + org.apache.manifoldcf.ui.util.Encoder.attributeEscape(lHost) + "\"/></td>\n"+
" </tr>\n"+
" <tr>\n"+
" <td class=\"description\"><nobr>" + Messages.getBodyString(locale, "JDBCAuthority.DatabaseServiceNameOrInstanceDatabase") + "</nobr></td><td class=\"value\"><input type=\"text\" size=\"32\" name=\"databasename\" value=\"" + org.apache.manifoldcf.ui.util.Encoder.attributeEscape(lDatabaseName) + "\"/></td>\n"+
" </tr>\n"+
" <tr>\n"+
" <td class=\"description\"><nobr>" + Messages.getBodyString(locale,"JDBCAuthority.RawDatabaseConnectString") + "</nobr></td><td class=\"value\"><input type=\"text\" size=\"80\" name=\"rawjdbcstring\" value=\""+org.apache.manifoldcf.ui.util.Encoder.attributeEscape(rawJDBCString)+"\"/></td>\n"+
" </tr>\n"+
"</table>\n"
);
} else {
out.print(
"<input type=\"hidden\" name=\"databasehost\" value=\"" + org.apache.manifoldcf.ui.util.Encoder.attributeEscape(lHost) + "\"/>\n"+
"<input type=\"hidden\" name=\"databasename\" value=\"" + org.apache.manifoldcf.ui.util.Encoder.attributeEscape(lDatabaseName) + "\"/>\n"+
"<input type=\"hidden\" name=\"rawjdbcstring\" value=\""+org.apache.manifoldcf.ui.util.Encoder.attributeEscape(rawJDBCString)+"\"/>\n"
);
}
// "Credentials" tab
if (tabName.equals(Messages.getString(locale, "JDBCAuthority.Credentials"))) {
out.print(
"<table class=\"displaytable\">\n"
+ " <tr><td class=\"separator\" colspan=\"2\"><hr/></td></tr>\n"
+ " <tr>\n"
+ " <td class=\"description\"><nobr>" + Messages.getBodyString(locale, "JDBCAuthority.UserName") + "</nobr></td><td class=\"value\"><input type=\"text\" size=\"32\" name=\"username\" value=\"" + org.apache.manifoldcf.ui.util.Encoder.attributeEscape(databaseUser) + "\"/></td>\n"
+ " </tr>\n"
+ " <tr>\n"
+ " <td class=\"description\"><nobr>" + Messages.getBodyString(locale, "JDBCAuthority.Password") + "</nobr></td><td class=\"value\"><input type=\"password\" size=\"32\" name=\"password\" value=\"" + org.apache.manifoldcf.ui.util.Encoder.attributeEscape(databasePassword) + "\"/></td>\n"
+ " </tr>\n"
+ "</table>\n");
} else {
out.print(
"<input type=\"hidden\" name=\"username\" value=\"" + org.apache.manifoldcf.ui.util.Encoder.attributeEscape(databaseUser) + "\"/>\n"
+ "<input type=\"hidden\" name=\"password\" value=\"" + org.apache.manifoldcf.ui.util.Encoder.attributeEscape(databasePassword) + "\"/>\n");
}
if (tabName.equals(Messages.getString(locale, "JDBCAuthority.Queries"))) {
out.print(
"<table class=\"displaytable\">\n"
+ " <tr><td class=\"separator\" colspan=\"2\"><hr/></td></tr>\n"
+ " <tr>\n"
+ " <td class=\"description\"><nobr>" + Messages.getBodyString(locale, "JDBCAuthority.UserIdQuery") + "</nobr><br/><nobr>" + Messages.getBodyString(locale, "JDBCAuthority.returnUserIdOrEmptyResultset") + "</nobr></td>\n"
+ " <td class=\"value\"><textarea name=\"idquery\" cols=\"64\" rows=\"6\">" + org.apache.manifoldcf.ui.util.Encoder.bodyEscape(lIdQuery) + "</textarea></td>\n"
+ " </tr>\n"
+ " <tr>\n"
+ " <td class=\"description\"><nobr>" + Messages.getBodyString(locale, "JDBCAuthority.TokenQuery") + "</nobr><br/><nobr>" + Messages.getBodyString(locale, "JDBCAuthority.returnTokensForUser") + "</nobr></td>\n"
+ " <td class=\"value\"><textarea name=\"tokenquery\" cols=\"64\" rows=\"6\">" + org.apache.manifoldcf.ui.util.Encoder.bodyEscape(lTokenQuery) + "</textarea></td>\n"
+ " </tr>\n"
+ "</table>\n");
} else {
out.print(
"<input type=\"hidden\" name=\"idquery\" value=\"" + org.apache.manifoldcf.ui.util.Encoder.attributeEscape(lIdQuery) + "\"/>\n"
+ "<input type=\"hidden\" name=\"tokenquery\" value=\"" + org.apache.manifoldcf.ui.util.Encoder.attributeEscape(lTokenQuery) + "\"/>\n");
}
}
/**
* Process a configuration post. This method is called at the start of the
* 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 {
String type = variableContext.getParameter("databasetype");
if (type != null) {
parameters.setParameter(JDBCConstants.providerParameter, type);
}
String accessMethod = variableContext.getParameter("accessmethod");
if (accessMethod != null)
parameters.setParameter(JDBCConstants.methodParameter,accessMethod);
String lHost = variableContext.getParameter("databasehost");
if (lHost != null) {
parameters.setParameter(JDBCConstants.hostParameter, lHost);
}
String lDatabaseName = variableContext.getParameter("databasename");
if (lDatabaseName != null) {
parameters.setParameter(JDBCConstants.databaseNameParameter, lDatabaseName);
}
String rawJDBCString = variableContext.getParameter("rawjdbcstring");
if (rawJDBCString != null)
parameters.setParameter(JDBCConstants.driverStringParameter,rawJDBCString);
String lUserName = variableContext.getParameter("username");
if (lUserName != null) {
parameters.setParameter(JDBCConstants.databaseUserName, lUserName);
}
String lPassword = variableContext.getParameter("password");
if (lPassword != null) {
parameters.setObfuscatedParameter(JDBCConstants.databasePassword, variableContext.mapKeyToPassword(lPassword));
}
String lIdQuery = variableContext.getParameter("idquery");
if (lIdQuery != null) {
parameters.setParameter(JDBCConstants.databaseUserIdQuery, lIdQuery);
}
String lTokenQuery = variableContext.getParameter("tokenquery");
if (lTokenQuery != null) {
parameters.setParameter(JDBCConstants.databaseTokensQuery, lTokenQuery);
}
return null;
}
/**
* View configuration. This method is called in the body section of the
* 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 &lt;html&gt;
* and &lt;body&gt; 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 {
out.print(
"<table class=\"displaytable\">\n"
+ " <tr>\n"
+ " <td class=\"description\" colspan=\"1\"><nobr>" + Messages.getBodyString(locale, "JDBCAuthority.Parameters") + "</nobr></td>\n"
+ " <td class=\"value\" colspan=\"3\">\n");
Iterator iter = parameters.listParameters();
while (iter.hasNext()) {
String param = (String) iter.next();
String value = parameters.getParameter(param);
if (param.length() >= "password".length() && param.substring(param.length() - "password".length()).equalsIgnoreCase("password")) {
out.print(
" <nobr>" + org.apache.manifoldcf.ui.util.Encoder.bodyEscape(param) + "=********</nobr><br/>\n");
} else if (param.length() >= "keystore".length() && param.substring(param.length() - "keystore".length()).equalsIgnoreCase("keystore")) {
IKeystoreManager kmanager = KeystoreManagerFactory.make("", value);
out.print(
" <nobr>" + org.apache.manifoldcf.ui.util.Encoder.bodyEscape(param) + "=&lt;" + Integer.toString(kmanager.getContents().length) + " certificate(s)&gt;</nobr><br/>\n");
} else {
out.print(
" <nobr>" + org.apache.manifoldcf.ui.util.Encoder.bodyEscape(param) + "=" + org.apache.manifoldcf.ui.util.Encoder.bodyEscape(value) + "</nobr><br/>\n");
}
}
out.print(
" </td>\n"
+ " </tr>\n"
+ "</table>\n");
}
/**
* Given a query, and a parameter map, substitute it. Each variable
* substitutes the string, and it also substitutes zero or more query
* parameters.
*/
protected static void substituteQuery(String inputString, VariableMap inputMap, StringBuilder outputQuery, ArrayList outputParams)
throws ManifoldCFException {
// We are looking for strings that look like this: $(something)
// Right at the moment we don't care even about quotes, so we just want to look for $(.
int startIndex = 0;
while (true) {
int nextIndex = inputString.indexOf("$(", startIndex);
if (nextIndex == -1) {
outputQuery.append(inputString.substring(startIndex));
break;
}
int endIndex = inputString.indexOf(")", nextIndex);
if (endIndex == -1) {
outputQuery.append(inputString.substring(startIndex));
break;
}
String variableName = inputString.substring(nextIndex + 2, endIndex);
VariableMapItem item = inputMap.getVariable(variableName);
if (item == null) {
throw new ManifoldCFException("No such substitution variable: $(" + variableName + ")");
}
outputQuery.append(inputString.substring(startIndex, nextIndex));
outputQuery.append(item.getValue());
ArrayList inputParams = item.getParameters();
if (inputParams != null) {
int i = 0;
while (i < inputParams.size()) {
Object x = inputParams.get(i++);
outputParams.add(x);
}
}
startIndex = endIndex + 1;
}
}
/**
* Add string query variables
*/
protected static void addVariable(VariableMap map, String varName, String variable) {
ArrayList params = new ArrayList();
params.add(variable);
map.addVariable(varName, "?", params);
}
/**
* Add string query constants
*/
protected static void addConstant(VariableMap map, String varName, String value) {
map.addVariable(varName, value, null);
}
// pass params to preparedStatement
protected static void loadPS(PreparedStatement ps, ArrayList data)
throws java.sql.SQLException, ManifoldCFException {
if (data != null) {
for (int i = 0; i < data.size(); i++) {
// If the input type is a string, then set it as such.
// Otherwise, if it's an input stream, we make a blob out of it.
Object x = data.get(i);
if (x instanceof String) {
String value = (String) x;
// letting database do lame conversion!
ps.setString(i + 1, value);
}
if (x instanceof BinaryInput) {
BinaryInput value = (BinaryInput) x;
// System.out.println("Blob length on write = "+Long.toString(value.getLength()));
// The oracle driver does a binary conversion to base 64 when writing data
// into a clob column using a binary stream operator. Since at this
// point there is no way to distinguish the two, and since our tests use CLOB,
// this code doesn't work for them.
// So, for now, use the ascii stream method.
//ps.setBinaryStream(i+1,value.getStream(),(int)value.getLength());
ps.setAsciiStream(i + 1, value.getStream(), (int) value.getLength());
}
if (x instanceof java.util.Date) {
ps.setDate(i + 1, new java.sql.Date(((java.util.Date) x).getTime()));
}
if (x instanceof Long) {
ps.setLong(i + 1, ((Long) x).longValue());
}
if (x instanceof TimeMarker) {
ps.setTimestamp(i + 1, new java.sql.Timestamp(((Long) x).longValue()));
}
if (x instanceof Double) {
ps.setDouble(i + 1, ((Double) x).doubleValue());
}
if (x instanceof Integer) {
ps.setInt(i + 1, ((Integer) x).intValue());
}
if (x instanceof Float) {
ps.setFloat(i + 1, ((Float) x).floatValue());
}
}
}
}
/**
* Variable map entry.
*/
protected static class VariableMapItem {
protected String value;
protected ArrayList params;
/**
* Constructor.
*/
public VariableMapItem(String value, ArrayList params) {
this.value = value;
this.params = params;
}
/**
* Get value.
*/
public String getValue() {
return value;
}
/**
* Get parameters.
*/
public ArrayList getParameters() {
return params;
}
}
/**
* Variable map.
*/
protected static class VariableMap {
protected Map variableMap = new HashMap();
/**
* Constructor
*/
public VariableMap() {
}
/**
* Add a variable map entry
*/
public void addVariable(String variableName, String value, ArrayList parameters) {
VariableMapItem e = new VariableMapItem(value, parameters);
variableMap.put(variableName, e);
}
/**
* Get a variable map entry
*/
public VariableMapItem getVariable(String variableName) {
return (VariableMapItem) variableMap.get(variableName);
}
}
protected static StringSet emptyStringSet = new StringSet();
/**
* This is the cache object descriptor for cached access tokens from this
* connector.
*/
protected class JdbcAuthorizationResponseDescription extends BaseDescription {
/**
* The user name
*/
protected final String userName;
/**
* LDAP connection string with server name and base DN
*/
protected final String connectionString;
/** The user query. */
protected final String userQuery;
/** The token query. */
protected final String tokenQuery;
/**
* The response lifetime
*/
protected final long responseLifetime;
/**
* The expiration time
*/
protected long expirationTime = -1;
/**
* Constructor.
*/
public JdbcAuthorizationResponseDescription(String userName, String connectionString, String userQuery, String tokenQuery, long responseLifetime, int LRUsize) {
super("JDBCAuthority", LRUsize);
this.userName = userName;
this.connectionString = connectionString;
this.userQuery = userQuery;
this.tokenQuery = tokenQuery;
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).append("-")
.append(userQuery).append("-").append(tokenQuery);
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() +
userQuery.hashCode() + tokenQuery.hashCode();
}
@Override
public boolean equals(Object o) {
if (!(o instanceof JdbcAuthorizationResponseDescription)) {
return false;
}
JdbcAuthorizationResponseDescription ard = (JdbcAuthorizationResponseDescription) o;
if (!ard.userName.equals(userName)) {
return false;
}
if (!ard.connectionString.equals(connectionString)) {
return false;
}
if (!ard.userQuery.equals(userQuery)) {
return false;
}
if (!ard.tokenQuery.equals(tokenQuery)) {
return false;
}
return true;
}
}
}