/* | |
* 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 <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 { | |
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 (> V6.5)</option>\n"+ | |
" <option value=\"jtds:sybase://\" " + (lJdbcProvider.equals("jtds:sybase:") ? "selected=\"selected\"" : "") + ">Sybase (>= V10)</option>\n"+ | |
" <option value=\"mysql://\" " + (lJdbcProvider.equals("mysql:") ? "selected=\"selected\"" : "") + ">MySQL (>= 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 <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 { | |
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) + "=<" + Integer.toString(kmanager.getContents().length) + " certificate(s)></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; | |
} | |
} | |
} |