Apereo CAS 6.1.x - Attribute Repositories w/ Person Directory


Collaborate
This blog is managed and hosted on GitHub. If you wish to update the contents of this post or if you have found an inaccuracy and wish to make corrections, we recommend that you please submit a pull request to this repository.

Overview

The ability to fetch attributes from external data stores has been present in CAS since the days of 3.x. This functionality was and, to this day, is provided by an Apereo project called Person Directory which is a Java framework for resolving persons and attributes from a variety of underlying sources. It consists of a collection of components that retrieve, cache, resolve, aggregate and merge person attributes from JDBC, LDAP and more. CAS attempts to take advantage of this framework through a concept called PrincipalResolver whose goal is to construct a final identifiable authenticated principal for CAS which carries a number of attributes inside it fetched from attribute repository sources. This meant that for instance, one could authenticate with LDAP in one query and then turn around the ask the same LDAP, a relational database and a Groovy script to fetch attributes for the resolved principal and combine all results into a final collection.

Note that in most cases, and starting around CAS 4.x, the authentication engine has been enhanced to be able to retrieve and resolve attributes from the authentication source, which would eliminate the need for configuring a separate attribute repository especially if both the authentication and the attribute source are the same. Using separate resolvers and sources should only be required when sources are different, or when there is a need to tackle more advanced attribute resolution use cases such as cascading, merging, etc.

What About...?
Note that attribute resolution via PrincipalResolver components and Person Directory's attribute repositories shall always execute, if and when configured, regardless of how the authentication event occurs. Whether the user is authenticating against a CAS-owned account store or is handed off to an external identity provider, in either scenario CAS is able to put together attributes from both the authentication source as well as any and all attribute repositories configured.

This tutorial focuses on a number of use case involving attribute repositories, fetching attributes from an external store a JSON file and a collection of hard-coded stubbed attributes. We will demonstrate variations on how attribute sources may be cached, filtered and released in a number of fancy ways.

Our starting position is based on:

Baseline Configuration

Our JSON attribute repository source, separate from the CAS authentication store is fairly simple:

{
    "casuser": {
        "firstName": ["Bob"],
        "employeeNumber": ["123456"],
        "lastName": ["Johnson"]
    }
}

Our external attribute repositories are then taught to CAS:

cas.authn.attributeRepository.json[0].location=file://etc/cas/config/attribute-repository.json
cas.authn.attributeRepository.json[0].id=MyJson

cas.authn.attributeRepository.stub.id=StaticStub
cas.authn.attributeRepository.stub.attributes.uid=mmoayyed
cas.authn.attributeRepository.stub.attributes.displayName=Misagh Moayyed
cas.authn.attributeRepository.stub.attributes.firstName=Misagh
cas.authn.attributeRepository.stub.attributes.lastName=Moayyed

Note that each attribute repository is given an id which can the be used as a filter to narrow the resolution logic down to matching repositories.

Use Cases

1

The requirements for this use case are as follows:

  • Disable attribute resolution from external attribute repositories via Person Directory.
  • Resolve attributes at release time for a given application only using the MyJson attribute repository.
  • No caching of attributes shall happen either globally or for the application.

The relevant properties for this use case are:

cas.authn.attributeRepository.expirationTime=0
cas.authn.attributeRepository.expirationTimeUnit=seconds
cas.authn.attributeRepository.merger=multivalued

cas.personDirectory.attributeResolutionEnabled=false

Our service definition matches the following:

{
  "@class" : "org.apereo.cas.services.RegexRegisteredService",
  "serviceId" : "https://app.example.org",
  "name" : "ExampleApplication",
  "id" : 1,
  "attributeReleasePolicy" : {
    "@class" : "org.apereo.cas.services.ReturnAllAttributeReleasePolicy",
    "principalAttributesRepository" : {
      "@class" : "org.apereo.cas.authentication.principal.DefaultPrincipalAttributesRepository",
      "attributeRepositoryIds": ["java.util.HashSet", [ "myjson" ]]
    }
  }
}

Since caching is disabled, you can change the underlying attribute value in the JSON attribute repository and CAS should pick up the change and release the new attribute value to the example application.

2

The requirements for this use case are as follows:

  • Disable attribute resolution from external attribute repositories via Person Directory.
  • Resolve attributes at release time for a given application only using the MyJson attribute repository.
  • Turn on global caching of attributes for 60 seconds.

The relevant properties for this use case are:

cas.authn.attributeRepository.expirationTime=60
cas.authn.attributeRepository.expirationTimeUnit=seconds
cas.authn.attributeRepository.merger=multivalued

cas.personDirectory.attributeResolutionEnabled=false

Our service definition matches the following:

{
  "@class" : "org.apereo.cas.services.RegexRegisteredService",
  "serviceId" : "https://app.example.org",
  "name" : "ExampleApplication",
  "id" : 1,
  "attributeReleasePolicy" : {
    "@class" : "org.apereo.cas.services.ReturnAllAttributeReleasePolicy",
    "principalAttributesRepository" : {
      "@class" : "org.apereo.cas.authentication.principal.DefaultPrincipalAttributesRepository",
      "attributeRepositoryIds": ["java.util.HashSet", [ "myjson" ]]
    }
  }
}

Since caching is disabled, you can change the underlying attribute value in the JSON attribute repository and CAS should pick up the change and release the new attribute value to the example application in about 60 seconds.

3

The requirements for this use case are as follows:

  • Disable attribute resolution from external attribute repositories via Person Directory.
  • Resolve attributes at release time for a given application only using the MyJson attribute repository.
  • Turn on global caching of attributes for 5 seconds and service-level caching for 30 seconds.

The relevant properties for this use case are:

cas.authn.attributeRepository.expirationTime=5
cas.authn.attributeRepository.expirationTimeUnit=seconds
cas.authn.attributeRepository.merger=multivalued

cas.personDirectory.attributeResolutionEnabled=false

Our service definition matches the following:

{
  "@class" : "org.apereo.cas.services.RegexRegisteredService",
  "serviceId" : "https://app.example.org",
  "name" : "ExampleApplication",
  "id" : 1,
  "attributeReleasePolicy" : {
    "@class" : "org.apereo.cas.services.ReturnAllAttributeReleasePolicy",
    "principalAttributesRepository" : {
      "@class" : "org.apereo.cas.authentication.principal.cache.CachingPrincipalAttributesRepository",
      "duration" : {
        "@class" : "javax.cache.expiry.Duration",
        "timeUnit" : [ "java.util.concurrent.TimeUnit", "SECONDS" ],
        "expiration" : 30
      },
      "attributeRepositoryIds": ["java.util.HashSet", [ "myjson" ]]
    }
  }
}

Attributes fetched and released for this example application may be updated after 30 seconds, while the global cache attempts to expire and resolve attributes for other applications after 5 seconds.

4

The requirements for this use case are as follows:

  • Enable attribute resolution from external attribute repositories via Person Directory.
  • However, let CAS resolve attributes from our StaticStub attribute repository.
  • Resolve attributes at release time for a given application only using the MyJson attribute repository.
  • Combine all attributes into one collection as multi-valued attributes where necessary.
  • Turn on global caching of attributes for 30 seconds.

The relevant properties for this use case are:

cas.authn.attributeRepository.expirationTime=30
cas.authn.attributeRepository.expirationTimeUnit=seconds
cas.authn.attributeRepository.merger=multivalued

cas.personDirectory.attributeResolutionEnabled=true
cas.personDirectory.activeAttributeRepositoryIds=StaticStub

Our service definition matches the following:

{
  "@class" : "org.apereo.cas.services.RegexRegisteredService",
  "serviceId" : "https://app.example.org",
  "name" : "ExampleApplication",
  "id" : 1,
  "attributeReleasePolicy" : {
    "@class" : "org.apereo.cas.services.ReturnAllAttributeReleasePolicy",
    "principalAttributesRepository" : {
      "@class" : "org.apereo.cas.authentication.principal.DefaultPrincipalAttributesRepository",
      "attributeRepositoryIds": ["java.util.HashSet", [ "myjson" ]],
      "mergingStrategy" : "MULTIVALUED"
    }
  }
}

5

The requirements for this use case are identical to the one above. The only difference is, we are going to ignore attributes resolved at authentication time from our StaticSub attribute repository for the example application and only hit our selected attribute repository, MyJson.

Our service definition matches the following:

{
  "@class" : "org.apereo.cas.services.RegexRegisteredService",
  "serviceId" : "https://app.example.org",
  "name" : "ExampleApplication",
  "id" : 1,
  "attributeReleasePolicy" : {
    "@class" : "org.apereo.cas.services.ReturnAllAttributeReleasePolicy",
    "principalAttributesRepository" : {
      "@class" : "org.apereo.cas.authentication.principal.DefaultPrincipalAttributesRepository",
      "attributeRepositoryIds": ["java.util.HashSet", [ "myjson" ]],
      "ignoreResolvedAttributes": true,
      "mergingStrategy" : "MULTIVALUED"
    }
  }
}

6

The requirements for this use case are as follows:

  • Enable attribute resolution from external attribute repositories via Person Directory.
  • However, let CAS resolve attributes from our StaticStub attribute repository.
  • Resolve attributes at release time for a given application only using the MyJson attribute repository.
  • Combine all attributes into one collection as multi-valued attributes where necessary.
  • Turn on global caching of attributes for 30 seconds, and service-level caching for 30 minutes.

The relevant properties for this use case are:

cas.authn.attributeRepository.expirationTime=10
cas.authn.attributeRepository.expirationTimeUnit=seconds
cas.authn.attributeRepository.merger=multivalued

cas.personDirectory.attributeResolutionEnabled=true
cas.personDirectory.activeAttributeRepositoryIds=StaticStub

Our service definition matches the following:

{
  "@class" : "org.apereo.cas.services.RegexRegisteredService",
  "serviceId" : "https://app.example.org",
  "name" : "ExampleApplication",
  "id" : 1,
  "attributeReleasePolicy" : {
    "@class" : "org.apereo.cas.services.ReturnAllAttributeReleasePolicy",
    "principalAttributesRepository" : {
      "@class" : "org.apereo.cas.authentication.principal.cache.CachingPrincipalAttributesRepository",
      "duration" : {
        "@class" : "javax.cache.expiry.Duration",
        "timeUnit" : [ "java.util.concurrent.TimeUnit", "MINUTES" ],
        "expiration" : 30
      },
      "attributeRepositoryIds": ["java.util.HashSet", [ "myjson" ]]
    }
  }
}

So…

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 know that all other use cases, scenarios, features and theories certainly are possible as well. Feel free to engage and contribute as best as you can.

Happy Coding,

Misagh Moayyed

Related Posts

CAS 6.1.0 RC3 Feature Release

...in which I present an overview of CAS 6.1.0 RC3 release.

Apereo CAS - Webflow Decorations

Learn how you may decorate the Apereo CAS login webflow to inject data pieces and objects into the processing engine for display purposes, peace on earth and prosperity of all mankind, etc. Mainly, etc.

Apereo CAS - SAML2 Metadata Query Protocol

Learn how you may configure Apereo CAS to fetch and validate SAML2 metadata for service providers from InCommon's MDQ server using the metadata query protocol.

Saving Time is Time Consuming

May you live in the best of times. May you live in the startup times.

Apereo CAS 6.1.x - Credential Caching & Proxy AuthN

Learn how you may configure Apereo CAS to capture and cache the credential's password and the proxy-granting ticket in proxy authentication scenarios, pass them along to applications as regular attributes/claims. We will also be reviewing a handful of attribute release strategies that specifically affect authentication attributes, conveying metadata about the authentication event itself.

Apereo CAS 6.1.x - Building CAS Feature Modules

An overview of how various CAS features modules today can be changed and tested from the perspective of a CAS contributor working on the codebase itself to handle a feature request, bug fix, etc.

CAS 6.1.0 RC2 Feature Release

...in which I present an overview of CAS 6.1.0 RC2 release.

Apereo CAS - Riffing on Attribute Release Policies

Learn how to release the kraken of attributes to CAS clients, relying parties and service providers using a variety of attribute release policies and authentication protocols, sampled and collected here to fun and profit.

Apereo CAS - Delegated Authentication to SAML2 Identity Providers

Learn how your Apereo CAS deployment may be configured to delegate authentication to an external SAML2 identity provider.

Apereo CAS - Custom Login Fields w/ Dynamic Bindings

Learn how to extend the Spring Webflow model to add custom fields to the CAS login form and the authentication process and take advantage of the additional user-provided data in customized authentication handlers.