Overview
The ModuleConfiguration page discusses the general idea behind IdP Modules and how they're used by deployers. This page describes the concept technically, explains how the system's built-in modules are laid out, and explains how to expose them from third-party plugins, which is very straightforward.
An IdPModule is an implementation of the titular Java interface that exposes information about a module to the system and provides very basic operations to enable and disable the module. The CLI uses the interface to perform that work and the information returned to report results.
Generally speaking, modules tend to manage at least one "resource". While that is not technically required, in Spring terms it's unlikely that any feature can be on or off independently of whether it's wired into the configuration or not. So most modules exist solely to expose an interface to create, update, and remove specific resources that are needed for the module to work. Resources are typically XML files, property files, and views, and they usually are pulled from the classpath from within implementation jars and are usually copied to the filesystem.
An IdPModule is found through the Java Services API, which relies on a special file in META-INF to identify the possible implementations of an interface. The tools that operate on IdPModules use the ServiceLoader class to locate the implementations and instantiate the interface to perform work. There are no other inherent restrictions on how modules may be implemented, but there are classes provided that simplify the work involved to simply defining a few properties and embedding various files in a jar on the IdP's classpath.
Resource Management
Being that the core purpose of modules is to manage configuration resources, most of the module system's behavior comes from the way it manages configuration resources, which is derived from RPM's config(replace) and config(noreplace) concepts that govern how installing, updating, and removing resources happens. All files are config(noreplace) by default, with the option to mark them as config(replace).
We have NOT implemented, nor will we implement, actual scripting hooks, which is where RPM becomes more powerful but also goes badly sideways dealing with upgrades because of script ordering.
As such, enabling and disabling modules are idempotent and do not depend on existing state, and upgrades are simply handled by enabling again.
For a Module derived from the AbstractIdPModule class, the following outline describes what happens for example.xml during enable (updates are the same):
- If noreplace:
- If the file already exists:
- If the old content is the same as the new content, nothing happens.
- Else the new content is copied to example.xml.idpnew (previous versions of example.xml.idpnew are overwritten).
- Else the new content is copied to example.xml
- If the file already exists:
- Else
- If the file already exists:
- If the old content is the same as the new content, nothing happens.
- Else the new content is copied to example.xml and the old content is copied to example.xml.idpsave (failing the enable operation if this file exists).
- If the file already exists:
For disable:
- If the file exists:
- If the clean option is supplied or the old content is the same as the default content, then the file is removed.
- Else the old file is copied to example.xml.idpsave (overwriting previous versions).
- Else nothing happens.
The basis for comparison is a SHA-1 hash of the content.
Third-Party Modules
To expose a module, at least one new jarfile must be incorporated into the IdP's classpath containing an implementation of the IdPModule interface and the necessary services file identifying it in META-INF/services/net.shibboleth.idp.module.IdPModule
That's technically all that is required in absolute terms. Practically speaking, most modules will be based on the PropertyDrivenIdPModule API class and will be implemented via a very short Java class, a module.properties file, and any additional resources the module manages.
Property Reference
When using the PropertyDrivenIdPModule class to implement modules, all of the module description takes the form of a Java properties file. The "root" property for a module has the name of the implementation class of the module, and the value is the module's ID, or unique key. Module naming should rely on Java package syntax for uniqueness, but need not match actual Java packages or classes.
All other property names are prefixed by the module ID, followed by a period (.), and then a property suffix. The following suffixes are supported. Only "name" is required.
Suffix | Description |
---|---|
name | A human-readable but brief name |
desc | A longer, human-readable description |
url | A URL for more information or documentation |
postenable | A message to display to a deployer that enables the module |
postdisable | A message to display to a deployer that disables the module |
n.src | Classpath or HTTP/HTTPS resource location for resource N managed by the module |
n.dest | Filesystem location relative to idp.home for resource N to be placed when enabled |
n.replace | A true/false value indicating whether a resource should directly overwrite an existing copy of itself. Default is false. |
The final three properties are a repeating pair (src/dest) for each resource managed by the module, with replace being optional. Resources are numbered starting at 1 and increasing atomically up to the last resource managed (no gaps allowed). Sources are generally classpath-based and should be absolute to ensure proper lookup. Destinations are filesystem paths relative to the IdP's install tree.
While HTTP resources are technically supported, this is not advisable for security reasons. Generally speaking, plugins should install remote files and modules should enable them based on local classpath lookup after installation.
Example
The following approach and layout is strongly suggested.
META-INF/ services/ net.shibboleth.idp.module.IdPModule (property file) org/ example/ module/ impl/ Example.java (module implementation) module.properties (property file) conf/ example.xml (a Spring configuration file, optional) views/ example.vm (a Velocity view, optional) edit-webapp/ example.jsp (a JSP page, optional)
Given the above example, the required files have content as follows:
Assuming the (optional) resources noted above in the original layout, the module definition properties would be:
Core IdP Modules
The IdP modules that are part of the core software release are implemented inside of the idp-conf-impl Maven module as subtypes of the CoreIdPModule implementation class. This allows the module "url" property to be relative to the proper IdP wiki space (IDP4 initially). All modules are implemented in classes in impl packages and are assigned IDs that are prefixed with "idp.", further subdivided by functional area. The layout is as follows:
META-INF/services/net.shibboleth.idp.module.IdPModule (Java Service registry file) net/shibboleth/idp/module/admin/impl/*.java (Admin Module implementation classes) net/shibboleth/idp/module/admin/impl/module.properties (Admin Module definitions) net/shibboleth/idp/module/authn/impl/*.java (Authentication Module implementation classes) net/shibboleth/idp/module/authn/impl/module.properties (Authentication Module definitions) net/shibboleth/idp/module/conf/* (Managed resources that go into idp.home/conf/ in a parallel layout) net/shibboleth/idp/module/edit-webapp/* (Managed resources that go into idp.home/edit-webapp/ in a parallel layout) net/shibboleth/idp/module/intercept/impl/*.java (Interceptor Module implementation classes) net/shibboleth/idp/module/intercept/impl/module.properties (Interceptor Module definitions) net/shibboleth/idp/module/views/* (Managed resources that go into idp.home/views/ in a parallel layout)
Note that the other content inside net/shibboleth/idp/ is outside the module "sphere" and are generally the internal system resources that used to reside in the idp.home/system/ folder.
Project Plugin Modules
Modules packaged within plugins produced by the Shibboleth Project should implement them based on the PluginIdPModule implementation class, a special case of depending on an implementation class in a separate module. This is strictly to "hide" the class from third parties. This allows the module "url" property to be relative to the proper wiki space (TBD initially).