Documentation Get Started

Getting Started Tutorial

How can you start using OACC in your own project?

This document will bring you up to speed on how to install OACC, configure and initialize the OACC database, and define your application model. But before we jump in, let's quickly review the OACC dependencies.

Dependencies

OACC runs on Java™ SE 7 (Java™ version 1.7.0), or higher.

OACC depends on the following external libraries for cryptographic password hashing functions:

Note that if you plan to use OACC's built-in secure password authentication provider and want to prevent a cleanable char[] password being turned into a temporary String during Unicode character normalization, you need to include a dependency to ICU4J, the International Components for Unicode library, which is also available as a Maven dependency with groupId: com.ibm.icu and artifactId: icu4j.

1. Installation

There are several ways you can get the OACC library:

Include a Maven dependency

If your project uses Apache Maven as a build tool, the easiest way to include the latest OACC release into your project is to simply paste the following dependency into your POM file:

<dependency>
    <groupId>com.acciente.oacc</groupId>
    <artifactId>acciente-oacc</artifactId>
    <version>2.0.0</version>
</dependency>

Download the JAR files

You can download the latest OACC libraries directly from our Downloads page or from GitHub.

Build from source

If you wish, you can download the latest source from the OACC repository on GitHub and build it yourself.

To compile OACC you will need to include the following dependencies:

To compile and run the OACC unit tests, you will additionally need the following external libraries:

2. Database Setup

OACC persists all security relationships in database tables and currently supports several relational database management systems. For each supported RDBMS, OACC provides SQL scripts to set up the database schema, tables, user and privileges.

The currently supported database systems are:

  • IBM DB2 10.5
  • Microsoft SQL Server 12.0 (2014)
  • Oracle 11g R2
  • PostgreSQL 9.3
  • HSQLDB 2.3
  • MySQL 5.6 / MariaDB 10.0
  • SQLite 3.8

Database Setup Scripts

The database setup scripts can be found on the OACC Downloads page and consist of four different files that should be executed in the following sequence:

  1. create_database.sql
    • creates a dedicated database for OACC
    • typically run as a DBMS admin user
    • running this script is optional - you could simply create the OACC schema and/or tables within your project's current database
    • if you are using Oracle, HSQLDB or SQLite, please refer to the RDBMS-specific notes, below
  2. create_schema.sql
    • creates a database schema to house OACC-specific tables
    • run this script while connected to the database you set up with the create_database.sql script above
    • if you are using MySQL/MariaDB or SQLite, please refer to the RDBMS-specific notes, below
  3. create_tables.sql
    • creates OACC sequences, tables and constraints
    • run this script while connected to the database you set up with the create_database.sql script above
    • Note: if you modified (or omitted running) the previous create_schema.sql script, you need to update this script to reflect the modified (or lack of) database schema, before running it
  4. create_user.sql
    • creates a database user for OACC - Note: update this script to set the OACC database user's password!
    • grants privileges to connect to the OACC-database you set up with the create_database.sql script above
    • grants privileges to the OACC sequences and tables
    • if you are using IBM DB2 or SQLite, please refer to the RDBMS-specific notes, below

You are free to modify the provided scripts to suit your project's needs, as far as the database, schema, user and password are concerned - you'll get a chance to apply your customizations to the OACC configuration separately, after the database setup is complete.

There is a fifth script, drop_tables.sql, to facilitate removal of OACC constraints, tables and sequences, which you would only run when uninstalling OACC from your project.

These database scripts have been tested against the specified database system and version that their folder is named after. Often they can be run against other (especially higher) versions of the same database, as well, without any issues - but please keep in mind that we didn't actually verify this. Running a database setup script against a completely different database system might be possible between certain databases, but similarly to different SQL dialects, there could be small differences in DDL syntax that would cause an issue.

IBM DB2 Database Setup Notes

  • create_user.sql
    • DB2 typically uses OS authentication, which means that in DB2 a user has to be created externally to the database first!
    • The create_user.sql script assumes a database user by name of oaccuser has already been created. The script will grant that oaccuser privileges to the required OACC database objects.
    • If you wish to use a different database user name, please modify the create_user.sql script accordingly.

Oracle Database Setup Notes

  • create_database.sql
    • The Oracle version of this script is provided for completeness' sake, but doesn't actually do anything, because schema/table creation in Oracle sufficiently handles the namespacing of the OACC database objects

HSQLDB Database Setup Notes

  • create_database.sql
    • The HSQLDB version of this script is provided for completeness' sake, but doesn't actually do anything, because database creation in HSQLDB is implicit in starting an HSQLDB server or connecting to one

MySQL/MariaDB Database Setup Notes

  • create_schema.sql
    • The MySQL/MariaDB version of this script is provided for completeness' sake, but doesn't actually do anything, because database creation in MySQL/MariaDB is a synonym for schema creation and sufficiently handles the namespacing of the OACC database objects
    • Note that you'll need to pass null as the value for the schemaName parameter when acquiring an AccessControlContext from SQLAccessControlContextFactory in your application

SQLite Database Setup Notes

  • create_database.sql
    • The SQLite version of this script is provided for completeness' sake, but doesn't actually do anything, because database creation in SQLite is implicit during SQLite instantiation if the specifed database does not already exist
  • create_schema.sql
    • The SQLite version of this script is provided for completeness' sake, but doesn't actually do anything, because the database name is the only supported way to namespace tables in SQLite
    • Note that you'll need to pass null as the value for the schemaName parameter when acquiring an AccessControlContext from SQLAccessControlContextFactory in your application
  • create_user.sql
    • The SQLite version of this script is provided for completeness' sake, but doesn't actually do anything, because SQLite does not support user/account creation or granting authorization to database objects

3. OACC Initialization

Prior to using OACC in your project, you need to perform one more step:

Initialize OACC

OACC ships with a system initializer utility SQLAccessControlSystemInitializer that needs to be called once, after you create your tables. This utility prepares the OACC system for use and creates a default security domain and a super user in that domain.

Initialize OACC by running the SQLAccessControlSystemInitializer in Java with the following command-line parameters:

  • dburl - a JDBC connection URL *
  • dbuser - the OACC database user name from the create_user.sql script (oaccuser)
  • dbpwd - the password for the OACC database user from the create_user.sql script
  • pwdencryptor - the password hashing algorithm you want to use (either bcrypt for an OpenBSD BCrypt implementation or jasypt for the Jasypt digester)
  • oaccsystempwd - specify a password for the OACC system resource (aka the "super user"), which you'll need for later
  • dbschema - the OACC database schema from the create_schema.sql script (oacc), or omit if you didn't run the script

The usage to run the initializer from the command line:

$ java com.acciente.oacc.sql.SQLAccessControlSystemInitializer -dburl=<db-url> \
  -dbuser=<db-user> \
  -dbpwd=<db-password> \
  -pwdencryptor=(bcrypt | jasypt) \   # pick one: bcrypt or jasypt
  -oaccsystempwd=<OACC-root-password> \
  [ -dbschema=<db-schema> ]   # [optional - omit if no database schema was created]

If you run the initializer script on tables that have already been initialized OACC will detect this and exit safely without making any changes.

( * ) Note: If you're using MySQL/MariaDB or another RDBMS that by default generates the next sequence value when INSERTing 0 (zero) into an auto-incrementing/identity column, you will have to turn that behavior off when performing the OACC system initialization, because OACC attempts to create Ids of value zero for its system objects.
In MySQL set sql_mode=NO_AUTO_VALUE_ON_ZERO via sessionVariables during connection generation (unless you want to apply this globally to the entire MySQL instance). A sample JDBC connection url that specifies this for a MySQL connection is shown below:

jdbc:mysql://localhost/oaccdb?sessionVariables=sql_mode='NO_AUTO_VALUE_ON_ZERO,STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION'

Database Configuration for OACC unit tests

If you downloaded the OACC source and plan to run OACC unit tests, you need to also configure the included database system specific .properties file(s) (in the src/test/resources directory) with the appropriate database settings for your environment, based on the database setup scripts and the OACC initialization parameters used previously.

4. Application model definition

OACC allows you to manage access control on individual objects in your application. To do so, you have to define or "register" the parts of your application model you'd like to secure in OACC. This section will walk you through the basic concepts and some examples of how to do exactly that.

AccessControlContext

Before you can start defining your application model in OACC, you need to get a hold of an instance of an OACC AccessControlContext. The SQLAccessControlContextFactory in com.acciente.oacc.sql provides two methods for this purpose, with the following signatures:

// SQLAccessControlContextFactory
getAccessControlContext(Connection con, String schemaName, SQLProfile sqlProfile, PasswordEncryptor pwdEncryptor);
getAccessControlContext(DataSource ds, String schemaName, SQLProfile sqlProfile, PasswordEncryptor pwdEncryptor);

The schemaName String argument represents the database schema name used in the create_schema.sql script, when you set up the database. The script defaults to OACC for the schema name, but you need to pass whatever schema name you actually used to the factory, or null if you didn't run the script or if you were using the scripts provided for MySQL or SQLite.

OACC currently supports RDBMS with seven different flavors of SQL dialects, and can utilize either a recursive or non-recursive query strategy, depending on the database. Thus you need to specify which dialect and query strategy you're using when obtaining an AccessControlContext from the factory, by passing in a SQLProfile parameter. The SQLProfile class provides the following options, listed by target database:

  • IBM DB2:
    • DB2_10_5_RECURSIVE
    • DB2_10_5_NON_RECURSIVE
  • Oracle:
    • Oracle_11_2_RECURSIVE
    • Oracle_11_2_NON_RECURSIVE
  • PostgreSQL:
    • PostgreSQL_9_3_RECURSIVE
    • PostgreSQL_9_3_NON_RECURSIVE
  • Microsoft SQL Server:
    • SQLServer_12_0_RECURSIVE
    • SQLServer_12_0_NON_RECURSIVE
  • HSQLDB:
    • HSQLDB_2_3_NON_RECURSIVE
  • MySQL/MariaDB:
    • MySQL_5_6_NON_RECURSIVE
  • SQLite:
    • SQLite_3_8_RECURSIVE
    • SQLite_3_8_NON_RECURSIVE

The PasswordEncryptor argument is used to configure the password encryption scheme employed by the built-in SQLPasswordAuthenticationProvider and must correspond to the one specified during initialization in the previous step. Your choices are a BCryptPasswordEncryptor for an implementation of the OpenBSD BCrypt algorithm or a JasyptPasswordEncryptor for a Jasypt password digester. You can specify configuration details for each type of encryptor through the static factory methods provided in the respective password encryptor implementations.

Note that if you ever need to switch password encryption algorithms while still being able to authenticate existing users, you can employ a TransitioningPasswordEncryptor that takes both a source and a target encryptor: it will be able to authenticate old and new resources, but it will store or reset passwords only using the new encryption scheme.

OACC System Resource

The OACC system resource is the "super user" that was created when you initialized OACC earlier. We need to authenticate to the AccessControlContext as the system resource, in order to define parts of your application model, such as the resource classes described in the next step after this one.

The OACC system resource always has zero (0) as its resourceId, and its super user password is what you specified as the oaccsystempwd parameter to the SQLAccessControlSystemInitializer earlier.

The authenticate method of the AccessControlContext takes a reference to a Resource and its corresponding authentication credentials, so you can authenticate as the system resource as follows:

    accessControlContext.authenticate(Resources.getInstance(0),
          PasswordCredentials.newInstance("yourOaccSystemPassword".toCharArray()));

To put it all together, let's take a look at a sample code snippet that will let us authenticate as the system resource for OACC running on a PostgreSQL database:

import com.acciente.oacc.*;
import com.acciente.oacc.encryptor.bcrypt.*;
import com.acciente.oacc.sql.*;
import java.sql.*;

public class OACCApplicationModelDefinition {
   public static void main(String[] args) throws Exception {
      // get a connection to the rsf database
      String url = "jdbc:postgresql://localhost/oaccdb?user=oaccuser&password=oaccpwd";
      try (Connection con = DriverManager.getConnection(url)) {
         // get the access control context
         AccessControlContext accessControlContext
               = SQLAccessControlContextFactory.getAccessControlContext(con,
                                                                        "OACC",
                                                                        SQLProfile.PostgreSQL_9_3_RECURSIVE,
                                                                        BCryptPasswordEncryptor.newInstance(12));

         // authenticate as the system resource
         accessControlContext.authenticate(Resources.getInstance(0),
                                           PasswordCredentials.newInstance("yourOaccSystemPassword".toCharArray()));

         // now we're ready to register our application model with OACC
         // (...)
      }
   }
}

ResourceClass

The first step to defining your application model in OACC is to tell OACC about the different types of resources you have.

These different types of application entities you wish to secure in OACC translate into OACC resource classes. For each resource class, you need to decide

  • Does it represent an authenticatable entity type in your application, in other words, can it "log in"?
    • sample authenticatable resource classes: User, Admin, Vendor
    • unauthenticatable resource class examples: Document, Product, Order
  • Can instances of this type be created by other unauthenticated resources, in other words, can it be created without someone having to log in first?
    • a classic example of a resource class that allows creation from an unauthenticated context is a User, if your application allows users to sign themselves up from a website, for example
    • some examples of resource classes that typically can only be created from an authenticated context: Admin, Product

The OACC API to create a resource class looks like this:

AccessControlContext {
    void createResourceClass(String resourceClassName,
                             boolean authenticatable,
                             boolean unauthenticatedCreateAllowed);
}

As an example, we could define the following resource classes:

    accessControlContext.createResourceClass("USER", true, true);
    accessControlContext.createResourceClass("ADMIN", true, false);
    accessControlContext.createResourceClass("DOCUMENT", false, false);

Permission

Next, you get to decide what kinds of actions you want to secure on the resource classes you registered above.

OACC lets you define custom permissions to model your application operations, such as "read", "update", "delete", "download", "upload", or whatever you need secured within your project.

A key point to remember is that OACC does not attach any meaning to permissions, such as "view" or "edit" - they are just labels. OACC simply makes it easy for your application to check if a resource has a permission and your application would then allow or disallow an action, accordingly.

The OACC API to create a resource permission looks like this:

AccessControlContext {
    void createResourcePermission(String resourceClassName, String permissionName);
}

To expand on our example, we could define the following permissions for our resource classes:

    // permissions on resources of class "USER"
    accessControlContext.createResourcePermission("USER", "VIEW");
    accessControlContext.createResourcePermission("USER", "EDIT");
    accessControlContext.createResourcePermission("USER", "DEACTIVATE");

    // permissions on resources of class "ADMIN"
    accessControlContext.createResourcePermission("ADMIN", "EDIT");
    accessControlContext.createResourcePermission("ADMIN", "DEACTIVATE");

    // permissions on resources of class "DOCUMENT"
    accessControlContext.createResourcePermission("DOCUMENT", "READ");
    accessControlContext.createResourcePermission("DOCUMENT", "UPDATE");
    accessControlContext.createResourcePermission("DOCUMENT", "COPY");
    accessControlContext.createResourcePermission("DOCUMENT", "PRINT");

Note that OACC already provides functionality to deal with the life-cycle of resources, so you typically do not have to define CREATE or DELETE permissions for any resource class.

Other built-in permissions that are automatically available to all resource classes are:

  • *INHERIT - to inherit permissions from another resource
  • *QUERY - to inquire about (e.g. retrieve or verify permissions) a resource other than the session resource
  • *IMPERSONATE - to act on behalf of another authenticatable resource
  • *RESET-CREDENTIALS - to reset the password of an authenticatable resource

Domain

Every resource in OACC exists inside a domain. Domains serve to scope groups of resources, and most security operations in OACC work within the context of a specified domain.

Domains are incredibly useful to isolate groups of resources in multi-tenant applications, but even if your application is not multi-tenant, we recommend you create at least one domain specific to your application.

The OACC API to create domains looks like this:

AccessControlContext {
    void createDomain(String domainName);

    void createDomain(String domainName, String parentDomainName);
}

For our example, we could define a single domain as follows:

    accessControlContext.createDomain("APP_DOMAIN");

Next steps

At this point, we've defined a simple model consisting of domain(s), resource classes and permissions, and we're ready to use OACC from our application, as shown in the next section.

For more information regarding the aforementioned API methods, please take a look at the OACC Javadocs. For a much more detailed example on how to integrate OACC into a sample Java application to address several real-world authorization scenarios continue on to the SecureTodo sample application.

5. Sample Client Application

The following listing is a simplistic example to illustrate how an application could use OACC to facilitate securing access to the entities in its application model.

import com.acciente.oacc.*;
import com.acciente.oacc.encryptor.bcrypt.*;
import com.acciente.oacc.sql.*;
import java.sql.*;
import java.util.*;

public class OACCSampleApplication {
   public static void main(String[] args) throws Exception {
      // get a connection to the oacc database
      String url = "jdbc:postgresql://localhost/oaccdb?user=oaccuser&password=oaccpwd";

      try (Connection con = DriverManager.getConnection(url)) {
         // get the access control context
         AccessControlContext accessControlContext
               = SQLAccessControlContextFactory.getAccessControlContext(con,
                                                                        "OACC",
                                                                        SQLProfile.PostgreSQL_9_3_RECURSIVE,
                                                                        BCryptPasswordEncryptor.newInstance(12));

         // create new admin
         createAdmin(accessControlContext);

         // create new user
         createUser(accessControlContext);

         // login as admin
         loginAdmin(accessControlContext, "adminJoe", "pa55w0rd");

         // attempt to update user while logged in as admin
         updateUser(accessControlContext, "jsmith");
      }
   }

   private static void createAdmin(AccessControlContext accessControlContext) {
      // authenticate as the system resource (the super user) to set up an initial admin
      accessControlContext.authenticate(Resources.getInstance(0),
                                        PasswordCredentials.newInstance("yourOaccSystemPassword".toCharArray()));

      // persist the admin in your application
      // for example:
      AppAdmin admin = new AppAdmin.Builder()
            .login("adminJoe")
            .email("joeBloe@company.com")
            .build()
            .create();

      // create the corresponding OACC resource
      final Resource adminResource
            = accessControlContext.createResource("ADMIN",
                                                  "APP_DOMAIN",
                                                  admin.getLogin(),
                                                  PasswordCredentials.newInstance("pa55w0rd".toCharArray()));
      System.out.println("created new ADMIN resource with Id=" + adminResource.getId());

      // grant permissions to query about, view and deactivate any user account, but not to edit it
      Set<ResourcePermission> permissions = new HashSet<>();
      permissions.add(ResourcePermissions.getInstance(ResourcePermissions.QUERY));
      permissions.add(ResourcePermissions.getInstance("VIEW"));
      permissions.add(ResourcePermissions.getInstance("DEACTIVATE"));

      accessControlContext.setGlobalResourcePermissions(adminResource,
                                                        "USER",
                                                        "APP_DOMAIN",
                                                        permissions);
      accessControlContext.unauthenticate();
   }

   private static void createUser(AccessControlContext accessControlContext) {
      // persist the user in your application
      // e.g. UserHOME.create("jsmith", "Jane", "Smith", "jsmith@mail.com", userResource.getId())
      // for example:
      AppUser user = new AppUser.Builder()
            .login("jsmith")
            .firstName("Jane")
            .lastName("Smith")
            .email("jsmith@mail.com")
            .build()
            .create();

      // don't have to be authenticated to create users because
      // the resource class has the unauthenticatedCreateAllowed-flag set

      final Resource userResource
            = accessControlContext.createResource("USER",
                                                  "APP_DOMAIN",
                                                  user.getLogin(),
                                                  PasswordCredentials.newInstance("pa$$word1".toCharArray()));
      System.out.println("created new USER resource with Id=" + userResource.getId());
   }

   private static void loginAdmin(AccessControlContext accessControlContext,
                                  String adminLogin,
                                  String password) {
      // authenticate as the admin resource
      accessControlContext.authenticate(Resources.getInstance(adminLogin),
                                        PasswordCredentials.newInstance(password.toCharArray()));
   }

   private static void updateUser(AccessControlContext accessControlContext,
                                  String userLogin) {
      // assert that the authenticated admin has VIEW permission *before* attempting to load the user
      accessControlContext.assertResourcePermissions(accessControlContext.getSessionResource(),
                                                     Resources.getInstance(userLogin),
                                                     ResourcePermissions.getInstance("VIEW"));

      // load the user information and modify the local copy
      AppUser user = new AppUser.Finder().findByLogin(userLogin);
      user.setEmail("other@mail.com");

      // assert that the authenticated admin has EDIT permission *before* attempting to save the user
      accessControlContext.assertResourcePermissions(accessControlContext.getSessionResource(),
                                                     Resources.getInstance(userLogin),
                                                     ResourcePermissions.getInstance("EDIT"));

      // save the user
      // !NOTE! we won't get here because adminResource doesn't have EDIT permission on the userResource
      user.save();
   }
}