Create shared auxiliary services
A shared auxiliary service is a custom service you load through the plugin system. This allows you to use the same custom service in multiple nodes to improve consistency and maintainability.
A custom service is useful when you need to perform the same task from multiple nodes or share configuration between nodes. For example, you could use a shared auxiliary service to integrate with an external API or share common node configuration between nodes rather than repeating the same configuration in each node.
The following sections explain how to create and reference a shared auxiliary service, how to run more than one instance of an auxiliary service if required and how to get the configuration from services built-in to AM.
Create a shared auxiliary service
You can create a shared auxiliary service in the configuration interface defined as part of a node.
Annotate the service with the org.forgerock.openam.annotations.sm.Config
annotation
to describe how the service functions.
Specify the scope of the service, either GLOBAL
or REALM
, as shown below:
@Config(scope = Config.Scope.REALM)
public interface MyAuxService {
@Attribute(order = 1)
String serviceUrl();
}
You can also specify other features of the service, such as whether the service is a singleton in its scope, or if it can have multiple instances. You can find information about supporting multiple instances in Allow multiple instances of an auxiliary service.
Reference a shared auxiliary service instance
To access the shared auxiliary service,
add org.forgerock.openam.sm.AnnotatedServiceRegistry
to the @Inject
-annotated constructor of the node.
Obtain the instance using the get instance methods on that class, for example:
serviceRegistry.getRealmSingleton(MyAuxService.class, realm)
Reinstall a shared auxiliary service instance
When developing a custom authentication node that references a shared auxiliary service, it can be useful for the node to be able to remove and reinstall the auxiliary service during upgrade to clear any existing configuration.
In the upgrade
function of your plugin class, use the following example code to remove and reinstall a service:
public void upgrade(String fromVersion) throws PluginException {
SSOToken adminToken = AccessController.doPrivileged(AdminTokenAction.getInstance());
if (fromVersion.equals(PluginTools.DEVELOPMENT_VERSION)) {
ServiceManager sm = new ServiceManager(adminToken);
if (sm.getServiceNames().contains("MyAuxService")) {
sm.removeService("MyAuxService", "1.0");
}
pluginTools.install(MyAuxService.class);
}
}
You can find information on upgrading custom authentication nodes in Upgrade nodes and change node configuration.
Allow multiple instances of an auxiliary service
To enable configuring multiple instances of the auxiliary service in either the same realm or at a global level,
set the collection
attribute to true
in the Config
annotation.
You can present the names of the service instances as a drop-down menu to the tree administrator.
To be able to present the names, make sure the service instance exposes its id
as follows:
@Config(scope = Config.Scope.REALM, collection = true)
public interface MyAuxService {
@Id
String id();
@Attribute(order = 1)
String serviceUrl();
}
Change the nodes that will be using a service instance to store the id
it uses,
and implement choiceValuesClass
as shown below:
public class MyCustomNode implements Node {
public interface Config {
@Attribute(order = 1, choiceValuesClass = ExternalServiceValues.class)
String serviceId();
}
public static class ExternalServiceValues extends ChoiceValues {
AnnotatedServiceRegistry annotatedServiceRegistry;
@Injected
ExternalServiceValues(AnnotatedServiceRegistery annotatedServiceRegistry) {
this.annotatedServiceRegistry = annotatedServiceRegistry;
}
@Override
public Map<String, String> getChoiceValues() {
return getChoiceValues(null);
}
@Override
public Map<String, String> getChoiceValues(Map envParams) {
String realmName = "/";
if (envParams != null) {
realmName = (String) envParams.getOrDefault(Constants.ORGANIZATION_NAME, "/");
}
try {
return annotatedServiceRegistry
var instanceNames = configManager.getOrganizationConfig("realmName", null).getSubConfigNames()
.stream()
.collect(Collectors.toMap(s -> s, s -> s));
} catch (SSOException | SMSException | RealmLookupException e) {
LoggerFactory.getLogger("amAuth").error("Couldn't load realm {}", realmName, e);
throw new IllegalStateException("Couldn't load realm that was passed", e);
}
}
}
// ...
}
Get configuration of built-in services
You can get configuration from services built-in to AM. For example, access the Email Service configuration to get the SMTP settings for the realm.
AM services are defined by two methods:
-
An annotated interface (most services)
-
An XML file (legacy services)
To get an instance of the configuration from these services, use the APIs in the com.sun.identity.sm
package.
For example, to get configuration values from a realm instance of a service,
use ServiceConfigManager
as follows:
ServiceConfigManager scm = new ServiceConfigManager("serviceName", token);
ServiceConfig sc = scm.getOrganizationConfig(realm.asPath(), null);
final Map<String, Set<String>> configMap = sc.getAttributes();
To get configuration values from a global instance of a service, use ServiceSchemaManager
instead:
ServiceSchemaManager ssm = new ServiceSchemaManager("serviceName", getAdminToken());
Map<String, Set<String>> configMap = ssm.getGlobalSchema().getAttributeDefaults();