HSQLDB - 100% Java Database |
|
Corrections, criticisms, and comments will be appreciated. Please email them to blaine <dot> simpson <at> admc <dot> com.
We will work though examples of the different methods of external
authentication supported by HyperSQL version 2.1.0 and later.
I assume familiarity with basic SQL and Java.
I will assume here that you are running Java 6 or later.
If you know your way around Java, you can follow this tutorial using Java 5
by putting Java-1.5-compatible jar files in place.
(2.1.0 final, and later 1.5-compatible
jar is available from the Support page).
When I say the main HyperSQL directory below, I mean the HyperSQL
installation directory that contains the subdirectories
build
,
lib
, etc.
The only other setup required is to define the URLID "mem
" for a
memory-only catalog in the file sqltool.rc
in your home
directory.
If you don't have a sqltool.rc
file in your home directory,
just copy the file
sample/sqltool.rc
from your HyperSQL
distribution to there.
Otherwise, copy or otherwise ensure that you have a "mem
" Urlid
stanza like that in sample/sqltool.rc
.
So that I can concentrate on the target matter with minimal setup, and
without favoring specific IDE's or frameworks, this tutorial works from an
operating system shell.
I assume that you know how to get a command shell prompt and run Java
commands.
To eliminate lengthy explanations of pulldown menus, or the need for dozens
of screen shots,
I am using the command line tool
SqlTool
in preference to DatabaseManager, SquirrelSQL, etc.
If you prefer to use a graphical tool and are bright enough to adjust the
given instructions to your tool, you are welcome to do so.
Each section of this documents builds upon previous sections. Therefore, you are advised to follow the document in the given sequence. If you aren't interested in all sections, then stop wherever you want to-- just don't skip around. In particular, if you are not interested in LDAP authentication, then skip the last sections.
Authentication Functions are SQL/JRT Java methods, as described in the Java Language Routines chapter of the User Guide. The backing Java method must satisfy the signature
public static java.sql.Array authenticate(
String database, String user, String password) throws Exception
The throws may be narrowed to any Exception(s) or none. You may obtain the class containing this method from HyperSQL, from somewhere else, or you may write it yourself. For each catalog that you wish to use the authentication function for, you must run the SQL statement
SET DATABASE AUTHENTICATION FUNCTION EXTERNAL NAME
'CLASSPATH:full.name.of.static.method'
The User Guide explains both the SQL statement and the Java method requirements in this section, but at the time I am writing this, the explanations are wrong in some particulars. I'll update the text here if the User Guide gets corrected.
To save some coding, we'll use a minimalistic auth function method that is in
the HyperSQL distribution to support unit testing.
If your distribution installation does not contain the file
testsuite.jar
, then run
ant -f build/test.xml make.test.suite
from the main HyperSQL directory. (Ant will require that you put a 3.x version of the JUnit jar file where it needs it).
java -cp lib/sqltool.jar:lib/testsuite.jar org.hsqldb.cmdline.SqlTool mem
SET DATABASE AUTHENTICATION FUNCTION EXTERNAL NAME
'CLASSPATH:org.hsqldb.auth.AuthFunctionUtils.changeAuthFn';
test-src/org/hsqldb/auth/AuthFunctionUtils.java
, it returns the
role CHANGE_AUTHORIZATION
, which is required to use the
CONNECT
command.
CONNECT USER sa PASSWORD 'wrong';
ALTER USER x SET LOCAL true
(or false
) to
specify whether registered AUTHENTICATION FUNCTIONs will be applied to the
account.
This ALTER USER command is documented
in this section of the User Guide.
changeAuthFn
method allows access.
CONNECT USER fake PASSWORD 'bogus';
CHANGE_AUTHORIZATION
role, run
SELECT * FROM information_schema.enabled_roles;
COMMIT;
CONNECT USER SA PASSWORD '';
COMMIT
statement is to prevent a complication where the
database engine doesn't know what we want to do with the active
transaction before we switch to the new session.
As explained above, the methods for authentication functions are static Java methods. If your program uses only one catalog, or you want the same authentication for all catalogs, then you are all set. If you want catalog-specific authentication behavior, then you will need to use a different Java static authentication method for each database catalog, or your Java method implementation will have to switch behavior based upon the value of the database parameter.
The value of the database parameter is not a portion of the JDBC URL, but a
strictly 16-character unique catalog identifier that you can determine with
the SQL statement CALL database_name()
and you can change with
the SQL statement SET DATABASE UNIQUE NAME MYDB123456789012
.
Database identifiers are special in that double-quoting will not allow you
to use anything other than digits and capital letters, or to start with a
non-capital-letter.
As per normal database object names, unquoted lower-case letters will be
raised to caps.
The return value of authentication function methods represents a compromise between simple usage and the need to satisfy SQL/JRT specification requirements. If the method throws (a checked or unchecked Exception) instead of returning a value, then access is denied. Otherwise access is permitted as follows.
java.sql.Array
, then access will be
permitted with roles and initial schema set according to the array elements,
and no direct privileges.
If there is a local account of the same name, then the local account settings
will be ignored.
With respect to the individual array elements, if the element is the name of
an existing schema, then it will be interpreted as an initial schema setting;
if the element is the name of an existing role, then it will be added as a
role; otherwise the value will be ignored.
Consequently, a list of values, none of which are a valid role or schema,
will result in the user being logged in with default initial schema and no
roles, regardless of the existence or state of a local account of the same
name.
AuthBeanMultiplexer
is an authentication function method that
serves as a bridge between a static authentication function method, and
AuthFunctionBean
Java beans which are catalog-specific.
AuthBeanMultiplexer
does the work of switching to the bean(s)
registered for the catalog and allows for catalog-specific settings which
will not collide with settings for other catalogs.
It also allows for providing backup authenticators for when some
authentication methods fail due to system problems (such as inability to
reach the primary remote authentication server).
The same exact method,
org.hsqldb.auth.AuthBeanMultiplexer.authenticate
can safely be registered with any number of catalogs in the same JVM.
A primary use case is container-managed HyperSQL database catalogs.
An application server with multiple container-managed HyperSQL databases will
have HyperSQL classes stored in a single container ClassLoader.
If two webapps or enterprise apps happen to use the same authentication class,
they will necessarily share the same state and settings.
AuthBeanMultiplexer
is intended to be have
cross-application-sharable state and settings,
but each catalog gets its own list of authenticators (encapsulated in an
AuthFunctionBean
instance).
See the
API spec for org.hsqldb.auth.AuthFunctionBean
.
The method
org.hsqldb.auth.AuthFunctionBean.authenticate()
is
basically a more simple and controlled version of the direct authentication
functions described in the previous section.
Most importantly, there is no database name parameter, since
AuthBeanMultiplexer will not invoke the method unless it has already
matched the database/catalog name.
While throwing Exception
s still means to not allow access,
AuthFunctionBean.authenticate
differentiates between
Runtime and checked Exceptions.
The former indicates an authentication system failure, so that
AuthBeanMultiplexer will try remaining AuthFunctionBeans for the
catalog (if any).
Checked Exceptions mean that the authenticator has purposefully decided that
the attempt should be rejected, and therefore no other AuthFunctionBeans will
be tried.
Finally, the type returned by AuthFunctionBean.authenticate()
is
the simpler primitive String array, but the elements of the lists are
exactly the same as for a direct auth function method (as described fully in
the previous section).
AuthBeanMultiplexer
provides a singleton JavaBean instance which
can be used as an equivalent alternative to the static methods.
This AuthBeanMultiplexer singleton and the AuthFunctionBeans can therefore be
configured and managed declaratively with generic JavaBean facilities such as
app server web consoles, app server XML configuration files, or app-bundled
Spring bean files.
Be aware that the authentication settings are purposefully not stored in the database itself. It is stored in the database whether or not to use the AuthBeanMultiplexer, but the AuthFunctionBeans to be used, i.e. the settings for AuthBeanMultiplexer itself and its AuthFunctionBeans are set at runtime by Java code, or more typically, by JavaBean settings loaded by your container or framework as described in the previous paragraph. We purposefully consider these to be deployment or runtime application settings, in the same way that JDBC URLs for container-managed data sources are managed as runtime settings by application servers. If your app uses application-managed data sources, then your database backup strategy should make sure to also save your AuthBeanMuliplexer configurations. Note that this design does accommodate application recovery even if the AuthBeanMultiplexer settings are lost, because by default the SA account is a local-auth-only account. Therefore, even if AuthBeanMultiplexer is totally incapacitated, you can log into the database as user SA and disable external authentication until you restore or fix the AuthBeanMultiplexer settings.
HyperSQL Master/Slave Authentication is a situation where one or more slave
databases behave as if they have account records matching those in a master
database.
The master database is a normal HyperSQL catalog which may have been set up
for the dedicated purpose of serving as a master catalog, or that may just be
one of its purposes.
Like all external authentication methods, if the local
information_schema.system_users
record for a given
user_name
has authentication
value of
LOCAL
, then authentication will occur for that user as if no
external authentication had been set up.
All we will do in this section is run and look at the
extAuthWithSpring sample in your HyperSQL distribution.
The portion of extAuthWithSpring that we will examine in this section uses
Spring bean XML files to configure a
HsqldbSlaveAuthBean
for
our application to authenticate to our in-memory application database which
acts as an authentication slave to another in-memory authentication master
database.
From the directory integration/extAuthWithSpring
, run
ant -Dauthentication.mode=HsqldbSlave
The rest of this section will give file paths relative to the
integration/extAuthWithSpring
directory.
Unless you happen to have your environment set up right ahead of time, you
will get an error message with a suggestion of how to set up your environment.
Copy the suggested command for your particular environment and execute it on
your command line shell.
Then re-run the Ant command above.
If the last line of output for the Ant run says
"BUILD SUCCESSFUL
", then the sample program has run successfully.
Now I'll show you what the program did.
The ultimate purpose for the sample is to run the JDBC code in method
doJdbcWork()
in the class JdbcAppClass
.
src/org/hsqldb/sample/JdbcAppClass.java
with an editor, web browser, or text file viewer.
Notice that the source uses generic JDBC.
It is neither HSQLDB-specific nor Spring-specific.
The purpose of the doJdbcWork()
method is to read the table
t1
and verify that the read-in value is 456
.
If successful, it will log the message "Application Success
".
src/org/hsqldb/sample/SpringExtAuth.java
for viewing.
The main method here gets called by Ant after Ant compiles the program.
The main method calls prepMemoryDatabases()
for the obvious
purpose, then loads a Spring Framework context, and finally invokes the
method JdbcAppClass.doJdbcWork
.
The work in prepMemoryDatabases()
is entirely for setting up
this example.
In any real app, the master database will exist ahead-of-time.
An application with a persistent application database will not need to do
any runtime setup of that database, but the AuthBeanMultiplexer
function must be plugged in ahead of time with the SET DATABASE
AUTHENTICATION...
command (a one-time operation for a
persistent database).
An application with an in-memory database will do nothing special other
than plugging in the AuthBeanMultiplexer
each time that the
database is started and populated.
The SET DATABASE AUTHENTICATION...
command is described in
the first section above.
If your application uses Spring, it will already be wired to automatically instantiate a Spring context, and more likely than not, your container will invoke your application work methods. This sample uses Spring manually so that we can set up external authentication declaratively but without the need for a complicated container or application server. You could use an app server's JavaBean management facility to load the settings. You could also combine the two, for example to have JBoss load a Spring bean factory or context at the server level (as opposed to at the application level). Notice that we have Spring load the file beandefs.xml, plus either "ldapbeans.xml" or "slavebeans.xml". Since for this section we are running with argument "HsqldbSlave", we will load "beandefs.xml" and "slavebeans.xml".
resources/beandefs.xml
and
resources/slavebeans.xml
with a good viewer, browser, or editor.
See the
API spec for
org.hsqldb.auth.AuthBeanMultiplexer
and
org.hsqldb.auth.HsqldbSlaveAuthBean
to see what the used
and available methods for these classes do.
Spring developers should immediately see what is going on here, and how
easy it is to change settings.
Non-Spring developers with a basic understanding of JavaBeans should be
able to figure it out if they pay attention to the comments and the
property and bean names.
JaasAuthBean
provides a bridge between authentication for a
HyperSQL catalog and JAAS login module(s).
The critical property of JaasAuthBean is
applicationKey
.
This tells HyperSQL which runtime JAAS configurations to apply.
In the case of the Sun and OpenJDK JVMs, at least, the applicationKey is the
name of the stanza that will be used from the JAAS config file.
The JAAS config file stanza simply lists JAAS login modules and the settings
for each module.
See the
reference guide for details about JAAS.
The goal of this section is to see how to use JaasAuthBean to plug JAAS
login modules into HyperSQL.
We will run a sample program that does just this, and which proves it by
using the JAAS module to access our database catalog and retrieving an
expected value.
The setup is basically an extension to the setup of the previous section.
We have to configure the JaasAuthBean
with AuthBeanMultiplexer
, but we also have to configure the
JAAS login modules that JaasAuthBean
will use.
resources/jaasbeans.xml
.
If you understood the file resources/slavebeans.xml
, this is
entirely analogous but even simpler.
See the HyperSQL
API Spec for JaasAuthBean
to find out about
the available properties.
Note that we specify to use applicationKey value demo
.
That's all the setup needed for the bean.
resources/jaas.cfg
.
(You can see in build.xml
where we specify the location of
the JAAS configuration file with a Java system property).
View
this file.
The syntax for the file is documented at
http://download.oracle.com/javase/6/docs/api/javax/security/auth/login/Configuration.html,
with the significant omission that they don't say that you can use both
"//" and "/*...*/" style comments.
All we are interested in here is the {} block after the application key
"demo
".
Those who have configured PAM authentication will see that the setup is
just like PAM setup.
For our application, the config file just says that success of the
StartCharModule
is required for database access, with the
specified StartCharModule
options.
The option values here tell StartCharModule
to allow access
to anybody supplying a user name starting with "s" and a password starting
with "p".
This will work for us because, as you can see in
resources/beandefs.xml
(which we saw in the previous section),
our app will try to connect to our application database with user name
"straight" and password "pwd".
ant -Dauthentication.mode=JAAS
BUILD SUCCESSFUL
".
Our JAAS configuration said to use JAAS login module
StartCharModule
, but you can specify any login modules which
provide both a JAAS
NameCallback
and
PasswordCallback
, as described in the
API Spec for
JaasAuthBean
.
There are open source and commercial JAAS login modules available on the
Internet, and you can write your own according to the
JAAS Reference Guide.
Sun Java 1.6 comes with several login modules, and I'll say something about
them in the next section.
Sun Java 1.6 comes with several JAAS login modules.
Only one of these modules supports JAAS
NameCallback
and
PasswordCallback
, as required by
JaasAuthBean
.
That module is
com.sun.security.auth.module.LdapLoginModule
.
If you have an LDAP server, you can modify the settings in
resources/jaas.cfg
in the block for application "sunLdap",
according to the comments therein, and run
ant -Dauthentication.mode=JAAS_LDAP
I am not covering this in detail because most users who want to
authenticate using LDAP would be better off using the direct (non-JAAS)
AuthFunctionBean
described in the following sections.
Sun's LdapLoginModule
is not very flexible, supports the
deprecated LDAPS instead of StartTLS, and can only return a single value
that we can use to assign a role or initial schema.
If you do want to use LdapLoginModule
, just work through the
rest of this tutorial, pay attention to the comments in
resources/jaasldapbeans.xml
, and resources/jaas.cfg
,
and you will learn all you need to know to get JaasAuthBean
and LdapLoginModule
working together.
The remainder of this document is only of use if you have access to an LDAP
server.
The class
org.hsqldb.auth.LdapAuthBean
is an implementation of the
org.hsqldb.auth.AuthFunctionBean
interface, exactly like
HsqldbSlaveAuthBean
which was explained in the previous section.
But LdapAuthBean also provides a utility program for playing with and testing
your LDAP server and your LdapAuthBean settings.
The subsequent section will explain how to run the
extAuthWithSpring sample against your own LDAP server.
This section will explain how to set up an LdapAuthBean instance so that
HyperSQL will work with your LDAP server.
java -cp lib/hsqldb.jar org.hsqldb.auth.LdapAuthBean sample/ldap-exerciser.properties myuser mypassword
sample/ldap-exerciser.properties
,
and these won't match any
LDAP server that you have running... besides the fact that you probably
don't have an LDAP record for myuser/mypassword.
ldap-exerciser.properties
file. Something like
mkdir tmp
copy sample/ldap-exerciser.properties tmp/hldap.properties
java -cp lib/hsqldb.jar org.hsqldb.auth.LdapAuthBean tmp/hldap.properties myuser mypassword
rolesSchemaAttribute
or not).
Keep adjusting your properties file and re-running LdapAuthBean until the
program reports the desired results.
If you successfully completed the previous section, then you should now be
very confident that you know all of the values needed by
LdapAuthBean
for your specific LDAP server.
In this section I will once again specify file paths relative to directory
integration/extAuthWithSpring
.
We will run the extWithAuthSpring sample again, but this time we'll tell it
to not run the authentication master database, but to instead access your
LDAP database for authentication decisions.
resources/ldapbeans.xml
with a text or XML
editor.
Set the property values to match those of the properties file that you set
up in the previous section.
The only anomaly is for property file property trustStore
,
which you can ignore right now because we'll cover that in the next step.
integration/extAuthWithSpring
, run
ant -Dauthentication.mode=LDAP
trustStore
in the previous
section. In that case you need to tell Ant the path to your trust store
too, like this.
ant -Dauthentication.mode=LDAP -Dtruststore.path=$HOME/ca/cacert.store
BUILD SUCCESSFUL
".
Generally, to have LdapAuthBean trust an otherwise untrusted LDAP server's
certificate (with StartTLS), you use the standard Java SE mechanism for the
purpose by setting Java system property javax.net.ssl.trustStore
.