Node class
In Java terms, an authentication node is a class that implements the Node
interface,
org.forgerock.openam.auth.node.api.Node
.
The Node
class may access and modify the persisted state that is shared between the nodes within a tree,
and may request input by using callbacks. The class also defines the possible exit paths from the node.
The class is annotated with org.forgerock.openam.auth.node.api.Node.Metadata
.
The annotation has two main attributes - configClass
and outcomeProvider
.
Typically, the configClass
attribute is an inner interface in the node implementation class.
For simple use cases, the abstract implementations of the node interface,
org.forgerock.openam.auth.node.api.SingleOutcomeNode
and org.forgerock.openam.auth.node.api.AbstractDecisionNode
,
have their own outcome providers that can be used.
For more complex use cases you can provide your own implementation.
The sections that follow describe the Node
class.
Annotation
The annotation specifies the outcome provider and configuration class.
The outcome provider can use the default SingleOutcomeNode
or MultipleOutcomeNode
,
or a custom OutcomeProvider
can be created and referenced from the annotation.
See the Choice Collector node for an example of a custom outcome provider.
Config
The config interface defines the configuration data for a node. A node cannot have state, but it can have configuration data.
Note that you do not need to provide the implementation class for the Config
interface you define.
AM will create these automatically, as required.
An example is the Account Lockout node. The node can be configured to lock or unlock the users' account, based on it’s configuration.
Configuration is per-node. Different nodes of the same type in the same tree have their own configuration.
The config
interface configures values using methods.
To provide no default value to the tree administrator, provide the method’s signature but not the implementation.
To provide a default value to the tree administrator,
mark the method as default
and provide both a method and a value.
For example:
public interface Config {
//This will have no default value for the UI
@Attribute(order = 10)
String noDefaultAttribute();
//This will default to the value LOCK.
@Attribute(order = 20)
default LockStatus lockAction() {
return LockStatus.LOCK;
}
}
The Config
above would resemble the following in the tree designer view:
The @Attribute
annotation is required.
It can be configured with an order value, which determines the position of the attribute in the UI,
and with validators, to validate the values being set.
In the example above, a custom enum called LockStatus
is returned.
The options are displayed to the user automatically.
Constructor
Dependencies should be injected by using Guice as this makes it easier to unit test your node.
For example, you should accept the config
as a parameter.
You may also wish to obtain AM core classes, such as CoreWrapper
, instances of third-party dependencies,
or your own types.
@Inject
public AccountLockoutNode(CoreWrapper coreWrapper, @Assisted Config config)
throws NodeProcessException {
this.coreWrapper = coreWrapper;
this.config = config;
}
process
method
The process method takes a TreeContext
parameter, does some processing, and returns an Action object.
An action encapsulates changes to state and flow control.
The TreeContext
parameter is used to access the request, callbacks, shared state and other input.
The process
method is where state is retrieved and stored.
The returning Action
can be a response of callback to the user, an update of state, or a choice of outcome.
The choice of outcome in a simple decision node would be true
or false
,
resulting in the authentication tree flow moving from the current node to a node at the relevant connection.
Example implementation
The following example is the SetSessionPropertiesNode
class, taken from the Set Session Properties node:
package org.forgerock.openam.auth.nodes;
import java.util.Map;
import javax.inject.Inject;
import org.forgerock.openam.annotations.sm.Attribute;
import org.forgerock.openam.auth.node.api.Action;
import org.forgerock.openam.auth.node.api.Node;
import org.forgerock.openam.auth.node.api.SingleOutcomeNode;
import org.forgerock.openam.auth.node.api.TreeContext;
import org.forgerock.openam.auth.nodes.validators.SessionPropertyValidator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.inject.assistedinject.Assisted;
/**
* A node which contributes a configurable set of properties to be added to the user's session, if/when it is created.
*/
@Node.Metadata(outcomeProvider = SingleOutcomeNode.OutcomeProvider.class,
configClass = SetSessionPropertiesNode.Config.class) (1)
public class SetSessionPropertiesNode extends SingleOutcomeNode { (2)
/**
* Configuration for the node.
*/
public interface Config { (3)
/**
* A map of property name to value.
* @return a map of properties.
*/
@Attribute(order = 100, validators = SessionPropertyValidator.class)
Map<String, String> properties();
}
private final Config config;
private final Logger logger = LoggerFactory.getLogger("amAuth");
/**
* Constructs a new SetSessionPropertiesNode instance.
* @param config Node configuration.
*/
@Inject (4)
public SetSessionPropertiesNode(@Assisted Config config) {
this.config = config;
}
@Override
public Action process(TreeContext context) { (5)
logger.debug("SetSessionPropertiesNode started");
Action.ActionBuilder actionBuilder = goToNext();
config.properties().entrySet().forEach(property -> {
actionBuilder.putSessionProperty(property.getKey(), property.getValue());
logger.debug("set session property {}", property);
});
return actionBuilder.build();
}
}
1 | The @Node.Metadata annotation. See Metadata annotation. |
2 | Implementing the Node interface. See Node interface. |
3 | Implementing the Config interface. See Config interface. |
4 | Injecting the Node instance. See Inject objects into a node instance. |
5 | Creating an Action instance. See Action class. |