diff --git a/README.md b/README.md index e3c2d2c..5a4cd39 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ --> # Search For User Authentication Tree Node -A simple authentication node for ForgeRock's Identity Platform 5.5 and above. This node checks if a user exists. +A simple authentication node for ForgeRock's Identity Platform 5.5 and above. This node searches the user data store for a matching identity based on configurable search attributes. Possible outcomes are "found" - a unique match is found, "ambiguous" - multiple potential matches found, or "not found" - no matching identity. See screenshot below for an example. ## Installation @@ -28,10 +28,9 @@ Deploy the node and wire the appropriate exhausts. The code in this repository has binary dependencies that live in the ForgeRock maven repository. Maven can be configured to authenticate to this repository by following the following [ForgeRock Knowledge Base Article](https://backstage.forgerock.com/knowledge/kb/article/a74096897). -Edit the necessary ProfileAttributeDecisionNode.java as appropriate. To rebuild, run "mvn clean install" in the directory containing the pom.xml +Edit the necessary SearchForUserNode.java as appropriate. To rebuild, run "mvn clean install" in the directory containing the pom.xml ![ScreenShot](./search-for-user-1.png) -![ScreenShot](./search-for-user-2.png) ## Disclaimer The sample code described herein is provided on an "as is" basis, without warranty of any kind, to the fullest extent permitted by law. ForgeRock does not warrant or guarantee the individual success developers may have in implementing the sample code on their development platforms or in production configurations. diff --git a/pom.xml b/pom.xml index 08cea79..3896fde 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ search-for-user-node org.forgerock.am - 6.0.0-SNAPSHOT + 6.5.0 Search For User Authentication Node An Authentication Tree Node for ForgeRock's Identity Platform diff --git a/search-for-user-1.png b/search-for-user-1.png index c3580c4..0aa724a 100644 Binary files a/search-for-user-1.png and b/search-for-user-1.png differ diff --git a/src/main/java/org/forgerock/openam/auth/nodes/SearchForUserNode.java b/src/main/java/org/forgerock/openam/auth/nodes/SearchForUserNode.java index 4a95894..a4b26de 100644 --- a/src/main/java/org/forgerock/openam/auth/nodes/SearchForUserNode.java +++ b/src/main/java/org/forgerock/openam/auth/nodes/SearchForUserNode.java @@ -18,23 +18,22 @@ package org.forgerock.openam.auth.nodes; import com.google.inject.assistedinject.Assisted; +import com.iplanet.sso.SSOException; import com.sun.identity.shared.debug.Debug; -import org.forgerock.guava.common.collect.ImmutableList; +import com.sun.identity.sm.DNMapper; import org.forgerock.json.JsonValue; import org.forgerock.openam.annotations.sm.Attribute; import org.forgerock.openam.auth.node.api.*; import javax.inject.Inject; +import static com.sun.identity.idm.IdUtils.getAMIdentityRepository; import static org.forgerock.openam.auth.node.api.SharedStateConstants.REALM; import static org.forgerock.openam.auth.node.api.SharedStateConstants.USERNAME; import org.forgerock.openam.core.CoreWrapper; import com.sun.identity.idm.*; - import java.util.*; - +import org.forgerock.openam.utils.CollectionUtils; import org.forgerock.util.i18n.PreferredLocales; -import com.sun.identity.idm.IdUtils; - @Node.Metadata(outcomeProvider = SearchForUserNode.OutcomeProvider.class, configClass = SearchForUserNode.Config.class) @@ -56,12 +55,11 @@ public interface Config { @Attribute(order = 100) default String sharedStateAttribute() { return USERNAME; - }; + } - //Toggle as to whether UA is stored in the clear or SHA256 hashed for privacy @Attribute(order = 200) - default String datastoreAttribute() { - return USERNAME; + default List datastoreAttributes() { + return Arrays.asList("username"); } } @@ -79,61 +77,76 @@ public SearchForUserNode(@Assisted Config config, CoreWrapper coreWrapper) throw this.coreWrapper = coreWrapper; } + @Override public Action process(TreeContext context) throws NodeProcessException { debug.message("[" + DEBUG_FILE + "]: " + "Starting"); AMIdentity userIdentity = null; - //Pull out the user object - if (config.datastoreAttribute() == USERNAME && config.sharedStateAttribute() == USERNAME){ - userIdentity = coreWrapper.getIdentity(context.sharedState.get(USERNAME).asString(),context.sharedState.get(REALM).asString()); - } else { - Set userSearchAttributes = new HashSet<>(); - userSearchAttributes.add(config.datastoreAttribute()); - debug.message("[" + DEBUG_FILE + "]: " + "Datastore attribute: {}", config.datastoreAttribute()); - debug.message("[" + DEBUG_FILE + "]: " + "Shared state attribute: {}, and value: {}", config.sharedStateAttribute(), context.sharedState.get(config.sharedStateAttribute()).asString()); - userIdentity = IdUtils.getIdentity(context.sharedState.get(config.sharedStateAttribute()).asString(), context.sharedState.get(REALM).asString(),userSearchAttributes); + Set userSearchAttributes = new HashSet(config.datastoreAttributes()); - } + AMIdentityRepository amIdRepo = getAMIdentityRepository(DNMapper.orgNameToDN(context.sharedState.get(REALM).asString())); + IdSearchControl idsc = new IdSearchControl(); + idsc.setRecursive(true); + idsc.setAllReturnAttributes(true); + Set results = Collections.EMPTY_SET; + String searchUsername = context.sharedState.get(config.sharedStateAttribute()).asString(); + IdSearchResults searchResults; try { + idsc.setMaxResults(0); + Map> searchAVP = CollectionUtils.toAvPairMap(userSearchAttributes, searchUsername); + idsc.setSearchModifiers(IdSearchOpModifier.OR, searchAVP); + searchResults = amIdRepo.searchIdentities(IdType.USER, "*", idsc); - if (userIdentity == null) { - - debug.error("[" + DEBUG_FILE + "]: " + "Unable to find user"); - return goTo("notFound").build(); - - } else { - - debug.message("[" + DEBUG_FILE + "]: " + "user found: {}", userIdentity.getName()); - //ensure username is set in sharedstate - context.sharedState.put(USERNAME, userIdentity.getName()); - return goTo("found").build(); + if (searchResults != null) { + results = searchResults.getSearchResults(); + } - } + if (results == null || results.size() == 0) { + debug.error("[" + DEBUG_FILE + "]: " + "Unable to find any matching user"); + return Action.goTo("notFound").build(); + } + if (results.size() != 1) { + debug.error("[" + DEBUG_FILE + "]: " + "More than one matching user profile found - ambiguous"); + return Action.goTo("ambiguous").build(); + } - } catch (Exception e) { + userIdentity = (AMIdentity)results.iterator().next(); + } catch (IdRepoException e) { + debug.warning("Error searching for user identity"); + } catch (SSOException e) { + debug.warning("Error searching for user identity"); + } - debug.error("[" + DEBUG_FILE + "]: " + "Node exception", e); - return goTo("notFound").build(); - } + debug.message("[" + DEBUG_FILE + "]: " + "user found: {}", userIdentity.getName()); + //ensure username is set in sharedstate + context.sharedState.put(USERNAME, userIdentity.getName()); + return goTo("found").build(); } + private Action.ActionBuilder goTo(String outcome) { return Action.goTo(outcome); } + static final class OutcomeProvider implements org.forgerock.openam.auth.node.api.OutcomeProvider { private static final String BUNDLE = SearchForUserNode.class.getName().replace(".", "/"); @Override public List getOutcomes(PreferredLocales locales, JsonValue nodeAttributes) { ResourceBundle bundle = locales.getBundleInPreferredLocale(BUNDLE, OutcomeProvider.class.getClassLoader()); - return ImmutableList.of( - new Outcome( "found", bundle.getString("found")), - new Outcome("notFound", bundle.getString("notFound"))); + List results = new ArrayList<>(); + results.add(new Outcome("found", "Found")); + results.add(new Outcome("notFound", "Not Found")); + results.add(new Outcome("ambiguous", "Ambiguous")); + return Collections.unmodifiableList(results); } } } + + + diff --git a/src/main/resources/org/forgerock/openam/auth/nodes/SearchForUserNode.properties b/src/main/resources/org/forgerock/openam/auth/nodes/SearchForUserNode.properties index 5b1a4d7..19f23d8 100644 --- a/src/main/resources/org/forgerock/openam/auth/nodes/SearchForUserNode.properties +++ b/src/main/resources/org/forgerock/openam/auth/nodes/SearchForUserNode.properties @@ -13,8 +13,13 @@ # # Copyright 2017 ForgeRock AS. # -#simon.moffatt@forgerock.com +#jon.knight@forgerock.com + +# Description nodeDescription=Search For User + +# Outcomes found=User Found -notFound=User Not Found \ No newline at end of file +notFound=User Not Found +ambiguous=Ambigous