org.hsqldb.auth.AuthBeanMultiplexer Maven / Gradle / Ivy
The newest version!
/* Copyright (c) 2001-2014, The HSQL Development Group
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the HSQL Development Group nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.hsqldb.auth;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.hsqldb.jdbc.JDBCArrayBasic;
import org.hsqldb.lib.FrameworkLogger;
import org.hsqldb.types.Type;
/**
* This class provides a method which can be used directly as a HyperSQL static
* Java function method.
* Manages a set of AuthFunctionBean implementations
*
* @author Blaine Simpson (blaine dot simpson at admc dot com)
* @since 2.0.1
*/
public class AuthBeanMultiplexer {
private static FrameworkLogger logger =
FrameworkLogger.getLog(AuthBeanMultiplexer.class);
/**
* This sole constructor is purposefully private, so users or frameworks
* that want to work with instances will be forced to use the singleton
* instead of creating useless extra instance copies.
*/
private AuthBeanMultiplexer() {
// Intentionally empty
}
private static AuthBeanMultiplexer singleton = new AuthBeanMultiplexer();
/**
* @see #setAuthFunctionBeans(List)
*/
private static Map> beans =
new HashMap>();
public static AuthBeanMultiplexer getSingleton() {
return singleton;
}
/**
* Clear the set of AuthFunctionBeans
*/
public void clear() {
AuthBeanMultiplexer.beans.clear();
}
/**
* Primary purpose of this class is to manage this static map.
* From dbNames to ordered-lists-of-AuthFunctionBeans.
* This is not an "adder" function, but a "setter" function, so do not use
* this to add to a partial set, but to assign the entire set.
*
* The given entries are copied, to limit side-effects and concurrency
* issues.
*
*/
public void setAuthFunctionBeans(
Map> authFunctionBeanMap) {
if (AuthBeanMultiplexer.beans.size() > 0)
throw new IllegalStateException(
"Use setAuthFunctionBeans(Map) only when the set is empty");
AuthBeanMultiplexer.beans.putAll(authFunctionBeanMap);
}
protected static String getUniqueNameFor(Connection c) throws SQLException {
Statement st = c.createStatement();
ResultSet rs = null;
try {
rs = st.executeQuery("CALL database_name()");
if (!rs.next()) {
throw new SQLException(
"Engine did not reveal unique database name");
}
return rs.getString(1);
} finally {
if (rs != null) try {
rs.close();
} catch (SQLException se) {
logger.error(
"Failed to close ResultSet for retrieving db name");
}
rs = null; // Encourage GC
try {
st.close();
} catch (SQLException se) {
logger.error(
"Failed to close Statement for retrieving db name");
}
st = null; // Encourage GC
}
}
/**
* Wrapper for setAuthFunctioNBeans(String, List)
*
* @param c An open Connection to the desired database.
* @throws SQLException if failed to obtain unique name from given
* Connection.
*/
public void setAuthFunctionBeans(Connection c,
List authFunctionBeans) throws SQLException {
setAuthFunctionBeans(getUniqueNameFor(c), authFunctionBeans);
}
/**
* This is not an "adder" function, but a "setter" function for the
* specified dbName , so do not use this to add to a database's
* FunctionBeans, but to assign the entire list for that database.
*
* The given entries are copied, to limit side-effects and concurrency
* issues.
*
* Use this method instead of setAuthFunctionBean(String, AuthFunctionBean)
* in order to set up multiple authenticators for a single database for
* redundancy purposes.
*
*
* @see #setAuthFunctionBeans(Map)
* @see #setAuthFunctionBean(String, AuthFunctionBean)
*/
public void setAuthFunctionBeans(String dbName,
List authFunctionBeans) {
if (dbName == null || dbName.length() != 16) {
throw new IllegalArgumentException(
"Database name not exactly 16 characters long: "
+ dbName);
}
List dbsBeans = AuthBeanMultiplexer.beans.get(dbName);
if (dbsBeans == null) {
dbsBeans = new ArrayList();
AuthBeanMultiplexer.beans.put(dbName, dbsBeans);
} else {
if (dbsBeans.size() > 0)
throw new IllegalStateException(
"Use setAuthFunctionBeans(String, List) only when the "
+ "db's AuthFunctionBean list is empty");
}
dbsBeans.addAll(authFunctionBeans);
}
/**
* Exactly the same as setAuthFunctionBeans(String, List) other than taking
* an open Connection to identify the database.
*/
public void setAuthFunctionBean(Connection c,
AuthFunctionBean authFunctionBean) throws SQLException {
setAuthFunctionBeans(getUniqueNameFor(c),
Collections.singletonList(authFunctionBean));
}
/**
* This is not an "adder" function, but a "setter" function for the
* specified dbName , so do not use this to add to a database's
* FunctionBeans, but to assign ths single given AuthFunctionBean as the
* specified database's authenticator.
*
* To set up multiple authenticators for a single database for redundancy
* purposes, use the method setAuthFunctionBeans(String, List) instead.
*
*
* @see #setAuthFunctionBeans(String, List)
*/
public void setAuthFunctionBean(String dbName,
AuthFunctionBean authFunctionBean) {
setAuthFunctionBeans(
dbName, Collections.singletonList(authFunctionBean));
}
/**
* HyperSQL Java Function Method.
*
* Registered AuthFunctionBeans matching the specified database and password
* will be tried in order.
*
* - If the AuthFunctionBean being tried throws a non-runtime Exception,
* then that RuntimeException is passed through (re-thrown), resulting
* in a SQLException for the authenticating application.
*
- If the AuthFunctionBean being tried doesn't throw anything, then
* the return value is passed through (returned) and HyperSQL will
* allow access and set roles according to HyperSQL's authentication
* function contract.
*
- If the AuthFunctionBean being tried throws a RuntimeException, then
* the next AuthFunctionBean in turn will be tried.
* If all matching AuthFunctionBeans throw RuntimeExceptions, then the
* first RuntimeException that was thrown will be passed through
* (re-thrown), resulting in a SQLException for the authenticating
* application.
*
- If there are no AuthFunctionBeans registered for the specified
* dbName, then this method will throw an IllegalArgumentException,
* resulting in a SQLException for the authenticating application.
*
*
* @see "HyperSQL User Guide, System Management chapter, Authentication Settings subsection."
* @throws IllegalArgumentException if no AuthFunctionBean has been set for
* specified dbName.
* @throws RuntimeException if all matching AuthFunctionBeans threw
* RuntimeExceptions. (This indicates that no matching
* AuthFunctionBean functioned properly, not that authentication was
* purposefully denied by any AuthFunctionBean).
* @throws Exception (non-runtime). A matching AuthFunctionBean threw this
* Exception.
* @return Null or java.sql.Array to indicate successful authentication
* according to the contract for HyperSQL authentication functions.
*/
public static java.sql.Array authenticate(
String database, String user, String password)
throws Exception {
/* This method both logs and throws because due to JDBC requirements,
* the Exception messages will not make it to applications.
* Though these messages won't make it to the end user, at least the
* application adminster will have access to problem details. */
if (database == null || database.length() != 16) {
throw new IllegalStateException("Internal problem. "
+ "Database name not exactly 16 characters long: "
+ database);
}
List beanList =
AuthBeanMultiplexer.beans.get(database);
if (beanList == null) {
logger.error("Database '" + database
+ "' has not been set up with "
+ AuthBeanMultiplexer.class.getName());
throw new IllegalArgumentException("Database '" + database
+ "' has not been set up with "
+ AuthBeanMultiplexer.class.getName());
}
Exception firstRTE = null;
String[] beanRet;
for (AuthFunctionBean nextBean : beanList) try {
beanRet = nextBean.authenticate(user, password);
return (beanRet == null)
? null : new JDBCArrayBasic(beanRet, Type.SQL_VARCHAR);
} catch (RuntimeException re) {
if (firstRTE == null) {
firstRTE = re;
}
logger.error("System failure of an AuthFunctionBean: "
+ ((re.getMessage() == null)
? re.toString() : re.getMessage()));
} catch (Exception e) {
throw e;
}
throw firstRTE;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy