/* 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.security.Principal;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;

import org.hsqldb.lib.FrameworkLogger;

Provides authentication and authorization (roles and initial schema) according to JAAS modules configured by the runtime JAAS implementation.

JAAS modules used must have both a NameCallback and a PasswordCallback. This is how we pass the JDBC-provided user name and password to the module.

JAAS setup is Java-implementation-specific. For Sun Java, you set up a JAAS configuration file which resides at $HOME/.java.login.config or at the location that you set with Java system property java.security.auth.login.config.

You can use this bean to manage just access, or also to manage roles or initial schemas. To use for roles or initial schemas, you must set the roleSchemaValuePattern property to distinguish which of the JAAS-module-provided values to use. By default, all JAAS-module-provided Principles will be candidates. If you set property roleSchemaViaCredential to true, then all JAAS-module-provided public Credentials will be candidates instead.

Author:Blaine Simpson (blaine dot simpson at admc dot com)
See Also:
Since:2.5.0
/** * Provides authentication and authorization (roles and initial schema) * according to JAAS modules configured by the runtime JAAS implementation. * <P> * <b>JAAS modules used must have both a NameCallback and a PasswordCallback.</b> * This is how we pass the JDBC-provided user name and password to the module. * <P> * JAAS setup is Java-implementation-specific. * For Sun Java, you set up a JAAS configuration file which resides at * <code>$HOME/.java.login.config</code> or at the location that you set with * Java system property <code>java.security.auth.login.config</code>. * <P> * You can use this bean to manage just access, or also to manage roles or * initial schemas. * To use for roles or initial schemas, you must set the roleSchemaValuePattern * property to distinguish which of the JAAS-module-provided values to use. * By default, all JAAS-module-provided Principles will be candidates. * If you set property roleSchemaViaCredential to true, then all * JAAS-module-provided public Credentials will be candidates instead. * * @see AuthFunctionBean * @see NameCallback * @see PasswordCallback * @author Blaine Simpson (blaine dot simpson at admc dot com) * @since 2.5.0 */
public class JaasAuthBean implements AuthFunctionBean { private static FrameworkLogger logger = FrameworkLogger.getLog(JaasAuthBean.class); private boolean initialized; private String applicationKey; private Pattern roleSchemaValuePattern; private boolean roleSchemaViaCredential; public JaasAuthBean() { // Intentionally empty }
By default, If roleSchemaValuePattern is set, then role and schema values are obtained from principle values; otherwise existing account privileges are used (if any). If roleSchemaViaCredential is set to true and roleSchemaValuePattern is set, then credential values will be used instead.

Do not set roleSchemaViaCredential to true unless roleSchemaValuePattern is set.

Params:
  • roleSchemaViaCredential – boolean
/** * By default, If roleSchemaValuePattern is set, then role and schema * values are obtained from principle values; otherwise existing account * privileges are used (if any). * If roleSchemaViaCredential is set to true and roleSchemaValuePattern is * set, then credential values will be used instead. * <P> * Do not set roleSchemaViaCredential to true unless roleSchemaValuePattern * is set. * * @param roleSchemaViaCredential boolean */
public void setRoleSchemaViaCredential(boolean roleSchemaViaCredential) { this.roleSchemaViaCredential = roleSchemaViaCredential; }
Throws:
  • IllegalStateException – if any required setting has not been set.
/** * @throws IllegalStateException if any required setting has not been set. */
public void init() { if (applicationKey == null) { throw new IllegalStateException( "Required property 'applicationKey' not set"); } if (roleSchemaViaCredential && roleSchemaValuePattern == null) { throw new IllegalStateException( "Properties 'roleSchemaViaCredential' and " + "'roleSchemaValuePattern' are mutually exclusive. " + "If you want JaasAuthBean to manage roles or schemas, " + "you must set property 'roleSchemaValuePattern'."); } initialized = true; }
Set the key into the JAAS runtime configuration. For Sun's JAAS implementation, this is the "application" identifier for a stanza in the JAAS configuration file.
Params:
  • applicationKey – key
/** * Set the key into the JAAS runtime configuration. * * For Sun's JAAS implementation, this is the "application" identifier for * a stanza in the JAAS configuration file. * * @param applicationKey key */
public void setApplicationKey(String applicationKey) { this.applicationKey = applicationKey; }
Assign a pattern to both detect honored values, and optionally to map from a single principal name or public credential string to a single HyperSQL role or schema string. Do not use this method if you are using this JaasAuthBean only to permit or reject access (with roles and schema being determined by pre-existing local HyperSQL accounts). On that case, simple success of the login() method method will allow access as the specified user.

If every principal name or public credentials holds only the String values precisely as HyperSQL needs them, then set the pattern to ".+". For example, if the JAAS module returns principals (or credentials) with values "one", "two", "three", then if you set this pattern to ".+", HyperSQL will attempt to assign initial schema and roles for the values "one", "two", and "three".

These are two distinct and important purposes for the specified Pattern.

  1. Values that do not successfully match the pattern will be ignored. If the pattern does match, then the entire principal or credential value will be used to assign initial schema or role (as long as it is a valid schema name or role name in the local database).
  2. Optionally uses parentheses to specify a single capture group (if you use parentheses to specify more than one matching group, we will only capture for the first). What is captured by this group is exactly the role or schema that HyperSQL will attempt to assign. If no capture parens are given then the Pattern is only used for the acceptance decision, and the JAAS-provided value will be returned verbatim.

N.b. this Pattern will be used for the matches() operation, therefore it must match the entire candidate value strings (this is different than the find operation which does not need to satisfy the entire candidate value).

Example1 :


    cn=([^,]+),ou=dbRole,dc=admc,dc=com
will extract the CN value from matching attribute values.

Example1 :


    cn=[^,]+,ou=dbRole,dc=admc,dc=com
will return the entire cn...com string for matching attribute values.
Params:
  • roleSchemaValuePattern – pattern
See Also:
  • matches.matches()
/** * Assign a pattern to both detect honored values, and optionally * to map from a single principal name or public credential string * to a single HyperSQL role or schema string. * Do not use this method if you are using this JaasAuthBean only to * permit or reject access (with roles and schema being determined by * pre-existing local HyperSQL accounts). * On that case, simple success of the login() method method will allow * access as the specified user. * <P> * If every principal name or public credentials holds only the String * values precisely as HyperSQL needs them, then set the pattern to ".+". * For example, if the JAAS module returns principals (or credentials) with * values "one", "two", "three", then if you set this pattern to ".+", * HyperSQL will attempt to assign initial schema and roles for the values * "one", "two", and "three". * <P> * These are two distinct and important purposes for the specified Pattern. * <OL> * <LI> * Values that do not successfully match the pattern will be ignored. * If the pattern does match, then the entire principal or credential * value will be used to assign initial schema or role (as long as it * is a valid schema name or role name in the local database). * <LI> * Optionally uses parentheses to specify a single capture group * (if you use parentheses to specify more than one matching group, we * will only capture for the first). * What is captured by this group is exactly the role or schema that * HyperSQL will attempt to assign. * If no capture parens are given then the Pattern is only used for the * acceptance decision, and the JAAS-provided value will be returned * verbatim. * </OL> * <P> * N.b. this Pattern will be used for the matches() operation, therefore it * must match the entire candidate value strings (this is different than * the find operation which does not need to satisfy the entire candidate * value). * <P>Example1 :<PRE><CODE> * cn=([^,]+),ou=dbRole,dc=admc,dc=com * </CODE></PRE> * will extract the CN value from matching attribute values. * <P>Example1 :<PRE><CODE> * cn=[^,]+,ou=dbRole,dc=admc,dc=com * </CODE></PRE> * will return the entire <CODE>cn...com</CODE> string for matching * attribute values. * * @see Matcher#matches() * * @param roleSchemaValuePattern pattern */
public void setRoleSchemaValuePattern(Pattern roleSchemaValuePattern) { this.roleSchemaValuePattern = roleSchemaValuePattern; }
String wrapper for method setRoleSchemaValuePattern(Pattern) Use the (x?) Pattern constructs to set options.
Params:
  • patternString – pattern
Throws:
See Also:
  • setRoleSchemaValuePattern(Pattern)
/** * String wrapper for method setRoleSchemaValuePattern(Pattern) * * Use the (x?) Pattern constructs to set options. * * @see #setRoleSchemaValuePattern(Pattern) * @param patternString pattern * @throws java.util.regex.PatternSyntaxException exception */
public void setRoleSchemaValuePatternString(String patternString) { setRoleSchemaValuePattern(Pattern.compile(patternString)); } public static class UPCallbackHandler implements CallbackHandler { private String u; private char[] p; public UPCallbackHandler(String u, String pString) { this.u = u; p = pString.toCharArray(); } public void handle(Callback[] callbacks) throws UnsupportedCallbackException { boolean didSetName = false; boolean didSetPassword = false; for (Callback cb : callbacks) if (cb instanceof NameCallback) { ((NameCallback) cb).setName(u); didSetName = true; } else if (cb instanceof PasswordCallback) { ((PasswordCallback) cb).setPassword(p); didSetPassword = true; } else { throw new UnsupportedCallbackException(cb, "Unsupported Callback type: " + cb.getClass().getName()); } if (!didSetName) throw new IllegalStateException( "Supplied Callbacks does not include a NameCallback"); if (!didSetPassword) throw new IllegalStateException("Supplied Callbacks " + "does not include a PasswordCallback"); } }
See Also:
  • authenticate.authenticate(String, String)
/** * @see AuthFunctionBean#authenticate(String, String) */
public String[] authenticate(String userName, String password) throws DenyException { if (!initialized) { throw new IllegalStateException( "You must invoke the 'init' method to initialize the " + JaasAuthBean.class.getName() + " instance."); } try { LoginContext lc = new LoginContext(applicationKey, new UPCallbackHandler(userName, password)); try { lc.login(); } catch (LoginException le) { // I wish there were a way to distinguish system problems from // purposeful rejections here. :-( logger.finer("JSSE backend denying access: " + le); throw new DenyException(); } try { if (roleSchemaValuePattern == null) { return null; } int i = 0; Matcher m = null; List<String> rsCandidates = new ArrayList<String>(); List<String> rsList = new ArrayList<String>(); Subject s = lc.getSubject(); if (roleSchemaViaCredential) { for (Object cred : s.getPublicCredentials()) { rsCandidates.add(cred.toString()); } } else { for (Principal p : s.getPrincipals()) { rsCandidates.add(p.getName()); } } logger.finer(Integer.toString(rsCandidates.size()) + " candidate " + (roleSchemaViaCredential ? "Credentials" : "Principals")); for (String candid : rsCandidates) { m = roleSchemaValuePattern.matcher(candid); if (m.matches()) { logger.finer(" +" + ++i + ": " + ((m.groupCount() > 0) ? m.group(1) : candid)); rsList.add((m.groupCount() > 0) ? m.group(1) : candid); } else { logger.finer(" -" + ++i + ": " + candid); } } return rsList.toArray(new String[0]); } finally { lc.logout(); } } catch (LoginException le) { logger.severe("System JaasAuthBean failure", le); throw new RuntimeException(le); // JAAS System failure } catch (RuntimeException re) { logger.severe("System JaasAuthBean failure", re); throw re; } } }