OdbResource


Useful Links

OdbResource Javadocs

OrientDB Manual

Apache Tomcat 8 Documentation

Apache License, Version 2.0


Introduction

OrientDB was a product that I stumbled across a year or two ago. I instantly fell in love with what they were trying to do. A graph database with features of document-oriented databases that could run standalone or embedded. ACID transactions, multi-master replication and record level security!

After playing around with it for a short time, I saw the possibility of using it for web applications in a way that would improve security while simultaneously removing some of the boilerplate that usually has to be written.

OdbResource provides some components to facilitate this. Two of them are object factory classes that can be bound to a JNDI service. They provide singleton instances of the OServer class and OPartitionedDatabasePoolFactory class respectively. The former allows one to run OrientDB as an embedded server in a Tomcat web application if so desired.

The third component is OdbRealm, an OrientDB realm for Tomcat.


User Guide

This guide covers the use of the OdbResource components with Tomcat.

Download and Install

  1. Download the latest binary release from the OdbResource release page on Github. Or download the source and build the package phase with Maven.
  2. Unzip the file odbresource-[version number]-bin.zip in the lib directory of Apache Tomcat.

Configuration

There are three components that must be configured at either the context or server level. (context.xml or server.xml) They are OServerObjectFactory, OPDPFObjectFactory and OdbRealm.

OServerObjectFactory

This resource is only declared when are running OrientDB embedded. It is never referenced directly from your application but must referenced by the server attribute in the resource declaration for OPDPFObjectFactory. OPDPFObjectFactory will then look up the OServerObjectFactory resource which will start the embedded OrientDB server.

OServerObjectFactory resource declaration:

              <Resource
                auth="Container"
                closeMethod="shutdown"
                configFile="/mnt/share/orientdb-community-2.1.9/config/orientdb-server-config.xml"
                factory="com.ashtonit.odb.jndi.OServerObjectFactory"
                name="oserver"
                singleton="true"
                type="com.orientechnologies.orient.server.OServer"
              />
            	

The only attribute in the above element that is not described in the Tomcat configuration guide is configFile. The value of this attribute is the name of the configuration file for the OrientDB server. Refer to the OrientDB manual when editing this.

OPDPFObjectFactory

The OPDPFObjectFactory class must be configured as a JNDI Resource in Tomcat. This must be done in either server.xml or the context.xml file for the web application.

Putting the configuration in server.xml is generally more appropriate. When running the embedded server, the OServer instance runs as a singleton anyway so the configuration is effectively global wherever you put it. Two different configurations in different context.xml files would result in undefined behaviour. Most likely the first application to be loaded would override any others.

The Tomcat guide on resource configuration is here. Important things to note for OdbResource configuration are:

  1. The auth attribute must have a value of "Container"
  2. The capacity attribute sets the maximum number of connections available
  3. The closeMethod attribute must have a value of "close"
  4. The factory attribute must have a value of "com.ashtonit.odb.jndi.OPDPFObjectFactory"
  5. The type attribute must have a value of "com.orientechnologies.orient.core.db.OPartitionedDatabasePoolFactory"
  6. The server attribute must be present only if you are running OrientDB embedded, in which case the value must match the value of the name attribute for the OServer resource declaration
  7. The singleton attribute must have a value of true, to ensure that the close method is run (In fact the factory remains a singleton regardless of this attribute.)

OPDPFObjectFactory resource declaration:

              <Resource
                auth="Container"
                capacity="100"
                closeMethod="close"
                factory="com.ashtonit.odb.jndi.OPDPFObjectFactory"
                name="opdpfactory"
                server="oserver"
                singleton="true"
                type="com.orientechnologies.orient.core.db.OPartitionedDatabasePoolFactory"
              />
                

The only attributes in the above element that are not described in the Tomcat configuration guide are capacity and server. The server attribute must be present only if you want to run an embedded OrientDB server. The value must match the value of the name attribute for the OServer resource declaration.

Finally, to make the resource available to a given web application you also must add it as a Resource Link in the web app's context.xml:.

              <?xml version="1.0" encoding="UTF-8"?>
              <Context>
                <ResourceLink global="opdpfactory" name="opdpfactory"
                              type="com.orientechnologies.orient.core.db.OPartitionedDatabasePoolFactory">
                </ResourceLink>
              </Context>
            	

The Tomcat guide on JNDI resource configuration is here.

OdbRealm

This is a fairly simple and restricted authentication Realm for Tomcat. It only supports one form of authentication, the basic username/password method. Attempting to use any other method will result in an UnsupportedOperationException.

The Tomcat guide on realm configuration is here. See the API documentation for OdbRealm for more information on this specific implementation. Important things to note for OdbRealm configuration are:

  1. The className attribute must have a value of "com.ashtonit.odb.realm.OdbRealm"
  2. The value of the dbUser attribute must be the name of a user with read access to the OUser class in the OrientDB database. The "admin" user can be used for this for development and testing purposes
  3. The dbResource attribute is optional:
    • If present, the OdbRealm pool will be obtained from the OPartitionedDatabasePoolFactory resource. The value must match the value of the "name" attribute in the OdbResource configuration
    • If not present, a new pool with the default capacity will be instantiated by the realm
  4. The value of the dbUrl attribute must be a valid OrientDB URI
  5. The value of the query attribute must be an OSQL query string that takes one parameter (a user identifier) and returns the password hash and roles
              <Realm
                className="com.ashtonit.odb.realm.OdbRealm"
                dbPass="admin"
                dbResource="opdpfactory"
                dbUrl="plocal:/opt/odb/mygraphdb"
                dbUser="admin"
                query="SELECT password, roles.name AS roles FROM OUser WHERE status = 'ACTIVE' AND name = ?"
              />
            	

OdbRealm was originally written to authenticate only against the built-in OUser and ORole classes but the authentication is now configurable by a query.

It takes an OSQL query string as a attribute named query which must return the password hash and the roles for a user.

The query must:

  • Take one parameter (the user name or identifier)
  • Return the password hash as a String for the user with a parameter name of, "password"
  • Return the role names for the user as a List of Strings with a parameter name of, "roles"

The password is checked using the method OSecurityManager.checkPassword(String, String). It checks for three different types of password hashes by looking at the prefix of the string. They are:

  • SHA-256 (A string prefix of {SHA-256}-)
  • PBKDF2WithHmacSHA1 (A string prefix of {PBKDF2WithHmacSHA1}-)
  • PBKDF2WithHmacSHA256 (A string prefix of {PBKDF2WithHmacSHA256}-)

The simplest way to create a password hash in the correct format is to use the method OUser.encryptPassword(String).

The Principal implementation used by OdbRealm is OdbPrincipal. If you want to look up an OrientDB pool factory directly from a JNDI context in your web application you can cast the principal to OdbPrincipal and then obtain the OrientDB URL as well as the username and password. See the servlet example just below.

Declare Dependency

OdbResource is available as a dependency from the Maven Central Repository. Declare it with a scope of provided in your web application. The library itself must be available to the container at start up. An example is given for Maven:

              <dependency>
                  <groupId>com.ashtonit</groupId>
                  <artifactId>odbresource</artifactId>
                  <version>[2.2,2.3)</version>
                  <scope>provided</scope>
              </dependency>
                          

Here we have a simple example showing how to look up the database pool factory in the JNDI context, obtain and use an OrientGraph instance and release it again.

A Servlet Example

Here we have a simple example showing how to look up the database pool factory in the JNDI context, obtain and use an OrientGraph instance and release it again.

NOTE: The example given below pulls the username and password from the principal. This only makes sense when you are authenticating agains the built-in OUser and ORole classes. For any other data model you will need the username and password of a database user configured statically.

            public class MyServlet extends HttpServlet {

                public void service(HttpServletRequest request, HttpServletResponse response)
                  throws ServletException, IOException {

                    Context initCtx = new InitialContext();
                    Context envCtx = (Context) initCtx.lookup("java:comp/env");
                    OPartitionedDatabasePoolFactory factory = (OPartitionedDatabasePoolFactory) envCtx.lookup("opdpfactory");
                    OdbPrincipal principal = (OdbPrincipal) httpRequest.getUserPrincipal();
                    
                    // Get the username and the password from the principal only if
                    // you are authenticating an actual database user, i.e. against
                    // the OUser and ORole classes. If you are authenticating against
                    // your own classes you will need a username and password for an
                    // actual database user. Add these using some kind of static
                    // configuration - i.e. Spring, from your web.xml, etc.
                    OPartitionedDatabasePool pool = factory.get(principal.getDbUrl(),
                                                                principal.getName(),
                                                                principal.getPassword());

                    OrientGraph graph = null;
                    try {
                        graph = new OrientGraph(pool.acquire());

                        OrientVertex vertex = graph.getVertex("myVertexKey");
                        out.println("<html>");
                        out.println("<body>");
                        out.println("<h1>");
                        out.println(vertex.toString());
                        out.println("</h1>");
                        out.println("</body>");
                        out.println("</html>");
        
                    } finally {
                        if (graph != null && !graph.isClosed()) {
                            graph.shutdown();
                        }
                    }
                }
            }
          	

You may notice that the database URI is obtained from the principal. This allows multiple databases, each managed by a different realm. The OdbResource configuration defines the server, not the database.