Oracle NoSQL Database can be installed such that your client code does not have to authenticate to the store. (For the sake of clarity, most of the examples in this book do not perform authentication.) However, if you want your store to operate in a secure manner, you can require authentication. Note that doing so will result in a performance cost due to the overhead of using SSL and authentication. While best practice is for a production store to require authentication over SSL, some sites that are performance sensitive may want to forgo that level of security.
Authentication involves sending username/password credentials to the store at the time a store handle is acquired. A store that is configured to support authentication is automatically configured to communicate with clients using SSL in order to ensure privacy of the authentication and other sensitive information. When SSL is used, SSL certificates need to be installed on the machines where your client code runs in order to validate that the store that is being accessed is trustworthy.
Configuring a store for authentication is described in the Oracle NoSQL Database Security Guide.
If you are using a secure store, then all communications between your client code and the store is transported over SSL, including your authentication credentials. You must therefore configure your client code to use SSL. To do this, you identify where the SSL certificate data is, and you also separately indicate that the SSL transport is to be used.
When an Oracle NoSQL Database store is configured to use the SSL
transport, a series of security files are generated using
a security configuration tool. One of these files is the
client.trust
file, which must be copied
to any machine running Oracle NoSQL Database client code.
For information on using the security configuration tool, see the Oracle NoSQL Database Security Guide.
Your code must be told where the client.trust
file can be found because it contains the certificates
necessary for your client to establish an SSL connection
with the store. You indicate where this file is
physically located on your machine using the
oracle.kv.ssl.trustStore
property.
There are two ways to set this property:
Identify the location of the trust store by using
a Properties
object
to set the oracle.kv.ssl.trustStore
property. You then use
KVStoreConfig.setSecurityProperties()
to pass the Properties
object to your KVStore
handle.
When you use this method, you use
KVSecurityConstants.SSL_TRUSTSTORE_FILE_PROPERTY
as the property name.
Use the oracle.kv.security
property to refer to a properties file, such as
the client.trust
file. In that
file, set the
oracle.kv.ssl.trustStore
property.
In addition to identifying the location of the
client.trust
file, you must also tell
your client code to use the SSL transport. You
do this by setting the oracle.kv.transport
property in one of two ways:
Identify the location of the trust store by using
a Properties
object
to set the oracle.kv.transport
property. You then use
KVStoreConfig.setSecurityProperties()
to pass the Properties
object to your KVStore
handle.
When you use this method, you use
KVSecurityConstants.TRANSPORT_PROPERTY
as the property name, and
KVSecurityConstants.SSL_TRANSPORT_NAME
as the property value.
Use the oracle.kv.security
property to refer to a properties file, such as
the client.trust
file. In that
file, set the
oracle.kv.transport
property.
You authenticate to the store by specifying a
LoginCredentials
implementation instance to
KVStoreFactory.getStore()
. Oracle NoSQL Database
provides the PasswordCredentials
class as a
LoginCredentials
implementation.
If your store requires SSL to be used as the transport, configure
that prior to performing the authentication. (See the
previous section for details.)
Your code should be prepared to handle a failed
authentication attempt.
KVStoreFactory.getStore()
will throw
AuthenticationFailure
in the event of
a failed authentication attempt. You can catch that exception
and handle the problem there.
The following is a simple example of obtaining a store handle for a secured store. The SSL transport is used in this example.
import java.util.Properties; import oracle.kv.AuthenticationFailure; import oracle.kv.PasswordCredentials; import oracle.kv.KVSecurityConstants; import oracle.kv.KVStoreConfig; import oracle.kv.KVStoreFactory; KVStore store = null; try { /* * storeName, hostName, port, username, and password are all * strings that would come from somewhere else in your * application. */ KVStoreConfig kconfig = new KVStoreConfig(storeName, hostName + ":" + port); /* Set the required security properties */ Properties secProps = new Properties(); secProps.setProperty(KVSecurityConstants.TRANSPORT_PROPERTY, KVSecurityConstants.SSL_TRANSPORT_NAME); secProps.setProperty (KVSecurityConstants.SSL_TRUSTSTORE_FILE_PROPERTY, "/home/kv/client.trust"); kconfig.setSecurityProperties(secProps); store = KVStoreFactory.getStore(kconfig, new PasswordCredentials(username, password.toCharArray())); null /* ReauthenticateHandler */); } catch (AuthenticationFailureException afe) { /* * Could potentially retry the login, possibly with different * credentials, but in this simple example, we just fail the * attempt. */ System.out.println("authentication failed!") return; }
Another way to handle the login is to place your authentication credentials in a flat text file that contains all the necessary properties for authentication. In order for this to work, a password store must have been configured for your Oracle NoSQL Database store. (See the Oracle NoSQL Database Security Guide for information on setting up password stores).
For example, suppose your store has been configured to use a
password file password store and it is contained in a file
called login.pwd
. In that case, you might
create a login properties file called
login.txt
that looks like this:
oracle.kv.auth.username=clientUID1 oracle.kv.auth.pwdfile.file=/home/nosql/login.pwd oracle.kv.transport=ssl oracle.kv.ssl.trustStore=/home/nosql/client.trust
In this case, you can perform authentication in the following way:
import oracle.kv.AuthenticationFailure; import oracle.kv.PasswordCredentials; import oracle.kv.KVStoreConfig; import oracle.kv.KVStoreFactory; /* the client gets login credentials from the login.txt file */ /* can be set on command line as well */ System.setProperty("oracle.kv.security", "/home/nosql/login.txt"); KVStore store = null; try { /* * storeName, hostName, port are all strings that would come * from somewhere else in your application. * * Notice that we do not pass in any login credentials. * All of that information comes from login.txt */ myStoreHandle = KVStoreFactory.getStore( new KVStoreConfig(storeName, hostName + ":" + port)) } catch (AuthenticationFailureException afe) { /* * Could potentially retry the login, possibly with different * credentials, but in this simple example, we just fail the * attempt. */ System.out.println("authentication failed!") return; }
It is possible for an authentication session to expire. This
can happen for several reasons. One is that the store's
administrator has configured the store to not allow session
extension and the session has timed out. These properties are
configured using sessionExtendAllow
and
sessionTimeout
. See the
Oracle NoSQL Database Security Guide for information on
these properties.
Reauthentication might also be required if some kind of a major disruption has occurred to the store which caused the authentication session to become invalidated. This is a pathological condition which you should not see with any kind of frequency in a production store. Stores which are installed in labs might exhibit this condition more, especially if the stores are frequently restarted.
An application can encounter an expired authentication session at any point in its lifetime, so robust code that must remain running should always be written to respond to authentication session expirations.
When an authentication session expires, by default the method
which is attempting store access will throw
AuthenticationRequiredException
. Upon
seeing this, your code needs to reauthenticate to the store,
and then retry the failed operation.
You can manually reauthenticate to the store by
using the KVStore.login()
method.
This method requires you to provide the login credentials via
a LoginCredentials
class instance
(such as PasswordCredentials
):
try { ... /* Store access code happens here */ ... } catch (AuthenticationRequiredException are) { /* * myStoreHandle is a KVStore class instance. * * pwCreds is a PasswordCredentials class instance, obtained * from somewhere else in your code. */ myStoreHandle.login(pwCreds); }
Note that this is not required if you use the
oracle.kv.auth.username
and
oracle.kv.auth.pwdfile.file
properties, as shown in the previous section.
In that case, your Oracle NoSQL Database client code
will automatically and silently reauthenticate your client
using the values specified by those properties.
A third option is to create a
ReauthenticationHandler
class
implementation that performs your reauthentication for you.
This option is only necessary if you provided a
LoginCredentials
implementation
instance (that is,
PasswordCredentials
)
in a call to KVStoreFactory.getStore()
,
and you want to avoid a subsequent need to retry operations
by catching AuthenticationRequiredException
.
A truly robust example of a
ReauthenticationHandler
implementation
is beyond the scope of this manual (it would be driven by
highly unique requirements that are unlikely to be
appropriate for your site). Still, in the interest of
completeness, the following shows a very simple and not very
elegant implementation of
ReauthenticationHandler
:
package kvstore.basicExample import oracle.kv.ReauthenticationHandler; import oracle.kv.PasswordCredentials; public class MyReauthHandler implements ReauthenticationHandler { public void reauthenticate(KVStore reauthStore) { /* * The code to obtain the username and password strings would * go here. This should be consistent with the code to perform * simple authentication for your client. */ PasswordCredentials cred = new PasswordCredentials(username, password.toCharArray()); reauthStore.login(cred); } }
You would then supply a MyReauthHandler
instance when you obtain your store handle:
import java.util.Properties; import oracle.kv.AuthenticationFailure; import oracle.kv.PasswordCredentials; import oracle.kv.KVSecurityConstants; import oracle.kv.KVStoreConfig; import oracle.kv.KVStoreFactory; import kvstore.basicExample.MyReauthHandler; KVStore store = null; try { /* * storeName, hostName, port, username, and password are all * strings that would come from somewhere else in your * application. The code you use to obtain your username * and password should be consistent with the code used to * obtain that information in MyReauthHandler. */ KVStoreConfig kconfig = new KVStoreConfig(storeName, hostName + ":" + port); /* Set the required security properties */ Properties secProps = new Properties(); secProps.setProperty(KVSecurityConstants.TRANSPORT_PROPERTY, KVSecurityConstants.SSL_TRANSPORT_NAME); secProps.setProperty (KVSecurityConstants.SSL_TRUSTSTORE_FILE_PROPERTY, "/home/kv/client.trust"); kconfig.setSecurityProperties(secProps); store = KVStoreFactory.getStore(kconfig, new PasswordCredentials(username, password.toCharArray())); new MyReauthHandler()); } catch (AuthenticationFailureException afe) { /* * Could potentially retry the login, possibly with different * credentials, but in this simple example, we just fail the * attempt. */ System.out.println("authentication failed!") return; }
Currently, clients which must authenticate to a store either
have no access or complete access, depending on the current
state of their authorization session. However, future
releases of Oracle NoSQL Database may introduce authorization levels such
that a call to the Oracle NoSQL Database API could fail due to not having
the authority to perform the operation. When this happens,
UnauthorizedException
will be thrown.
In this release, your code should never see
UnauthorizedException
. However, you
might want to plan your code so that it is possible to respond
gracefully to this exception, especially if you think your
organization would be interested in using multi-level access
controls.
When UnauthorizedException
is seen,
the operation should not be retried. Instead, the operation
should either be abandoned entirely, or your code could
attempt to reauthenticate using different credentials that
would have the required permissions necessary to perform the
operation. Note that a client can log out of a store using
KVStore.logout()
. How your code logs
back in is determined by how your store is configured for
access, as described in the previous sections.