Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

There are some basic design and coding concepts to be aware of in developing extensions.

Table of Contents
minLevel2

Project Structure and API Boundary

The software is made up of several independent projects that are in theory managed and versioned independently; in practice, they all tend to be released in tandem with new IdP releases. The important projects and their internal organization are:

  • java-parent-project (a Maven parent POM)

  • java-support (single module, contains low level supporting classes)

  • spring-extensions (single module, contains Spring support classes)

  • java-opensaml (multi-module, contains a lot of core code for XML processing, security processing, messaging and profile abstractions, SAML function, storage, and generally anything without Spring dependencies)

  • java-identity-provider (multi-module, everything else, configuration handling, packaging, installer code, and all attribute-related functionality)

  • java-oidc-common (multi-module, supplies shared OIDC libraries and supporting code for OIDC plugins)

You should read, and thoroughly understand, the Java Product Version Policy, as it governs the rules you must follow if you want to guarantee a stable extension life cycle, and in particular if you intend to target multiple versions of the IdP with your extension.

...

As the policy states, all of the OpenSAML and IdP modules that end in -api are part of the public API and all of the rest of the modules are not part of the API with the following exceptions that are mostly part of the public API despite the names:

  • opensaml-core

  • idp-core

  • idp-schema (this contains no code, but the XML schemas are part of the public API)

  • idp-ui

The exception to the above list is any class within a package containing "impl" as a segment.

...

You will frequently encounter component interfaces that provide some basic lifecycle management for beans, principally:

  • net.shibboleth.utilities.java.support.component.InitializableComponent

  • net.shibboleth.utilities.java.support.component.DestructableComponent

Typically these interfaces are wired to Spring's lifecycle support, particularly so that beans created by Spring will be initialized explicitly separately from the object's constructor, allowing properties to be set first. You should generally take advantage of that support and implement that interface when creating stand alone beans.

...

Managing this capability requires the use of a pair of low-level interfaces:

  • net.shibboleth.utilities.java.support.service.ReloadableService<T>

  • net.shibboleth.utilities.java.support.service.ServiceableComponent<T>

Services that implement these related interfaces require a specific interaction pattern. The ReloadableService is the injectable type from a DI perspective. Accessing the underlying service API (which is represented by T above) involves the getServiceableComponent() method, which returns the ServiceableComponent wrapper for the service API, in a so-called "pinned" state, meaning it is locked and accessible. When finished, the unpinComponent() method is called (usually in a finally clause) to release the lock.

...

A core concept in the "meat" of the IdP, and in OpenSAML's messaging code, is the notion of a context. All contexts implement the BaseContext interface and are simply Java beans that store state relevant to an operation.

Within the IdP, contexts are arranged in a tree structure with the root generally represented by the ProfileRequestContext class. Hanging off of this root node are contexts related to other units of state (e.g., session information, attribute information, relying party information). Child nodes may themselves have children but we try and keep the tree relatively shallow and wide. So, you end up with something like this:

...

This approach has a number of benefits:

  • it provides strongly-typed access to data (as opposed to just using a map, which is really all these context implementations are)

  • new contexts can be added to the tree in the future (by new code or extensions) without disrupting existing code

  • existing contexts can be extended without disrupting existing code

  • different subsystems can operate on parts of the tree without concerning themselves with the rest of it

However this approach does have some drawbacks as well:

  • developers need to document what they require from the tree and what they add to the tree (and thus what other code can depend on)

  • documenting the tree isn't something that lends itself to typical API documentation approaches and we haven't really come up with anything for that as of yet, apart from simple diagrams and prose

  • it's a bit of a learning curve for developers

Persistence

Most requirements for persistence should, if possible, be met using the org.opensaml.storage.StorageService interface. This is a thread-safe API for storing, updating, reading, and deleting records that uses a very particular and precise API contract that individual use cases generally need to adapt themselves to, sometimes with a fair degree of creativity. In return, you can essentially ignore the details of how the data will be stored. Interfaces are also provided to interrogate the service's capabilities if you need to make sure that you can store keys and data of a certain size.

You will often find that using this API will be less than optimal in terms of performance or code design, but the advantages for deployers are substantial, and we urge people to exhaust every last possible trick to avoid creating a dedicated persistence solution specific to an extension. We already have one such case (storing SAML persistent IDs) and we want to avoid seeing more. The chances of any extension being accepted into the code base are close to zero if it implements its own persistence.

Spring Configuration

Out of the box, the IdP allows injection of beans from files resident on the classpath.  This allows you to initialize your system.  Two locations are supported

  • /META-INF/net.shibboleth.idp/preconfig.xml will be loaded before the IdP configuration

  • /META-INF/net.shibboleth.idp/postconfig.xml will be loaded after the IdP configuration.

Spring WebFlow Configuration

Refer to the SpringConfiguration documentation section on webflows for a summary of how you can register flows from extension libraries at runtime.

...

Plugins

Plugins are the unit of installation. They can be revised out of lockstep from the IdP. The sole task of a plugin is to inject jarfiles into the IdP's webapp in a controlled way. To ensure security, plugin distributions have to be signed. Plugin development is described here.

...