All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.hsqldb.auth.AuthBeanMultiplexer Maven / Gradle / Ivy

There is a newer version: 2.7.2
Show newest version
/* Copyright (c) 2001-2019, 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();

    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 {@code 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. *

    *
  1. 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. *
  2. 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. *
  3. 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. *
  4. 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 administer 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 - 2024 Weber Informatics LLC | Privacy Policy