Apereo CAS - JPA Service Registry w/ Oracle

Posted by Misagh Moayyed on April 07, 2020 · 9 mins read

An identity provider such as Apereo CAS that provides single sign-on is fully powered on once you begin to integrate and onboard applications to take advantage of centralized functionality and policy management. These applications, also known as services in the CAS vernacular, and their associated policies can be managed by a CAS deployment using a variety of services and technologies. The core component of the service/application management facility is what is referred to as the service registry that stores application definitions into an underlying store such as a relational database. Thus, the registry acts as a facade and gatekeeper between the business layers of the system and the storage facility that in our case happens to be an Oracle database.

In this tutorial, we will briefly take a look at JPA Service Registry support in CAS, with our starting position as follows:

Configuration

Let’s take a look at our modest task list:

  • (Optional) Start an Oracle database to store application definitions.
  • Configure CAS to connect to said Oracle database to activate service management.
  • (Optional) Import application definitions into our Oracle database via CAS.

Let’s begin.

Oracle Setup

If you do have an Oracle database at hand, feel free to skip this step. I don’t happen to have one readily available so I decided to keep things simple by using a Docker container, off of a fairly sizeable Docker image, to power on an Oracle database instance:

docker run -d -p 1521:1521 --name oracle-db store/oracle/database-enterprise:12.2.0.1-slim

Be patient. It might some time for Docker to pull down the image and start the container, but once ready, you should see the following output:

$ docker ps

CONTAINER ID   IMAGE                                             PORTS                              NAMES
a42d53af19f5   store/oracle/database-enterprise:12.2.0.1-slim    0.0.0.0:1521->1521/tcp, 5500/tcp   oracle-db

During the process, if you ever needed to start over fresh and spawn a new container, you may kill and remove all traces of the existing running container via the following bash function:

function dkc() {
   export CID=$(docker ps -aqf "name=$1");
   docker stop $CID 2>/dev/null
   docker rm -f $CID 2>/dev/null
   docker volume prune --force
}

…which allows you to simply pass along the container name as a function parameter to stop and remove the container:

dkc oracle-db
Lost in Translation?
dkc is short for Docker Kill Container. You're welcome to choose a more creative name!

So back to the task at hand, we will keep this container running in the background and will use it as a testbed to connect it to our CAS instance to store application registration records.

CAS Configuration

You should start by including the indicated module in your CAS WAR Overlay. Of course, we have to introduce CAS and our Oracle database to each other using the following settings:

cas.jdbc.showSql=true

cas.serviceRegistry.jpa.user=system
cas.serviceRegistry.jpa.password=Oradoc_db1
cas.serviceRegistry.jpa.driverClass=oracle.jdbc.driver.OracleDriver
cas.serviceRegistry.jpa.url=jdbc:oracle:thin:@localhost:1521:ORCLCDB
cas.serviceRegistry.jpa.dialect=org.hibernate.dialect.Oracle12cDialect

cas.serviceRegistry.jpa.ddlAuto=update

That should be all. Once you rebuild and run CAS again, you should see the following in the CAS logs:

INFO [o.a.c.s.AbstractServicesManager] - <Loaded [0] service(s) from [JpaServiceRegistry].>

In the above settings, we are instructing CAS to achieve the following:

  • Turn on logging for all SQL statements. Doing this allows you to see queries generated by Hibernate for database operations such as SELECT or DELETE, etc.
  • Connect to our Oracle database using the Docker-provided credentials, connection string, and the appropriate database dialect.
  • Control Hibernate’s DDL processing and generation by attempting to update the database schema if necessary.

So at this point, we have a running CAS server talking to our Oracle database. However, as the log message above indicates, we have no services available in our registry so let’s populate our database instance by importing a few application definitions.

Manual Entries
If you don't have any application records to import and would rather begin by manually adding applications to your CAS instance and Oracle database, you should begin by taking a look at the Management Web Application. Do NOT try to insert database records manually using INSERT statements; that has the potetial to corrupt the database state very quickly.

Import Applications

Based on the CAS documentation, our JPA service registry is able to auto initialize itself from default JSON service definitions available to CAS. The initializer can detect all service definitions files found on the classpath (i.e. src/main/resources/services) and import them into the real service registry used. Note that the location of the JSON files, while typically set to the classpath, may be controlled via CAS properties; The same setting that controls the location of the JSON service files for the JSON service registry is used by the initialization logic to locate service files.

So let’s instruct CAS to initialize itself from JSON definitions that can be found in our given path:

cas.serviceRegistry.initFromJson=true
cas.serviceRegistry.json.location=file:/etc/cas/config/services

Our /etc/cas/config/services directory should contain the following files to be importted:

$ ls /etc/cas/config/services

Permissions Size User   Group Date Modified Name
.rw-r--r--   488 Misagh wheel 30 Sep  2019  Sample-100.json

Our Sample-100.json file as an example contains the following:

{
    "@class": "org.apereo.cas.services.RegexRegisteredService",
    "serviceId": "https://mmoayyed.example.net:9443/sample.*",
    "name": "Sample",
    "id": 100,
    "description": "Sample Java CASified application"
}

If you build and run CAS again, you should see the following output in your CAS logs:

INFO [o.a.c.c.CasServiceRegistryInitializationConfiguration] - <Attempting to initialize the service registry \
  [EmbeddedResourceBasedServiceRegistry,JpaServiceRegistry] from service definition resources \
  found at [URL [file:/etc/cas/config/services]]>
...
...
<Loaded [2] service(s) from [EmbeddedResourceBasedServiceRegistry,JpaServiceRegistry].>

While the above output may be somewhat cryptic, what it’s saying is that two service registries are active:

  • The EmbeddedResourceBasedServiceRegistry which is mainly responsible for loading our service definitions and preparing them for import.
  • The JpaServiceRegistry which is the real registry and the target destination for the import operation.

From here on out, JpaServiceRegistry will be regarded as the main service registry. To confirm the import, you can always double-check the state of the database:

image

Of course, remember to turn off or remove the below settings when you’re done:

# cas.serviceRegistry.initFromJson=true
# cas.serviceRegistry.json.location=file:/etc/cas/config/services

Finale

I hope this review was of some help to you and I am sure that both this post as well as the functionality it attempts to explain can be improved in any number of ways. Please feel free to engage and contribute as best as you can.

Misagh Moayyed