Node class
The Node class can access and modify the persisted state shared between the nodes within a tree,
and can request input by using callbacks. The class also defines the possible exit paths from the node.
In Java terms, an authentication node is a class that implements the Node interface,
org.forgerock.openam.auth.node.api.Node.
The UsernameCollectorNodeV2 class shows the steps to implement the Node interface:
package org.forgerock.openam.auth.nodes;
import static java.util.Objects.requireNonNull;
import static org.forgerock.openam.auth.node.api.Action.send;
import static org.forgerock.openam.auth.node.api.SharedStateConstants.USERNAME;
import java.util.ResourceBundle;
import javax.inject.Inject;
import javax.security.auth.callback.NameCallback;
import org.forgerock.openam.annotations.sm.Attribute;
import org.forgerock.openam.auth.node.api.Action;
import org.forgerock.openam.auth.node.api.InputState;
import org.forgerock.openam.auth.node.api.Node;
import org.forgerock.openam.auth.node.api.OutputState;
import org.forgerock.openam.auth.node.api.SingleOutcomeNode;
import org.forgerock.openam.auth.node.api.TreeContext;
import org.forgerock.openam.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.inject.assistedinject.Assisted;
/
* A node that collects a username from the user using a name callback.
*
* <p>Allows setting of a customizable, localizable prompt and a default value retrieved from shared state</p>
*
* <p>Places the result in the shared state as 'username'.</p>
/
@Node.Metadata(outcomeProvider = SingleOutcomeNode.OutcomeProvider.class, 1
configClass = UsernameCollectorNodeV2.Config.class,
tags = {"basic authn", "basic authentication"})
@Node.VersionMetadata(name = "UsernameCollectorNode", version = 2) 2
public class UsernameCollectorNodeV2 extends SingleOutcomeNode { 3
private final Config config; 4
@Inject 5
UsernameCollectorNodeV2(@Assisted Config config) {
this.config = config;
}
/
* Configuration for the username collector node V2.
*/
public interface Config { 6
/*
* If true, will prepopulate the username field with a value from shared state.
*
* @return Whether to prepopulate the username input field with a value from shared state
*/
@Attribute(order = 100)
default boolean prepopulate() {
return false;
}
}
private static final String BUNDLE = UsernameCollectorNodeV2.class.getName();
private final Logger logger = LoggerFactory.getLogger(UsernameCollectorNodeV2.class);
@Override 7
public Action process(TreeContext context) {
logger.debug("UsernameCollectorNode started");
var nameCallback = context.getCallback(NameCallback.class);
if (nameCallback.isPresent() && StringUtils.isNotEmpty(nameCallback.get().getName())) {
context.getStateFor(this).putShared(USERNAME, nameCallback.get().getName());
return goToNext().build();
}
return collectUsername(context);
}
private Action collectUsername(TreeContext context) {
logger.debug("collecting username");
ResourceBundle bundle = context.request.locales.getBundleInPreferredLocale(BUNDLE, getClass().getClassLoader());
var nameCallback = new NameCallback(bundle.getString("callback.username"));
var nodeState = context.getStateFor(this);
if (config.prepopulate() && nodeState.isDefined(USERNAME)) {
nameCallback.setName(requireNonNull(nodeState.get(USERNAME)).asString());
}
return send(nameCallback).build();
}
@Override
public OutputState[] getOutputs() {
return new OutputState[]{
new OutputState(USERNAME)
};
}
@Override
public InputState[] getInputs() {
return new InputState[]{
new InputState(USERNAME)
};
}
}
| Step | Description | Further information |
|---|---|---|
1 Apply the |
The Use an existing outcome provider such as |
|
2 Apply the |
Create a versioned node. The |
|
3 Implement the |
Extend one of the following abstract classes to implement the
Alternatively, write your own implementation of the |
Javadoc:
|
4 Define private constants and methods |
Optional |
|
5 Inject dependencies |
Inject objects using Guice as this makes it easier to unit test your node. This example specifies |
|
6 Implement the |
The |
|
7 Override the |
The It takes a The method returns an The choice of outcome in a simple decision node is |