Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Table of Contents

Overview

Plugins are a mechanism to securely introduce function into an IdP in a manner which does not require lockstep versioning with the IdP. They do not include, by themselvesany configuration, but part of the functionality that they include can be (and usually is) one or more Modules.

...

Formally, a plugin consists of four files which are usually made available via a fixed URL so as to allow for easy installation

  • A tar.gz file

  • A zip file

  • GPG signatures for the above

The layout of the tar or zip files is fixed.  A single top level folder may have any name and contains two sub folders:

  • webapp

    • Contains the contents which will be included into the war. There must be at least one jar file contained inside webapp\WEB-INF\lib  and the jar files in that folder must implement exactly one service of type net.shibboleth.idp.plugin.IdPPlugin

  • bootstrap

    • This folder:

      • MUST contain a file called plugin.properties.

        • This should be a property file with at least one property, "plugin.id".

        • The ID is used to locate the default truststore that verifies the GPG signature. The trust store will reside within a folder, named after the plugin ID, nested inside the IdP's credentials folder, e.g., idp.home/credentials/<pluginid>/truststore.asc

        • It must match the plugin ID that the plugin identifies itself with. By convention, plugin IDs are associated with the package name of the plugin to ensure uniqueness.

      • MAY contain a file called keys.txt  being the public key(s) used to sign the distribution. This enables the plugin installer to perform a one-time, "leap of faith" credentials check, and thereafter to prevent use of an unauthorized key.

Building a Plugin

Given the constraints above you can use any mechanism you chose to build a plugin. However the format of the archive (and its signature) is conducive to being created automatically by maven and the IdP distribution contains support classes for constructing the service.

...

The easiest way to construct a plugin service is to build a class derived from the PropertyDrivenIdPPlugin class. This uses a property file to implement the IdPPlugin interface.

Info
title

Shibboleth Developers

Shibboleth developers ONLY use the subclass FirstPartyIdPPlugin which references the Shibboleth "standard property" file location that manages plugin compatibility metadata (https://shibboleth.net/downloads/identity-provider/plugins/plugins.properties), which is the only file in the java-idp-plugin-mgmt  project.

  • Build the implementing class by deriving it from the PropertyDrivenIdPPlugin class and supply the class as the parameter to the constructor.

The Service definition
Code Block
languagejava
titleThe Service definition
package org.example;
public class ExamplePlugin extends PropertyDrivenIdPPlugin {
    public ExamplePlugin () throws IOException, PluginException {
        super(ExamplePlugin .class);
    }
}
  • Declare the class as a service by creating the /META-INF/services/net.shibboleth.idp.plugin.IdPPlugin file.

...

...

 /META-INF/services/net.shibboleth.idp.plugin.IdPPlugin
Code Block
languagetext
org.example.ExamplePlugin 
  • Create the properties file used to implement the plugin service. This is a file in the same package as the implementing class and has the name plugin.properties 

Code Block
plugin.id = org.example.ExamplePlugin
# Only used when package manifest is not available, such as within Eclipse
plugin.version = 1.0.0

The full list of properties is:

Property

Required

What

Description

plugin.id

Y

Identifier

This is the unique identifier by which the plugin is described. It is used when manipulating the plugin (for instance doing an update).

NOTE
This file name and property name are purposely the same as required in the bootstrap location of the distribution. This enables one file to be used. See below.

plugin.version


two or three dot-separated numbers

This is current the version of the plugin. If configured correctly, the PropertyDrivenIdPPlugin class will use the version derived from the manifest of the containing jar to provide the version. This is a fallback if the manifest cannot be located.

plugin.license


location (in a jar file) of a text file

This is the (in jar) location of a file containing any licensing details. It is used only when the deployer requests it, and satisfies the spirit of providing the relevant licenses for a plugin.


     plugin.sh --license org.example.shibbolethplugin.ExamplePlugin


plugin.url.0
plugin.url.1
...

Y

URL

These are the URLs where the plugin compatibility property files (below) are located.  Multiple locations may be specified to allow for redundancy.

plugin.modules.required


Space delimited list

The names of any modules which need to be enabled before the plugin installer will install this plugin. This is the only sense in which plugins may "depend" on other plugins; it's the module(s) exposed by those plugins that are identified as required.

Constructing the Implementation Project

In constructing the POM for the implementation project, you need to pay attention to three aspects: dependency dependency management, dependency collection (for use in building the package) and managing the version. The Plugin Archetype may help.

Dependency Management

Any code you rely on from the IdP should be scoped "provided". You should avoid dependencies on impl modules.

...

Note

it is critical that the <name>  be the path to your plugin's package and that it be terminated with a /

Code Block
languagexml
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <configuration>
                    <archive>
                        <manifestEntries>
                            <Automatic-Module-Name>${automatic.module.name}</Automatic-Module-Name>
                        </manifestEntries>
                        <manifestSections>
                            <manifestSection>
                                <name>org/example/</name>
                                <manifestEntries>
                                    <Implementation-Title>${project.artifactId}</Implementation-Title>
                                    <Implementation-Version>${project.version}</Implementation-Version>
                                    <Implementation-Vendor>example.org</Implementation-Vendor>
                                </manifestEntries>
                            </manifestSection>
                        </manifestSections>
                    </archive>
                </configuration>
            </plugin>

Anchor
distribution
distribution
Constructing the Distribution Project

The distribution project is a simple "pom" package with one plugin - the assembly plugin to create the zip and tar.gz files.

Distribution Pom example
Code Block
languagexml
titleDistribution Pom example
<project>
    <modelVersion>4.0.0</modelVersion>

    <name>.....</name>
    <artifactId>.....</artifactId>
    <packaging>pom</packaging>

    <description>
        A project to 
    </description>

    <properties>
        <plugin.finalName>.....-${project.version}</nashorn.finalName>
    </properties>

    <build>
        <plugins>
            <!-- Assemble -->
            <plugin>
                <artifactId>maven-assembly-plugin</artifactId>
                <executions>
                    <execution>
                        <id>make-assembly</id>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <appendAssemblyId>false</appendAssemblyId>
                    <descriptors>
                        <descriptor>src/main/assembly/plugin-assembly-tgz.xml</descriptor>
                        <descriptor>src/main/assembly/plugin-assembly-zip.xml</descriptor>
                    </descriptors>
                    <finalName>${plugin.finalName}</finalName>
                    <tarLongFileMode>gnu</tarLongFileMode>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

The work is all done in the assembly files.  This collects together at several groups of files into the package

  • The webapp directory gets:

    • The jar file created by the implementation project

    • The dependencies assembled by the implementation project

    • Optionally, any other files which may need to do into the idp.war file

  • The bootstrap directory gets:

    • The shared property file

    • Optionally, the file of keys associated with signing this distribution

The following example build the tar.gz. The zip version is similar, but may decide to preserve line endings for any rare text files.

xml
Code Block
language
Expand
titletar.gz assembly file
Code Block
languagexml
<assembly>
    <id>assembly-tgz</id>
    <formats>
        <format>tar.gz</format>
    </formats>
    <fileSets>
        <fileSet>
            <!--  Our impl -->
            <directory>../impl/target</directory>
            <outputDirectory>webapp/WEB-INF/lib</outputDirectory>
            <includes>
                <include>XYZ-plugin-*.jar</include>
            </includes>
            <excludes>
                <exclude>*test*.jar</exclude>
                <exclude>*javadoc.jar</exclude>
                <exclude>*sources.jar</exclude>
            </excludes>
        </fileSet>
        <fileSet>
            <!--  Our dependencies -->
            <directory>../impl/target/dependency</directory>
            <outputDirectory>webapp/WEB-INF/lib</outputDirectory>
            <includes>
                <include>*.jar</include>
            </includes>
            <excludes>
                <exclude>*test*.jar</exclude>
                <exclude>*javadoc.jar</exclude>
                <exclude>*sources.jar</exclude>
            </excludes>
        </fileSet>
        <fileSet>
            <!--  The shared property file -->
            <directory>../impl/target/classes/org/example</directory>
            <outputDirectory>bootstrap</outputDirectory>
            <includes>
                <include>plugin.properties</include>
            </includes>
        </fileSet>
        <fileSet>
            <!--  The signing keys -->
            <directory>src/main/resources/bootstrap</directory>
            <outputDirectory>bootstrap</outputDirectory>
            <includes>
                <include>keys.txt</include>
            </includes>
        </fileSet>
    </fileSets>
</assembly>

Maintaining the Update Property File

An important motivation for plugin development is avoiding lockstep deployment with IdP versions. Further it is important that a plugin's IdP depenedencies can change over time. The information which describes a plugin's state and dependency is therefore not included in the plugin; rather it contains a URL. The URL points to a property file which contains information about the plugin and its versions and their IdP compatibility.

The plugin installer consults this URL to list all possible versions and their requirements (plugin -fl plugin-id) but also (and more importantly) to select and automatically download the latest version of the plugin for the current IdP version (plugin -u plugin-id)

Obviously this information can be changed at any time, allowing plugins to be retired or have their life extended.

One file may document multiple plugins and (of course) multiple versions of the plugin, so all property names are prefixed by the plugin ID and per-version properties are postfixed by the version.

...

The following properties should be provided:

Name

What

Description

PluginId.versions                

Space separated list of versions (three dot-separated numbers)

The versions of the plugin which are available. 

PluginId.downloadURL.<version>

A URL

The "directory" part of the the location of this version of this plugin.

PluginId.baseName.<version>

A file name

The "root" name of the four files which make up the plugin. The downloadURL is prepended and the four followig extension appended:

  1. .tar.gz

  2. .tar.gz.asc

  3. .zip

  4. .zip.asc

PluginId.idpVersionMax.<version>

An IdP version (three dot-separated numbers)

The first IdP version that this version of this plugin will not work with.
(exclusive)

PluginId.idpVersionMin.<version>

An IdP version (three dot-separated numbers)

The first IdP version that this version of this plugin will work with.
(inclusive)

PluginId.supportLevel.<version>

One of

  • Current

  • OutOfDate

  • Unsupported

  • Secadv

  • Withdrawn

A description of the current status of this version of this plugin.

The only semantic associated is that the plugin verb will only consider plugins with support level of Current for automatic update.

Wildcarding the Version

The dowloadURL and baseName for versions managed by a Nexus repository are usually predictable given the version number. Hence a simplified wildcarding is allowed for these two property classes.

...