The Config Interface
The Config
interface of a node contains the configuration values required by a particular node instance.
Note that you do not need to write a class that implements the interface you define, AM will create it automatically, as required.
Define the properties the node will use in the Config
interface, by using the @Attribute
annotation. The @Attribute
annotation specifies the order of the properties in the tree designer view as well as providing a way to specify additional validators.
Example:
public interface Config { @Attribute(order = 1) String domain(); @Attribute(order = 2, validators = RequiredValueValidator.class) boolean isVerificationRequired(); @Attribute(order = 3, validators = RequiredValueValidator.class) @Password char[] clientSecret(); @Attribute(order = 4) default YourCustomEnum action() { return YourCustomEnum.LockScreen; }; }
The | |
A boolean attribute with an additional parameter, | |
Use the Password annotation to mask the input characters and encrypt the value of the attribute. | |
A custom enum attribute. This provides type safety and negates the misuse of Strings as generic type-unsafe value holders. The UI will correctly handle the enum and only let the tree administrator chose from the defined enum values. |
The defined properties appear as configurable options in the tree designer view when adding a node of the relevant type. For example the configuration for the Scripted Decision Node appears as follows:
Note that attribute names are used when localizing the node's text, see Internationalization.
For more information, see the Config annotation type and the Attribute annotation type in the AM 7.1.4 Public API Javadoc.
Sharing Configuration Between Nodes
You can share configuration between nodes that have common properties. For example, a number of your nodes may call out to an external service that requires a username, password, IP address, and port setting.
Rather than repeat the same configuration in each of these nodes, you can create a shared, auxiliary service to hold the common properties in one of your nodes, and reference that service from multiple other nodes.
The following sections explain how to create this auxiliary service and reference it in your nodes. Also covered is how to run more than one instance of an auxiliary service if required, and how to obtain the configuration from services built-in to AM.
Allowing Multiple Instances of an Auxiliary Service
To enable configuration of 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 instances of the service 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 { @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 InjectorHolder.getInstance(AnnotatedServiceRegistry.class) .getRealmInstances(MyAuxService.class, Realms.of(realmName)) .stream() .collect(Collectors.toMap(MyAuxService::id, MyAuxService::id)); } 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); } } } // ... }
Obtaining Configuration of Services Built-in to AM
You can obtain configuration from services built-in to AM. For example, you might want to access the Email Service configuration to obtain the SMTP settings for the realm.
AM services are defined by two methods:
Most services are defined by using an annotated interface.
Legacy services that are defined by using an XML file.
See the following sections for information on obtaining the configuration from services defined with either of the two methods.
Obtaining the Configuration from an Annotated Service
To obtain the configuration from a service that uses an annotated interface, add org.forgerock.openam.sm.AnnotatedServiceRegistry
to your Guice constructor. If the configuration is realm-based, include the realm in the constructor, as follows:
public class MyCustomNode extends SingleOutcomeNode { private final AnnotatedServiceRegistry serviceRegistry; private final Realm realm; @Inject public MyCustomNode(@Assisted Realm realm, AnnotatedServiceRegistry serviceRegistry) { this.realm = realm; this.serviceRegistry = serviceRegistry; } // ... }
Obtain an instance of the service using one of the get methods of AnnotatedServiceRegistry
in the constructor.
If the calls you make depend on input from elsewhere in the tree you can add AnnotatedServiceRegistry
to the process
method. Note that the following example assumes that a previous node has stored the ID of the AM service to use in shared state:
public Action process(TreeContext context) throws NodeProcessException { String serviceId = context.getState.get("myAuxServiceId"); MyAuxService instance = serviceRegistry.getRealmInstance(MyAuxService.class, realm, serviceId); // ... }
Obtaining the Configuration from a Legacy Service
To obtain an instance of the configuration from a legacy service, use the APIs in the com.sun.identity.sm
package.
For example, to obtain the configuration values from a realm instance of a service, use ServiceConfigManager
as follows:
ServiceConfigManager scm = new ServiceConfigManager("legacyServiceName", token); ServiceConfig sc = scm.getOrganizationConfig(realm.asPath(), null); final Map<String, Set<String>> configMap = sc.getAttributes();
However, to obtain the configuration values from a global instance of a service, use ServiceSchemaManager
as follows:
ServiceSchemaManager ssm = new ServiceSchemaManager("legacyServiceName", getAdminToken()); Map<String, Set<String>> configMap = ssm.getGlobalSchema().getAttributeDefaults();