The 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.
Conceptually, the Node
class is structured as follows:
The annotation
The annotation specifies the outcome provider and configuration class. The outcome provider can use the default
SingleOutcomeNode
orMultipleOutcomeNode
, or a customOutcomeProvider
can be created and referenced from the annotation.See the Choice Collector Node for an example of a custom outcome provider.
Any private constants
The 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 asdefault
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.The 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; }
The 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 returningAction
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
orfalse
, resulting in the authentication tree flow moving from the current node to a node at the relevant connection.Any private methods
Optionally, a custom outcome provider
The following example is the SetSessionPropertiesNode
class, taken from the Set Session Properties Node:
/* * The contents of this file are subject to the terms of the Common Development and * Distribution License (the License). You may not use this file except in compliance with the * License. * * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the * specific language governing permission and limitations under the License. * * When distributing Covered Software, include this CDDL Header Notice in each file and include * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL * Header, with the fields enclosed by brackets [] replaced by your own identifying * information: "Portions copyright [year] [name of copyright owner]". * * Copyright 2017-2018 ForgeRock AS. */ 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) public class SetSessionPropertiesNode extends SingleOutcomeNode { /** * Configuration for the node. */ public interface Config { /** * 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 public SetSessionPropertiesNode(@Assisted Config config) { this.config = config; } @Override public Action process(TreeContext context) { 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(); } }
The | |
Implementing the | |
Implementing the | |
Injecting the | |
Creating an |