diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/pom.xml b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/pom.xml index 668392d8fca6..073207a47c38 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/pom.xml +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/pom.xml @@ -130,6 +130,27 @@ com.google.code.gson gson + + + org.graalvm.sdk + graal-sdk + + + org.graalvm.js + js + + + org.graalvm.truffle + truffle-api + + + org.graalvm.regex + regex + + + com.ibm.icu + icu4j + org.testng testng diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/AuthenticationDecisionEvaluator.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/AuthenticationDecisionEvaluator.java index 44bce0fd0e7d..1b59a3837961 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/AuthenticationDecisionEvaluator.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/AuthenticationDecisionEvaluator.java @@ -18,6 +18,7 @@ package org.wso2.carbon.identity.application.authentication.framework; +import org.graalvm.polyglot.HostAccess; import org.wso2.carbon.identity.application.authentication.framework.context.AuthenticationContext; import java.io.Serializable; @@ -39,5 +40,6 @@ public interface AuthenticationDecisionEvaluator extends Serializable { * @param context * @return */ + @HostAccess.Export Object evaluate(AuthenticationContext context, Object... params); } diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/loader/UIBasedConfigurationLoader.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/loader/UIBasedConfigurationLoader.java index b2bb62b3a851..dfa705ab168d 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/loader/UIBasedConfigurationLoader.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/loader/UIBasedConfigurationLoader.java @@ -27,7 +27,7 @@ import org.wso2.carbon.identity.application.authentication.framework.config.model.StepConfig; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.AuthenticationGraph; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.JsBaseGraphBuilder; -import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.JsBaseGraphBuilderFactory; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.JsGenericGraphBuilderFactory; import org.wso2.carbon.identity.application.authentication.framework.context.AuthenticationContext; import org.wso2.carbon.identity.application.authentication.framework.exception.FrameworkException; import org.wso2.carbon.identity.application.authentication.framework.internal.FrameworkServiceComponent; @@ -88,8 +88,8 @@ public SequenceConfig getSequenceConfig(AuthenticationContext context, Map stepConfigMapCopy = new HashMap<>(); originalStepConfigMap.forEach((k, v) -> stepConfigMapCopy.put(k, new StepConfig(v))); sequenceConfig.getStepMap().clear(); - JsBaseGraphBuilderFactory jsGraphBuilderFactory = FrameworkServiceDataHolder.getInstance() - .getJsGraphBuilderFactory(); + JsGenericGraphBuilderFactory jsGraphBuilderFactory = FrameworkServiceDataHolder.getInstance() + .getJsGenericGraphBuilderFactory(); JsBaseGraphBuilder jsGraphBuilder = jsGraphBuilderFactory.createBuilder(context, stepConfigMapCopy); context.setServiceProviderName(serviceProvider.getApplicationName()); context.setServiceProviderResourceId(serviceProvider.getApplicationResourceId()); diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/BaseSerializableJsFunction.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/BaseSerializableJsFunction.java index d4906388ea03..281745c1373a 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/BaseSerializableJsFunction.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/BaseSerializableJsFunction.java @@ -18,8 +18,6 @@ package org.wso2.carbon.identity.application.authentication.framework.config.model.graph; -import java.io.Serializable; - import javax.script.ScriptEngine; /** @@ -30,7 +28,7 @@ * The current authentication context holds this function in serialized form. * */ -public interface BaseSerializableJsFunction extends Serializable { +public interface BaseSerializableJsFunction extends GenericSerializableJsFunction { void setSource(String name); diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/DynamicDecisionNode.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/DynamicDecisionNode.java index e69285307110..73c1d773aced 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/DynamicDecisionNode.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/DynamicDecisionNode.java @@ -29,7 +29,7 @@ public class DynamicDecisionNode extends AbstractAuthGraphNode implements AuthGraphNode { private static final long serialVersionUID = -2151385170280964420L; - private Map functionMap = new HashMap<>(); + private Map functionMap = new HashMap<>(); private AuthGraphNode defaultEdge; @Override @@ -39,6 +39,15 @@ public String getName() { } public Map getFunctionMap() { + + if (isFunctionMapInstanceOfBaseSerializableJsFunction()) { + return Collections.unmodifiableMap((Map) (Map) functionMap); + } + return null; + } + + public Map getGenericFunctionMap() { + return Collections.unmodifiableMap(functionMap); } @@ -46,6 +55,10 @@ public void addFunction(String outcome, BaseSerializableJsFunction function) { functionMap.put(outcome, function); } + public void addGenericFunction(String outcome, GenericSerializableJsFunction function) { + functionMap.put(outcome, function); + } + public AuthGraphNode getDefaultEdge() { return defaultEdge; } @@ -53,4 +66,13 @@ public AuthGraphNode getDefaultEdge() { public void setDefaultEdge(AuthGraphNode defaultEdge) { this.defaultEdge = defaultEdge; } + + private boolean isFunctionMapInstanceOfBaseSerializableJsFunction() { + + if (functionMap == null || functionMap.isEmpty()) { + return true; + } + // Get the first element of the map and check if that is an instance of BaseSerializableJsFunction + return functionMap.entrySet().iterator().next().getValue() instanceof BaseSerializableJsFunction; + } } diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/GenericSerializableJsFunction.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/GenericSerializableJsFunction.java new file mode 100644 index 000000000000..bd2b242bcd42 --- /dev/null +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/GenericSerializableJsFunction.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.application.authentication.framework.config.model.graph; + +import java.io.Serializable; + +/** + * Serializable javascript function. + * This is required since the next javascript execution may happen on a different node than current node, when user + * submits a form in the browser. + * The request may come to different node. + * The current authentication context holds this function in serialized form. + * + * @param Script Engine + */ +public interface GenericSerializableJsFunction extends Serializable { + + void setSource(String name); + + String getSource(); + + boolean isFunction(); + + void setFunction(boolean function); + + Object apply(T scriptEngine, Object... params); + +} diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/JsBaseGraphBuilder.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/JsBaseGraphBuilder.java index ec4711192704..c79220d6f4c0 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/JsBaseGraphBuilder.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/JsBaseGraphBuilder.java @@ -41,6 +41,11 @@ public interface JsBaseGraphBuilder { AuthenticationDecisionEvaluator getScriptEvaluator(BaseSerializableJsFunction fn); + default AuthenticationDecisionEvaluator getScriptEvaluator(GenericSerializableJsFunction fn) { + + return null; + } + void addLongWaitProcessInternal(AsyncProcess asyncProcess, Map parameterMap); diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/JsBaseGraphBuilderFactory.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/JsBaseGraphBuilderFactory.java index 36615abcfcaf..cdda2986b485 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/JsBaseGraphBuilderFactory.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/JsBaseGraphBuilderFactory.java @@ -30,7 +30,7 @@ * This factory is there to reuse of script engine and any related expensive objects. * */ -public interface JsBaseGraphBuilderFactory { +public interface JsBaseGraphBuilderFactory extends JsGenericGraphBuilderFactory { void init(); diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/JsGenericGraphBuilderFactory.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/JsGenericGraphBuilderFactory.java new file mode 100644 index 000000000000..dd6f93b369d3 --- /dev/null +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/JsGenericGraphBuilderFactory.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.application.authentication.framework.config.model.graph; + +import org.wso2.carbon.identity.application.authentication.framework.config.model.StepConfig; +import org.wso2.carbon.identity.application.authentication.framework.context.AuthenticationContext; + +import java.util.Map; + +/** + * Interface for Factory to create a Javascript based sequence builder. + * This factory is there to reuse of script engine and any related expensive objects. + * + * @param Type of the Javascript Engine + */ +public interface JsGenericGraphBuilderFactory { + + void init(); + + JsBaseGraphBuilder createBuilder(AuthenticationContext context, Map stepConfigMapCopy); + + JsBaseGraphBuilder createBuilder(AuthenticationContext authenticationContext, + Map stepConfigMap, AuthGraphNode currentNode); + + T createEngine(AuthenticationContext authenticationContext); + + JsGenericSerializer getJsUtil(); + + JsBaseGraphBuilder getCurrentBuilder(); +} diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/JsGenericSerializer.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/JsGenericSerializer.java new file mode 100644 index 000000000000..5cbbafd79866 --- /dev/null +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/JsGenericSerializer.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.application.authentication.framework.config.model.graph; + +import org.wso2.carbon.identity.application.authentication.framework.exception.FrameworkException; + +/** + * Interface for serializer class supports Multiple JS Engines. + * + * @param Js Engine. + */ +public interface JsGenericSerializer { + + /** + * Serialize the object using selected serializable function. + * + * @param value Object to evaluate. + * @return Serialized Object. + */ + Object toJsSerializable(Object value); + + /** + * De-Serialize the object using selected serializable function. + * + * @param value Serialized Object. + * @param engine Js Engine. + * @return De-Serialize object. + * @throws FrameworkException FrameworkException. + */ + Object fromJsSerializable(Object value, T engine) throws FrameworkException; + +} diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/JsGraphBuilder.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/JsGraphBuilder.java index 3380dbd0b6b4..8694677d50eb 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/JsGraphBuilder.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/JsGraphBuilder.java @@ -21,6 +21,7 @@ import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.graalvm.polyglot.HostAccess; import org.wso2.carbon.context.CarbonContext; import org.wso2.carbon.identity.application.authentication.framework.AsyncProcess; import org.wso2.carbon.identity.application.authentication.framework.config.model.AuthenticatorConfig; @@ -54,14 +55,14 @@ public abstract class JsGraphBuilder implements JsBaseGraphBuilder { private static final Log log = LogFactory.getLog(JsGraphBuilder.class); - private Map stepNamedMap; - private AuthenticationGraph result = new AuthenticationGraph(); - private AuthGraphNode currentNode = null; - private AuthenticationContext authenticationContext; + protected Map stepNamedMap; + protected AuthenticationGraph result = new AuthenticationGraph(); + protected AuthGraphNode currentNode = null; + protected AuthenticationContext authenticationContext; private ScriptEngine engine; - private static ThreadLocal contextForJs = new ThreadLocal<>(); - private static ThreadLocal dynamicallyBuiltBaseNode = new ThreadLocal<>(); - private static ThreadLocal currentBuilder = new ThreadLocal<>(); + protected static ThreadLocal contextForJs = new ThreadLocal<>(); + protected static ThreadLocal dynamicallyBuiltBaseNode = new ThreadLocal<>(); + protected static ThreadLocal currentBuilder = new ThreadLocal<>(); private static final String REMOVE_FUNCTIONS = "var quit=function(){Log.error('quit function is restricted.')};" + "var exit=function(){Log.error('exit function is restricted.')};" + "var print=function(){Log.error('print function is restricted.')};" + @@ -73,6 +74,7 @@ public abstract class JsGraphBuilder implements JsBaseGraphBuilder { "var $ARG=null;var $ENV=null;var $EXEC=null;" + "var $OPTIONS=null;var $OUT=null;var $ERR=null;var $EXIT=null;" + "Object.defineProperty(this, 'engine', {});"; + /** * Returns the built graph. * @@ -247,7 +249,7 @@ protected void handleOptionsAsyncEvent(Map options, StepConfig s * @param stepOptions Options provided from the script for the step. * @param stepConfigMap StepConfigs of each step as a map. */ - private void handleStepOptions(StepConfig stepConfig, Map stepOptions, + protected void handleStepOptions(StepConfig stepConfig, Map stepOptions, Map stepConfigMap) { stepConfig.setForced(Boolean.parseBoolean(stepOptions.get(FrameworkConstants.JSAttributes.FORCE_AUTH_PARAM))); @@ -470,7 +472,7 @@ protected void authenticatorParamsOptions(Map options, StepConfi public static void addPrompt(String templateId, Map parameters, Map handlers, Map callbacks) { - FrameworkServiceDataHolder.getInstance().getJsGraphBuilderFactory().getCurrentBuilder() + FrameworkServiceDataHolder.getInstance().getJsGenericGraphBuilderFactory().getCurrentBuilder() .addPromptInternal(templateId, parameters, handlers, callbacks); } @@ -499,6 +501,18 @@ public String loadLocalLibrary(String functionLibraryName) throws FunctionLibrar return libraryScript; } + /** + * Load Executor implementation to load local libraries. + */ + public class JsGraalLoadExecutorImpl implements LoadExecutor { + + @HostAccess.Export + public String loadLocalLibrary(String libraryName) throws FunctionLibraryManagementException { + + return JsGraphBuilder.this.loadLocalLibrary(libraryName); + } + } + /** * Adds a function to show a prompt in Javascript code. * @@ -507,7 +521,7 @@ public String loadLocalLibrary(String functionLibraryName) throws FunctionLibrar public static void addLongWaitProcess(AsyncProcess asyncProcess, Map parameterMap) { - FrameworkServiceDataHolder.getInstance().getJsGraphBuilderFactory().getCurrentBuilder() + FrameworkServiceDataHolder.getInstance().getJsGenericGraphBuilderFactory().getCurrentBuilder() .addLongWaitProcessInternal(asyncProcess, parameterMap); } @@ -519,7 +533,7 @@ public static void addLongWaitProcess(AsyncProcess asyncProcess, * @param destination Current node. * @param newNode New node to attach. */ - private static void infuse(AuthGraphNode destination, AuthGraphNode newNode) { + protected static void infuse(AuthGraphNode destination, AuthGraphNode newNode) { if (destination instanceof StepConfigGraphNode) { StepConfigGraphNode stepConfigGraphNode = ((StepConfigGraphNode) destination); @@ -545,7 +559,7 @@ private static void infuse(AuthGraphNode destination, AuthGraphNode newNode) { * @param baseNode Base node. * @param nodeToAttach Node to attach. */ - private static void attachToLeaf(AuthGraphNode baseNode, AuthGraphNode nodeToAttach) { + protected static void attachToLeaf(AuthGraphNode baseNode, AuthGraphNode nodeToAttach) { if (baseNode instanceof StepConfigGraphNode) { StepConfigGraphNode stepConfigGraphNode = ((StepConfigGraphNode) baseNode); @@ -594,7 +608,7 @@ private static void attachToLeaf(AuthGraphNode baseNode, AuthGraphNode nodeToAtt * @param stepConfig Step Config Object. * @return built and wrapped new StepConfigGraphNode. */ - private static StepConfigGraphNode wrap(StepConfig stepConfig) { + protected static StepConfigGraphNode wrap(StepConfig stepConfig) { return new StepConfigGraphNode(stepConfig); } @@ -623,6 +637,7 @@ public interface StepExecutor { @FunctionalInterface public interface PromptExecutor { + @HostAccess.Export void prompt(String template, Object... parameterMap); } @@ -645,6 +660,15 @@ public interface LoadExecutor { String loadLocalLibrary(String libraryName) throws FunctionLibraryManagementException; } + /** + * Functional interface for sending error in the authentication script. + */ + @FunctionalInterface + public interface SendErrorFunction { + + void sendError(String url, Map parameterMap); + } + @Deprecated public void exitFunction(Object... arg) { diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/JsNashornGraphBuilder.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/JsNashornGraphBuilder.java index 019d30ea34e4..52f18ae6f8d9 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/JsNashornGraphBuilder.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/JsNashornGraphBuilder.java @@ -143,6 +143,12 @@ public AuthenticationGraph build() { return result; } + @Override + public AuthenticationDecisionEvaluator getScriptEvaluator(GenericSerializableJsFunction fn) { + + return new JsBasedEvaluator((SerializableJsFunction) fn); + } + /** * Creates the graph with the given Script and step map. * @@ -401,7 +407,7 @@ protected void handleOptionsAsyncEvent(Map options, StepConfig s * @param stepOptions Options provided from the script for the step. * @param stepConfigMap StepConfigs of each step as a map. */ - private void handleStepOptions(StepConfig stepConfig, Map stepOptions, + protected void handleStepOptions(StepConfig stepConfig, Map stepOptions, Map stepConfigMap) { stepConfig.setForced(Boolean.parseBoolean(stepOptions.get(FrameworkConstants.JSAttributes.FORCE_AUTH_PARAM))); @@ -843,7 +849,7 @@ private static void attachEventListeners(Map eventsMap, AuthGrap } DynamicDecisionNode decisionNode = new DynamicDecisionNode(); addEventListeners(decisionNode, eventsMap); - if (!decisionNode.getFunctionMap().isEmpty()) { + if (!decisionNode.getGenericFunctionMap().isEmpty()) { attachToLeaf(currentNode, decisionNode); } } @@ -855,7 +861,7 @@ private void attachEventListeners(Map eventsMap) { } DynamicDecisionNode decisionNode = new DynamicDecisionNode(); addEventListeners(decisionNode, eventsMap); - if (!decisionNode.getFunctionMap().isEmpty()) { + if (!decisionNode.getGenericFunctionMap().isEmpty()) { attachToLeaf(currentNode, decisionNode); currentNode = decisionNode; } @@ -898,12 +904,12 @@ private static void addHandlers(ShowPromptNode showPromptNode, Map { /** * Serialize the object using selected serializable function. + * * @param value Object to evaluate. * @return Serialized Object. */ @@ -36,7 +38,8 @@ public interface JsSerializer { /** * De-Serialize the object using selected serializable function. - * @param value Serialized Object. + * + * @param value Serialized Object. * @param engine Js Engine. * @return De-Serialize object. * @throws FrameworkException FrameworkException. diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/JsWrapperBaseFactory.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/JsWrapperBaseFactory.java index 0a1ff5a0ecb0..96c705894374 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/JsWrapperBaseFactory.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/JsWrapperBaseFactory.java @@ -18,12 +18,17 @@ package org.wso2.carbon.identity.application.authentication.framework.config.model.graph; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.CommonJsHeaders; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.base.JsBaseAuthenticatedUser; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.base.JsBaseAuthenticationContext; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.base.JsBaseClaims; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.base.JsBaseCookie; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.base.JsBaseParameters; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.base.JsBaseRuntimeClaims; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.base.JsBaseServletRequest; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.base.JsBaseServletResponse; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.base.JsBaseStep; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.base.JsBaseSteps; import org.wso2.carbon.identity.application.authentication.framework.context.AuthenticationContext; import org.wso2.carbon.identity.application.authentication.framework.context.TransientObjectWrapper; import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser; @@ -55,6 +60,17 @@ public interface JsWrapperBaseFactory { JsBaseAuthenticatedUser createJsAuthenticatedUser(AuthenticationContext authenticationContext, AuthenticatedUser authenticatedUser); + /** + * Creates a JavaScript Proxy for authenticated User. + * + * @param context - Represent Authentication Request data from servlet + * @param wrappedUser - Wrapped Authenticated User + * @param step - Represent Authentication Step + * @param idp - Represent Identity Provider + * @return Proxy for authenticated User + */ + JsBaseAuthenticatedUser createJsAuthenticatedUser(AuthenticationContext context, AuthenticatedUser wrappedUser, + int step, String idp); /** * Creates a JavaScript Proxy for authentication Context. @@ -97,4 +113,73 @@ JsBaseAuthenticatedUser createJsAuthenticatedUser(AuthenticationContext authenti * @return Proxy for wrapped Servlet Response */ JsBaseServletResponse createJsServletResponse(TransientObjectWrapper wrapped); + + /** + * Creates a JavaScript Proxy for Claims. + * + * @param context - Represent Authentication Request data from servlet + * @param step - Represent Authentication Step + * @param idp - Represent Identity Provider + * @param isRemoteClaimRequest - Represent Remote Claim Request + * @return Proxy for Claims + */ + JsBaseClaims createJsClaims(AuthenticationContext context, int step, String idp, boolean isRemoteClaimRequest); + + /** + * Creates a JavaScript Proxy for Claims. + * + * @param context - Represent Authentication Request data from servlet + * @param user - Represent Authenticated Subject + * @param isRemoteClaimRequest - Represent Remote Claim Request + * @return Proxy for Claims + */ + JsBaseClaims createJsClaims(AuthenticationContext context, AuthenticatedUser user, boolean isRemoteClaimRequest); + + /** + * Creates a JavaScript Proxy for Runtime Claims. + * + * @param context - Represent Authentication Request data from servlet + * @param step - Represent Authentication Step + * @param idp - Represent Identity Provider + * @return Proxy for Runtime Claims + */ + JsBaseRuntimeClaims createJsRuntimeClaims(AuthenticationContext context, int step, String idp); + + /** + * Creates a JavaScript Proxy for Runtime Claims. + * + * @param context - Represent Authentication Request data from servlet + * @param user - Represent Authenticated Subject + * @return Proxy for Runtime Claims + */ + JsBaseRuntimeClaims createJsRuntimeClaims(AuthenticationContext context, AuthenticatedUser user); + + /** + * Creates a JavaScript Proxy for Step. + * + * @param context - Represent Authentication Request data from servlet + * @param step - Represent Authentication Step + * @param authenticatedIdp - Represent Identity Provider + * @param authenticatedAuthenticator - Represent Authenticator + * @return Proxy for Step + */ + JsBaseStep createJsStep(AuthenticationContext context, int step, String authenticatedIdp, + String authenticatedAuthenticator); + + /** + * Creates a JavaScript Proxy for HTTP Headers. + * + * @param wrapped - HTTP Headers in Servlet Request + * @param response - Wrapped Servlet Response + * @return Proxy for HTTP headers/Cookies in Servlet Request + */ + CommonJsHeaders createJsHeaders(Map wrapped, HttpServletResponse response); + + /** + * Creates a JavaScript Proxy for Steps. + * + * @param context - Represent Authentication Request data from servlet + * @return Proxy for Steps + */ + JsBaseSteps createJsSteps(AuthenticationContext context); } diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/JsWrapperFactory.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/JsWrapperFactory.java index 2f8cfb8ab866..e6a672b3a192 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/JsWrapperFactory.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/JsWrapperFactory.java @@ -18,12 +18,17 @@ package org.wso2.carbon.identity.application.authentication.framework.config.model.graph; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.nashorn.JsHeaders; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.nashorn.JsNashornAuthenticatedUser; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.nashorn.JsNashornAuthenticationContext; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.nashorn.JsNashornClaims; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.nashorn.JsNashornCookie; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.nashorn.JsNashornParameters; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.nashorn.JsNashornRuntimeClaims; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.nashorn.JsNashornServletRequest; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.nashorn.JsNashornServletResponse; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.nashorn.JsNashornStep; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.nashorn.JsNashornSteps; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.nashorn.JsNashornWritableParameters; import org.wso2.carbon.identity.application.authentication.framework.context.AuthenticationContext; import org.wso2.carbon.identity.application.authentication.framework.context.TransientObjectWrapper; @@ -52,6 +57,14 @@ public JsNashornAuthenticatedUser createJsAuthenticatedUser(AuthenticationContex return new JsNashornAuthenticatedUser(authenticationContext, authenticatedUser); } + + @Override + public JsNashornAuthenticatedUser createJsAuthenticatedUser(AuthenticationContext context, + AuthenticatedUser wrappedUser, int step, String idp) { + + return new JsNashornAuthenticatedUser(context, wrappedUser, step, idp); + } + @Override public JsNashornAuthenticationContext createJsAuthenticationContext(AuthenticationContext authenticationContext) { @@ -87,4 +100,49 @@ public JsNashornServletResponse createJsServletResponse(TransientObjectWrapper data; private Map parameters; - private Map handlerMap = new HashMap<>(); + private Map handlerMap = new HashMap<>(); public String getTemplateId() { @@ -69,11 +69,26 @@ public void setParameters(Map parameters) { public Map getHandlerMap() { + if (isHandlerMapInstanceOfBaseSerializableJsFunction()) { + return Collections.unmodifiableMap((Map) (Map) handlerMap); + } + return null; + } + + public Map getGenericHandlerMap() { + return Collections.unmodifiableMap(handlerMap); } public void setHandlerMap(Map handlerMap) { + if (isHandlerMapInstanceOfBaseSerializableJsFunction()) { + this.handlerMap = (Map) (Map) handlerMap; + } + } + + public void setGenericHandlerMap(Map handlerMap) { + this.handlerMap = handlerMap; } @@ -81,9 +96,22 @@ public void addHandler(String outcome, BaseSerializableJsFunction function) { handlerMap.put(outcome, function); } + public void addGenericHandler(String outcome, GenericSerializableJsFunction function) { + handlerMap.put(outcome, function); + } + @Override public String getName() { return "ShowPromptNode"; } + + private boolean isHandlerMapInstanceOfBaseSerializableJsFunction() { + + if (handlerMap == null || handlerMap.isEmpty()) { + return true; + } + // Get the first element of the map and check if that is an instance of BaseSerializableJsFunction + return handlerMap.entrySet().iterator().next().getValue() instanceof BaseSerializableJsFunction; + } } diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/graaljs/GraalSerializableJsFunction.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/graaljs/GraalSerializableJsFunction.java new file mode 100644 index 000000000000..82967d74e1f2 --- /dev/null +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/graaljs/GraalSerializableJsFunction.java @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.application.authentication.framework.config.model.graph.graaljs; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.graalvm.polyglot.Context; +import org.graalvm.polyglot.PolyglotException; +import org.graalvm.polyglot.Value; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.GenericSerializableJsFunction; + +import java.util.function.Function; + +/** + * Javascript function wrapper. This allows serialization of a javascript defined function. + * Since Nashorn is deprecated in JDK 11 and onwards. We replaced it with GraalJS classes. + */ +public class GraalSerializableJsFunction implements GenericSerializableJsFunction { + + private static final Log log = LogFactory.getLog(GraalSerializableJsFunction.class); + private static final long serialVersionUID = -7001351065432647040L; + private String source; + private boolean isPolyglotFunction = false; + private String name; + + public GraalSerializableJsFunction(String source) { + + this.source = source; + this.isPolyglotFunction = true; + } + + public GraalSerializableJsFunction(String source, boolean isFunction) { + + this.source = source; + this.isPolyglotFunction = true; + } + + public String getSource() { + + return source; + } + + public void setSource(String source) { + + this.source = source; + } + + @Override + public boolean isFunction() { + + return isPolyglotFunction; + } + + @Override + public void setFunction(boolean function) { + + } + + public Object apply(Context polyglotContext, Object... params) { + + if (isPolyglotFunction) { + try { + Value jsFunction = polyglotContext.eval("js", "(" + getSource() + ")"); + return jsFunction.execute(params); + } catch (PolyglotException e) { + log.error("Error when executing function", e); + } + } + + return null; + } + + /** + * This will return the converted GraalSerializableJsFunction if the given value is a function. + * + * @param functionObject Value type Function to Serialize + * @return null if the ScriptObjectMirror is not a function. + */ + public static GraalSerializableJsFunction toSerializableForm(Object functionObject) { + + if (functionObject == null) { + return null; + } + try { + Value functionAsValue; + if (functionObject instanceof Function) { + Context context = Context.getCurrent(); + functionAsValue = context.asValue(functionObject); + } else { + functionAsValue = (Value) functionObject; + } + if (functionAsValue.canExecute()) { + if (functionAsValue.isHostObject()) { + return null; + } + String source = (String) functionAsValue.getSourceLocation().getCharacters(); + return serializePolyglot(source); + } + } catch (PolyglotException e) { + log.error("Error when serializing JavaScript Function: ", e); + } + return null; + + } + + public String getName() { + + return name; + } + + public void setName(String name) { + + this.name = name; + } + + private static GraalSerializableJsFunction serializePolyglot(String source) { + + return new GraalSerializableJsFunction(source); + } +} diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/graaljs/GraalSerializer.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/graaljs/GraalSerializer.java new file mode 100644 index 000000000000..1ad2800f687c --- /dev/null +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/graaljs/GraalSerializer.java @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.application.authentication.framework.config.model.graph.graaljs; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.graalvm.polyglot.Context; +import org.graalvm.polyglot.Value; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.JsGenericSerializer; +import org.wso2.carbon.identity.application.authentication.framework.exception.FrameworkException; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants.JSAttributes.POLYGLOT_LANGUAGE; + +/** + * Serializer class supports GraalJS Engine. + */ +public class GraalSerializer implements JsGenericSerializer { + + private static final Log log = LogFactory.getLog(GraalSerializer.class); + private static final GraalSerializer graalSerializer = new GraalSerializer(); + + public static GraalSerializer getInstance() { + + return graalSerializer; + } + + @Override + public Object toJsSerializable(Object value) { + + return toJsSerializableInternal(value); + } + + public static Object toJsSerializableInternal(Object value) { + + if (value instanceof Serializable) { + if (value instanceof HashMap) { + Map map = new HashMap<>(); + ((HashMap) value).forEach((k, v) -> map.put(k, toJsSerializableInternal(v))); + return map; + } else { + return value; + } + } else if (value instanceof Map) { + // Polyglot Map is not serializable. Hence, converting to HashMap. + Map map = new HashMap<>(); + ((Map) value).forEach((k, v) -> map.put(k, toJsSerializableInternal(v))); + return map; + } else if (value instanceof List) { + // Polyglot List is not serializable. Hence, converting to ArrayList. + List list = new ArrayList<>(); + ((List) value).forEach(v -> list.add(toJsSerializableInternal(v))); + return list; + } else if (value instanceof Value) { + Value valueObj = (Value) value; + if (valueObj.canExecute()) { + return GraalSerializableJsFunction.toSerializableForm(valueObj); + } else if (valueObj.isNumber()) { + return valueObj.asInt(); + } else if (valueObj.isString()) { + return valueObj.toString(); + } else if (valueObj.isDate()) { + return valueObj.asDate(); + } else if (valueObj.isBoolean()) { + return valueObj.asBoolean(); + } else if (valueObj.isDuration()) { + return valueObj.asDuration(); + } else if (valueObj.isTime()) { + return valueObj.asTime(); + } else if (valueObj.isTimeZone()) { + return valueObj.asTimeZone(); + } else if (valueObj.isNull()) { + return null; + } else if (valueObj.hasArrayElements()) { + int arraySize = (int) valueObj.getArraySize(); + List arrayItems = new ArrayList<>(arraySize); + for (int key = 0; key < arraySize; key++) { + Object arrayObj = valueObj.getArrayElement(key); + Object serializedObj = toJsSerializableInternal(arrayObj); + arrayItems.add((Serializable) serializedObj); + } + return arrayItems; + } else if (valueObj.isProxyObject()) { + return toJsSerializableInternal(valueObj.asProxyObject()); + } else if (valueObj.hasMembers()) { + Map serializedMap = new HashMap<>(); + valueObj.getMemberKeys().forEach((key) -> { + Object serializedObj = toJsSerializableInternal(valueObj.getMember(key)); + if (serializedObj instanceof Serializable) { + serializedMap.put(key, (Serializable) serializedObj); + if (log.isDebugEnabled()) { + log.debug("Serialized the value for key : " + key); + } + } else { + log.warn( + String.format("Non serializable object for key : %s, and will not be persisted.", key)); + } + }); + return serializedMap; + } else { + return Collections.EMPTY_MAP; + } + } + return value; + } + + @Override + public Object fromJsSerializable(Object value, Context engine) throws FrameworkException { + + return fromJsSerializableInternal(value, engine); + } + + public static Object fromJsSerializableInternal(Object value, Context context) throws FrameworkException { + if (value == null) { + return null; + } else if (value instanceof GraalSerializableJsFunction) { + GraalSerializableJsFunction serializableJsFunction = (GraalSerializableJsFunction) value; + try { + return context.eval("js", "(" + serializableJsFunction.getSource() + ")"); + } catch (Exception e) { + log.error("Error when recreating JS Object", e); + } + } else if (value instanceof Map) { + context.eval(POLYGLOT_LANGUAGE, "_tempMap = {}"); + Value tempMap = context.getBindings(POLYGLOT_LANGUAGE).getMember("_tempMap"); + for (Map.Entry entry : ((Map) value).entrySet()) { + Object deserializedObj = fromJsSerializableInternal(entry.getValue(), context); + tempMap.putMember(entry.getKey(), deserializedObj); + } + //remove from bindings + context.getBindings(POLYGLOT_LANGUAGE).removeMember("_tempMap"); + return tempMap; + } else if (value instanceof List) { + Value deserializedValue = context.eval(POLYGLOT_LANGUAGE, "[]"); + List valueList = (List) value; + int listSize = valueList.size(); + for (int index = 0; index < listSize; index++) { + Object deserializedObject = fromJsSerializableInternal(valueList.get(index), context); + deserializedValue.setArrayElement(index, deserializedObject); + } + return deserializedValue; + } + return value; + } +} diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/graaljs/JsGraalGraphBuilder.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/graaljs/JsGraalGraphBuilder.java new file mode 100644 index 000000000000..3791f4529236 --- /dev/null +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/graaljs/JsGraalGraphBuilder.java @@ -0,0 +1,787 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.application.authentication.framework.config.model.graph.graaljs; + +import org.apache.commons.collections.MapUtils; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.graalvm.polyglot.Context; +import org.graalvm.polyglot.HostAccess; +import org.graalvm.polyglot.PolyglotException; +import org.graalvm.polyglot.Source; +import org.graalvm.polyglot.Value; +import org.wso2.carbon.identity.application.authentication.framework.AsyncProcess; +import org.wso2.carbon.identity.application.authentication.framework.AuthenticationDecisionEvaluator; +import org.wso2.carbon.identity.application.authentication.framework.JsFunctionRegistry; +import org.wso2.carbon.identity.application.authentication.framework.config.model.StepConfig; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.AuthGraphNode; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.AuthenticationGraph; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.BaseSerializableJsFunction; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.DynamicDecisionNode; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.FailNode; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.GenericSerializableJsFunction; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.JSExecutionMonitorData; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.JSExecutionSupervisor; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.JsGraphBuilder; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.LongWaitNode; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.ShowPromptNode; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.StepConfigGraphNode; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.graaljs.JsGraalAuthenticationContext; +import org.wso2.carbon.identity.application.authentication.framework.context.AuthenticationContext; +import org.wso2.carbon.identity.application.authentication.framework.internal.FrameworkServiceDataHolder; + +import java.io.IOException; +import java.io.Serializable; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import static org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants.AdaptiveAuthentication.PROP_EXECUTION_SUPERVISOR_RESULT; +import static org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants.JSAttributes.AUTHENTICATION_OPTIONS; +import static org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants.JSAttributes.AUTHENTICATOR_PARAMS; +import static org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants.JSAttributes.JS_AUTH_FAILURE; +import static org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants.JSAttributes.JS_FUNC_EXECUTE_STEP; +import static org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants.JSAttributes.JS_FUNC_LOAD_FUNC_LIB; +import static org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants.JSAttributes.JS_FUNC_ON_LOGIN_REQUEST; +import static org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants.JSAttributes.JS_FUNC_SEND_ERROR; +import static org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants.JSAttributes.JS_FUNC_SHOW_PROMPT; +import static org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants.JSAttributes.POLYGLOT_LANGUAGE; +import static org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants.JSAttributes.POLYGLOT_SOURCE; +import static org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants.JSAttributes.PROP_CURRENT_NODE; +import static org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants.JSAttributes.STEP_OPTIONS; + +/** + * Translate the authentication graph config to runtime model. + * This is not thread safe. Should be discarded after each build. + *

+ * Since Nashorn is deprecated in JDK 11 and onwards. We are introducing GraalJS engine. + */ +public class JsGraalGraphBuilder extends JsGraphBuilder { + + private static final Log log = LogFactory.getLog(JsGraalGraphBuilder.class); + protected Context context; + + private static final String REMOVE_FUNCTIONS = "var quit=function(){Log.error('quit function is restricted.')};" + + "var exit=function(){Log.error('exit function is restricted.')};" + + "var print=function(){Log.error('print function is restricted.')};" + + "var echo=function(){Log.error('echo function is restricted.')};" + + "var readFully=function(){Log.error('readFully function is restricted.')};" + + "var readLine=function(){Log.error('readLine function is restricted.')};" + + "var load=function(){Log.error('load function is restricted.')};" + + "var loadWithNewGlobal=function(){Log.error('loadWithNewGlobal function is restricted.')};" + + "var $ARG=null;var $ENV=null;var $EXEC=null;" + + "var $OPTIONS=null;var $OUT=null;var $ERR=null;var $EXIT=null;" + + "Object.defineProperty(this, 'engine', {});"; + + /** + * Constructs the builder with the given authentication context. + * + * @param authenticationContext current authentication context. + * @param stepConfigMap The Step map from the service provider configuration. + * @param context Polyglot Context. + */ + public JsGraalGraphBuilder(AuthenticationContext authenticationContext, Map stepConfigMap, + Context context) { + + this.authenticationContext = authenticationContext; + this.context = context; + stepNamedMap = stepConfigMap.entrySet() + .stream() + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + } + + /** + * Constructs the builder with the given authentication context. + * + * @param authenticationContext current authentication context. + * @param stepConfigMap The Step map from the service provider configuration. + * @param context polyglot context. + * @param currentNode Current authentication graph node. + */ + public JsGraalGraphBuilder(AuthenticationContext authenticationContext, Map stepConfigMap, + Context context, AuthGraphNode currentNode) { + + this.authenticationContext = authenticationContext; + this.context = context; + this.currentNode = currentNode; + stepNamedMap = stepConfigMap.entrySet() + .stream() + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + } + + /** + * Creates the graph with the given Script and step map. + * + * @param script the Dynamic authentication script. + */ + @Override + public JsGraalGraphBuilder createWith(String script) { + + try { + currentBuilder.set(this); + Value bindings = context.getBindings(POLYGLOT_LANGUAGE); + + bindings.putMember(JS_FUNC_EXECUTE_STEP, new JsGraalStepExecuter()); + bindings.putMember(JS_FUNC_SEND_ERROR, new SendErrorAsyncFunctionImpl()); + bindings.putMember(JS_FUNC_SHOW_PROMPT, new JsGraalPromptExecutorImpl()); + bindings.putMember(JS_FUNC_LOAD_FUNC_LIB, new JsGraalLoadExecutorImpl()); + JsFunctionRegistry jsFunctionRegistrar = FrameworkServiceDataHolder.getInstance().getJsFunctionRegistry(); + if (jsFunctionRegistrar != null) { + Map functionMap = + jsFunctionRegistrar.getSubsystemFunctionsMap(JsFunctionRegistry.Subsystem.SEQUENCE_HANDLER); + functionMap.forEach(bindings::putMember); + } + currentBuilder.set(this); + context.eval(Source + .newBuilder(POLYGLOT_LANGUAGE, + FrameworkServiceDataHolder.getInstance().getCodeForRequireFunction(), + POLYGLOT_SOURCE) + .build()); + + String identifier = UUID.randomUUID().toString(); + Optional optionalScriptExecutionData; + + try { + startScriptExecutionMonitor(identifier, authenticationContext); + context.eval(Source.newBuilder(POLYGLOT_LANGUAGE, script, POLYGLOT_SOURCE).build()); + + Value onLoginRequestFn = bindings.getMember(JS_FUNC_ON_LOGIN_REQUEST); + if (onLoginRequestFn == null) { + log.error("Could not find the entry function " + JS_FUNC_ON_LOGIN_REQUEST + " \n" + script); + result.setBuildSuccessful(false); + result.setErrorReason("Error in executing the Javascript. " + JS_FUNC_ON_LOGIN_REQUEST + + " function is not defined."); + return this; + } + onLoginRequestFn.executeVoid(new JsGraalAuthenticationContext(authenticationContext)); + } finally { + optionalScriptExecutionData = Optional.ofNullable(endScriptExecutionMonitor(identifier)); + } + optionalScriptExecutionData.ifPresent( + scriptExecutionData -> storeAuthScriptExecutionMonitorData(authenticationContext, + scriptExecutionData)); + JsGraalGraphBuilderFactory.persistCurrentContext(authenticationContext, context); + } catch (PolyglotException e) { + result.setBuildSuccessful(false); + result.setErrorReason( + "Error in executing the Javascript. " + JS_FUNC_ON_LOGIN_REQUEST + " reason, " + e.getMessage()); + if (log.isDebugEnabled()) { + log.debug("Error in executing the Javascript.", e); + } + } catch (IOException e) { + result.setBuildSuccessful(false); + result.setErrorReason("Error in building the Javascript source" + e.getMessage()); + if (log.isDebugEnabled()) { + log.debug("Error in building the Javascript source", e); + } + } finally { + clearCurrentBuilder(context); + } + return this; + } + + @Override + public AuthenticationDecisionEvaluator getScriptEvaluator(BaseSerializableJsFunction fn) { + + return null; + } + + private static void attachEventListeners(Map eventsMap, AuthGraphNode currentNode) { + + if (eventsMap == null) { + return; + } + DynamicDecisionNode decisionNode = new DynamicDecisionNode(); + addEventListeners(decisionNode, eventsMap); + if (!decisionNode.getGenericFunctionMap().isEmpty()) { + attachToLeaf(currentNode, decisionNode); + } + } + + private void attachEventListeners(Map eventsMap) { + + if (eventsMap == null) { + return; + } + DynamicDecisionNode decisionNode = new DynamicDecisionNode(); + addEventListeners(decisionNode, eventsMap); + if (!decisionNode.getGenericFunctionMap().isEmpty()) { + attachToLeaf(currentNode, decisionNode); + currentNode = decisionNode; + } + } + + /** + * Adds all the event listeners to the decision node. + * + * @param eventsMap Map of events and event handler functions, which is handled by this execution. + * @return created Dynamic Decision node. + */ + private static void addEventListeners(DynamicDecisionNode decisionNode, Map eventsMap) { + + if (eventsMap == null) { + return; + } + eventsMap.forEach((key, value) -> { + if ((!(value instanceof GraalSerializableJsFunction))) { + GraalSerializableJsFunction jsFunction = GraalSerializableJsFunction.toSerializableForm(value); + if (jsFunction != null) { + decisionNode.addGenericFunction(key, jsFunction); + } else { + log.error("Event handler : " + key + " is not a function : " + value); + } + } else { + decisionNode.addGenericFunction(key, (GraalSerializableJsFunction) value); + } + }); + } + + private static void addHandlers(ShowPromptNode showPromptNode, Map handlersMap) { + + if (handlersMap == null) { + return; + } + handlersMap.forEach((key, value) -> { + if (!(value instanceof GraalSerializableJsFunction)) { + GraalSerializableJsFunction jsFunction = GraalSerializableJsFunction.toSerializableForm(value); + if (jsFunction != null) { + showPromptNode.addGenericHandler(key, jsFunction); + } else { + log.error("Event handler : " + key + " is not a function : " + value); + } + } else { + showPromptNode.addGenericHandler(key, (GraalSerializableJsFunction) value); + } + }); + } + + /** + * Adds the step given by step ID tp the authentication graph. + * + * @param params params + */ + @SuppressWarnings("unchecked") + @HostAccess.Export + public void executeStepInAsyncEvent(int stepId, Object... params) { + + AuthenticationContext context = contextForJs.get(); + AuthGraphNode currentNode = dynamicallyBuiltBaseNode.get(); + + if (log.isDebugEnabled()) { + log.debug("Execute Step on async event. Step ID : " + stepId); + } + AuthenticationGraph graph = context.getSequenceConfig().getAuthenticationGraph(); + if (graph == null) { + log.error("The graph happens to be null on the sequence config. Can not execute step : " + stepId); + return; + } + + StepConfig stepConfig = graph.getStepMap().get(stepId); + if (stepConfig == null) { + if (log.isDebugEnabled()) { + log.error("The stepConfig of the step ID : " + stepId + " is null"); + } + return; + } + // Inorder to keep original stepConfig as a backup in AuthenticationGraph. + StepConfig clonedStepConfig = new StepConfig(stepConfig); + StepConfig stepConfigFromContext = null; + if (MapUtils.isNotEmpty(context.getSequenceConfig().getStepMap())) { + stepConfigFromContext = context.getSequenceConfig().getStepMap().values() + .stream() + .filter(contextStepConfig -> (stepConfig.getOrder() == contextStepConfig.getOrder())) + .findFirst() + .orElse(null); + } + clonedStepConfig.applyStateChangesToNewObjectFromContextStepMap(stepConfigFromContext); + if (log.isDebugEnabled()) { + log.debug("Found step for the Step ID : " + stepId + ", Step Config " + clonedStepConfig); + } + StepConfigGraphNode newNode = wrap(clonedStepConfig); + if (currentNode == null) { + if (log.isDebugEnabled()) { + log.debug("Setting a new node at the first time. Node : " + newNode.getName()); + } + dynamicallyBuiltBaseNode.set(newNode); + } else { + attachToLeaf(currentNode, newNode); + } + + if (params.length > 0) { + // if there is only one param, it is assumed to be the event listeners + if (params[params.length - 1] instanceof Map) { + attachEventListeners((Map) params[params.length - 1], newNode); + } else { + log.error("Invalid argument and hence ignored. Last argument should be a Map of event listeners."); + } + } + + if (params.length == 2) { + // There is an argument with options present + if (params[0] instanceof Map) { + Map options = (Map) params[0]; + handleOptionsAsyncEvent(options, clonedStepConfig, context.getSequenceConfig().getStepMap()); + } + } + } + + + /** + * Executes the given script in an async event. + */ + public class JsGraalStepExecuterInAsyncEvent implements StepExecutor { + + @HostAccess.Export + public void executeStep(Integer stepId, Object... parameterMap) { + + JsGraalGraphBuilder.this.executeStepInAsyncEvent(stepId, parameterMap); + } + } + + @Override + public void addLongWaitProcessInternal(AsyncProcess asyncProcess, Map parameterMap) { + + LongWaitNode newNode = new LongWaitNode(asyncProcess); + + if (parameterMap != null) { + addEventListeners(newNode, parameterMap); + } + if (this.currentNode == null) { + this.result.setStartNode(newNode); + } else { + attachToLeaf(this.currentNode, newNode); + } + + this.currentNode = newNode; + } + + /** + * @param templateId Identifier of the template. + * @param parameters Parameters. + * @param handlers Handlers to run before and after the prompt. + * @param callbacks Callbacks to run after the prompt. + */ + @SuppressWarnings("unchecked") + @Override + public void addPromptInternal(String templateId, Map parameters, Map handlers, + Map callbacks) { + + ShowPromptNode newNode = new ShowPromptNode(); + newNode.setTemplateId(templateId); + newNode.setParameters(parameters); + + JsGraalGraphBuilder currentBuilder = getCurrentBuilder(); + if (currentBuilder.currentNode == null) { + currentBuilder.result.setStartNode(newNode); + } else { + attachToLeaf(currentBuilder.currentNode, newNode); + } + + currentBuilder.currentNode = newNode; + addEventListeners(newNode, callbacks); + addHandlers(newNode, handlers); + } + + public AuthenticationDecisionEvaluator getScriptEvaluator(GenericSerializableJsFunction fn) { + + return new JsBasedEvaluator((GraalSerializableJsFunction) fn); + } + + public static void clearCurrentBuilder(Context context) { + + context.close(); + clearCurrentBuilder(); + } + + /** + * Adds the step given by step ID tp the authentication graph. + * + * @param stepId Step Id + * @param params params + */ + @SuppressWarnings("unchecked") + @HostAccess.Export + public void executeStep(int stepId, Object... params) { + + StepConfig stepConfig; + stepConfig = stepNamedMap.get(stepId); + + if (stepConfig == null) { + log.error("Given Authentication Step :" + stepId + " is not in Environment"); + return; + } + StepConfigGraphNode newNode = wrap(stepConfig); + if (currentNode == null) { + result.setStartNode(newNode); + } else { + attachToLeaf(currentNode, newNode); + } + currentNode = newNode; + if (params.length > 0) { + // if there are any params provided, last one is assumed to be the event listeners + if (params[params.length - 1] instanceof Map) { + attachEventListeners((Map) params[params.length - 1]); + } else { + log.error("Invalid argument and hence ignored. Last argument should be a Map of event listeners."); + } + } + if (params.length == 2) { + // There is an argument with options present + if (params[0] instanceof Map) { + Map options = (Map) params[0]; + handleOptions(options, stepConfig); + } + } + } + + /** + * Executes the given script. + */ + public class JsGraalStepExecuter implements StepExecutor { + + @HostAccess.Export + public void executeStep(Integer stepId, Object... parameterMap) { + + JsGraalGraphBuilder.this.executeStep(stepId, parameterMap); + } + } + + /** + * Handle options within executeStepInAsyncEvent function. This method will update step configs through context. + * + * @param options Map of authenticator options. + * @param stepConfig Current stepConfig. + * @param stepConfigMap Map of stepConfigs get from the context object. + */ + @Override + @SuppressWarnings("unchecked") + protected void handleOptionsAsyncEvent(Map options, StepConfig stepConfig, + Map stepConfigMap) { + + Object authenticationOptionsObj = options.get(AUTHENTICATION_OPTIONS); + if (authenticationOptionsObj instanceof List) { + List> authenticationOptionsList = (List>) authenticationOptionsObj; + authenticationOptionsObj = IntStream + .range(0, authenticationOptionsList.size()) + .boxed() + .collect(Collectors.toMap(String::valueOf, authenticationOptionsList::get)); + } + + if (authenticationOptionsObj instanceof Map) { + filterOptions((Map>) authenticationOptionsObj, stepConfig); + } else { + log.debug("Authenticator options not provided or invalid, hence proceeding without filtering"); + } + + Object authenticatorParams = options.get(AUTHENTICATOR_PARAMS); + if (authenticatorParams instanceof Map) { + authenticatorParamsOptions((Map) authenticatorParams, stepConfig); + } else { + log.debug("Authenticator params not provided or invalid, hence proceeding without setting params"); + } + + Object stepOptions = options.get(STEP_OPTIONS); + if (stepOptions instanceof Map) { + handleStepOptions(stepConfig, (Map) stepOptions, stepConfigMap); + } else { + log.debug("Step options not provided or invalid, hence proceeding without handling"); + } + } + + @HostAccess.Export + public static void sendErrorAsync(String url, Map parameterMap) { + + FailNode newNode = createFailNode(url, parameterMap, true); + + AuthGraphNode currentNode = dynamicallyBuiltBaseNode.get(); + if (currentNode == null) { + dynamicallyBuiltBaseNode.set(newNode); + } else { + attachToLeaf(currentNode, newNode); + } + } + + /** + * Implementation of the SendErrorFunction interface as an adaptor for sendErrorAsync function. + */ + public static class SendErrorAsyncFunctionImpl implements SendErrorFunction { + + @HostAccess.Export + public void sendError(String url, Map parameterMap) { + sendErrorAsync(url, parameterMap); + } + } + + /** + * Implementation of the SendErrorFunction interface as an adaptor for sendError function. + */ + public class SendErrorFunctionImpl implements SendErrorFunction { + + @HostAccess.Export + public void sendError(String url, Map parameterMap) { + JsGraalGraphBuilder.this.sendError(url, parameterMap); + } + } + private static FailNode createFailNode(String url, Map parameterMap, boolean isShowErrorPage) { + + FailNode failNode = new FailNode(); + if (isShowErrorPage && StringUtils.isNotBlank(url)) { + failNode.setErrorPageUri(url); + } + // setShowErrorPage is set to true as sendError function redirects to a specific error page. + failNode.setShowErrorPage(isShowErrorPage); + + parameterMap.forEach((key, value) -> failNode.getFailureData().put(key, String.valueOf(value))); + return failNode; + } + + /** + * Javascript based Decision Evaluator implementation. + * This is used to create the Authentication Graph structure dynamically on the fly while the authentication flow + * is happening. + * The graph is re-organized based on last execution of the decision. + */ + public class JsBasedEvaluator implements AuthenticationDecisionEvaluator { + + private static final long serialVersionUID = 6853505881096840344L; + private final GraalSerializableJsFunction jsFunction; + + public JsBasedEvaluator(GraalSerializableJsFunction jsFunction) { + + this.jsFunction = jsFunction; + } + + @Override + @HostAccess.Export + public Object evaluate(AuthenticationContext authenticationContext, Object... params) { + + JsGraalGraphBuilder graphBuilder = JsGraalGraphBuilder.this; + Object result = null; + if (jsFunction == null) { + return null; + } + if (!jsFunction.isFunction()) { + return jsFunction.getSource(); + } + try { + currentBuilder.set(graphBuilder); + JsGraalGraphBuilderFactory.restoreCurrentContext(authenticationContext, context); + Context context = getContext(); + Value bindings = context.getBindings(POLYGLOT_LANGUAGE); + + bindings.putMember(JS_FUNC_EXECUTE_STEP, new JsGraalStepExecuterInAsyncEvent()); + bindings.putMember(JS_FUNC_SEND_ERROR, new SendErrorFunctionImpl()); + bindings.putMember(JS_AUTH_FAILURE, new FailAuthenticationFunctionImpl()); + bindings.putMember(JS_FUNC_SHOW_PROMPT, new JsGraalPromptExecutorImpl()); + bindings.putMember(JS_FUNC_LOAD_FUNC_LIB, new JsGraalLoadExecutorImpl()); + JsFunctionRegistry jsFunctionRegistrar = + FrameworkServiceDataHolder.getInstance().getJsFunctionRegistry(); + if (jsFunctionRegistrar != null) { + Map functionMap = + jsFunctionRegistrar.getSubsystemFunctionsMap(JsFunctionRegistry.Subsystem.SEQUENCE_HANDLER); + functionMap.forEach(bindings::putMember); + } + removeDefaultFunctions(context); + JsGraalGraphBuilder.contextForJs.set(authenticationContext); + + String identifier = UUID.randomUUID().toString(); + Optional optionalScriptExecutionData = + Optional.ofNullable(retrieveAuthScriptExecutionMonitorData(authenticationContext)); + try { + startScriptExecutionMonitor(identifier, authenticationContext, + optionalScriptExecutionData.orElse(null)); + result = jsFunction.apply(context, params); + } finally { + optionalScriptExecutionData = Optional.ofNullable(endScriptExecutionMonitor(identifier)); + } + optionalScriptExecutionData.ifPresent( + scriptExecutionData -> storeAuthScriptExecutionMonitorData(authenticationContext, + scriptExecutionData)); + + JsGraalGraphBuilderFactory.persistCurrentContext(authenticationContext, context); + + AuthGraphNode executingNode = (AuthGraphNode) authenticationContext.getProperty(PROP_CURRENT_NODE); + if (canInfuse(executingNode)) { + infuse(executingNode, dynamicallyBuiltBaseNode.get()); + } + + } catch (Throwable e) { + //We need to catch all the javascript errors here, then log and handle. + log.error("Error in executing the javascript for service provider : " + + authenticationContext.getServiceProviderName() + ", Javascript Fragment : \n" + + jsFunction.getSource(), e); + AuthGraphNode executingNode = (AuthGraphNode) authenticationContext.getProperty(PROP_CURRENT_NODE); + FailNode failNode = new FailNode(); + attachToLeaf(executingNode, failNode); + } finally { + contextForJs.remove(); + dynamicallyBuiltBaseNode.remove(); + clearCurrentBuilder(context); + } + return result; + } + } + + private Context getContext() { + + return this.context; + } + + /** + * Adds a function to show a prompt in Javascript code. + * + * @param templateId Identifier of the template + * @param parameters parameters + */ + @SuppressWarnings("unchecked") + @HostAccess.Export + public void addShowPrompt(String templateId, Object... parameters) { + + ShowPromptNode newNode = new ShowPromptNode(); + newNode.setTemplateId(templateId); + + if (parameters.length == 2) { + newNode.setData((Map) GraalSerializer.getInstance().toJsSerializable(parameters[0])); + } + if (currentNode == null) { + result.setStartNode(newNode); + } else { + attachToLeaf(currentNode, newNode); + } + + currentNode = newNode; + if (parameters.length > 0) { + if (parameters[parameters.length - 1] instanceof Map) { + addEventListeners(newNode, (Map) parameters[parameters.length - 1]); + } else { + log.error("Invalid argument and hence ignored. Last argument should be a Map of event listeners."); + } + + } + } + + /** + * GraalJS specific prompt implementation + */ + public class JsGraalPromptExecutorImpl implements PromptExecutor { + + @HostAccess.Export + public void prompt(String templateId, Object... parameters) { + + JsGraalGraphBuilder.this.addShowPrompt(templateId, parameters); + } + } + + private boolean canInfuse(AuthGraphNode executingNode) { + + return executingNode instanceof DynamicDecisionNode && dynamicallyBuiltBaseNode.get() != null; + } + + @HostAccess.Export + public static void failAsync(Object... parameters) { + + Map parameterMap; + + if (parameters.length == 1) { + parameterMap = (Map) parameters[0]; + } else { + parameterMap = Collections.EMPTY_MAP; + } + + FailNode newNode = createFailNode(StringUtils.EMPTY, parameterMap, false); + + AuthGraphNode currentNode = dynamicallyBuiltBaseNode.get(); + if (currentNode == null) { + dynamicallyBuiltBaseNode.set(newNode); + } else { + attachToLeaf(currentNode, newNode); + } + } + + /** + * Fail function implementation for GraalJS. + */ + public static class FailAuthenticationFunctionImpl implements FailAuthenticationFunction { + + @HostAccess.Export + public void fail(Object... parameters) { + + failAsync(parameters); + } + } + + private void removeDefaultFunctions(Context context) throws IOException { + + context.eval(Source.newBuilder(POLYGLOT_LANGUAGE, REMOVE_FUNCTIONS, POLYGLOT_SOURCE).build()); + } + + private JSExecutionSupervisor getJSExecutionSupervisor() { + + return FrameworkServiceDataHolder.getInstance().getJsExecutionSupervisor(); + } + + private void storeAuthScriptExecutionMonitorData(AuthenticationContext context, + JSExecutionMonitorData jsExecutionMonitorData) { + + context.setProperty(PROP_EXECUTION_SUPERVISOR_RESULT, jsExecutionMonitorData); + } + + private JSExecutionMonitorData retrieveAuthScriptExecutionMonitorData(AuthenticationContext context) { + + JSExecutionMonitorData jsExecutionMonitorData; + Object storedResult = context.getProperty(PROP_EXECUTION_SUPERVISOR_RESULT); + if (storedResult != null) { + jsExecutionMonitorData = (JSExecutionMonitorData) storedResult; + } else { + jsExecutionMonitorData = new JSExecutionMonitorData(0L, 0L); + } + return jsExecutionMonitorData; + } + + private void startScriptExecutionMonitor(String identifier, AuthenticationContext context, + JSExecutionMonitorData previousExecutionResult) { + + JSExecutionSupervisor jsExecutionSupervisor = getJSExecutionSupervisor(); + if (jsExecutionSupervisor == null) { + return; + } + getJSExecutionSupervisor().monitor(identifier, context.getServiceProviderName(), context.getTenantDomain(), + previousExecutionResult.getElapsedTime(), previousExecutionResult.getConsumedMemory()); + } + + private void startScriptExecutionMonitor(String identifier, AuthenticationContext context) { + + startScriptExecutionMonitor(identifier, context, new JSExecutionMonitorData(0L, 0L)); + } + + private JSExecutionMonitorData endScriptExecutionMonitor(String identifier) { + + JSExecutionSupervisor executionSupervisor = getJSExecutionSupervisor(); + if (executionSupervisor == null) { + return null; + } + return getJSExecutionSupervisor().completed(identifier); + } + +} diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/graaljs/JsGraalGraphBuilderFactory.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/graaljs/JsGraalGraphBuilderFactory.java new file mode 100644 index 000000000000..922657b1dea1 --- /dev/null +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/graaljs/JsGraalGraphBuilderFactory.java @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.application.authentication.framework.config.model.graph.graaljs; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.graalvm.polyglot.Context; +import org.graalvm.polyglot.HostAccess; +import org.graalvm.polyglot.ResourceLimits; +import org.graalvm.polyglot.Value; +import org.wso2.carbon.identity.application.authentication.framework.config.model.StepConfig; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.AuthGraphNode; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.JsBaseGraphBuilder; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.JsGenericGraphBuilderFactory; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.JsGenericSerializer; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.AbstractJSObjectWrapper; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.JsAuthenticatedUser; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.JsAuthenticationContext; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.JsLogger; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.JsServletRequest; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.JsServletResponse; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.graaljs.JsGraalAuthenticatedUser; +import org.wso2.carbon.identity.application.authentication.framework.context.AuthenticationContext; +import org.wso2.carbon.identity.application.authentication.framework.exception.FrameworkException; +import org.wso2.carbon.identity.application.authentication.framework.handler.sequence.impl.GraalSelectAcrFromFunction; +import org.wso2.carbon.identity.core.util.IdentityUtil; + +import java.util.HashMap; +import java.util.Map; + +import static org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants.AdaptiveAuthentication.DEFAULT_GRAALJS_SCRIPT_STATEMENTS_LIMIT; +import static org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants.AdaptiveAuthentication.GRAALJS_SCRIPT_STATEMENTS_LIMIT; +import static org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants.JSAttributes.JS_FUNC_SELECT_ACR_FROM; +import static org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants.JSAttributes.JS_LOG; +import static org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants.JSAttributes.POLYGLOT_LANGUAGE; + +/** + * Factory to create a Javascript based sequence builder. + * This factory is there to reuse of GraalJS Polyglot Context and any related expensive objects. + *

+ * Since Nashorn is deprecated in JDK 11 and onwards. We are introducing GraalJS engine. + */ +public class JsGraalGraphBuilderFactory implements JsGenericGraphBuilderFactory { + + private static final Log LOG = LogFactory.getLog(JsGraalGraphBuilderFactory.class); + private static final String JS_BINDING_CURRENT_CONTEXT = "JS_BINDING_CURRENT_CONTEXT"; + private int javascriptResourceLimit = 0; + + public void init() { + + setJavascriptResourceLimit(); + } + + @SuppressWarnings("unchecked") + public static void restoreCurrentContext(AuthenticationContext authContext, Context context) + throws FrameworkException { + + Map map = (Map) authContext.getProperty(JS_BINDING_CURRENT_CONTEXT); + Value bindings = context.getBindings(POLYGLOT_LANGUAGE); + if (map != null) { + for (Map.Entry entry : map.entrySet()) { + Object deserializedValue = GraalSerializer.getInstance().fromJsSerializable(entry.getValue(), context); + if (deserializedValue instanceof AbstractJSObjectWrapper) { + ((AbstractJSObjectWrapper) deserializedValue).initializeContext(authContext); + } + bindings.putMember(entry.getKey(), deserializedValue); + } + } + } + + public static void persistCurrentContext(AuthenticationContext authContext, Context context) { + + Value engineBindings = context.getBindings(POLYGLOT_LANGUAGE); + Map persistableMap = new HashMap<>(); + engineBindings.getMemberKeys().forEach((key) -> { + Value binding = engineBindings.getMember(key); + /* + * Since, we don't have a difference between global and engine scopes, we need to identify what are the + * custom functions and the logger object we added bindings to, and not persist them since we will anyways + * bind them again. + * The functions will be host objects and can be executed. The Logger object needs to be identified by name. + */ + if (!((binding.isHostObject() && binding.canExecute()) || key.equals("Log"))) { + persistableMap.put(key, GraalSerializer.getInstance().toJsSerializable(binding)); + } + }); + authContext.setProperty(JS_BINDING_CURRENT_CONTEXT, persistableMap); + } + + public Context createEngine(AuthenticationContext authenticationContext) { + + Context context = Context.newBuilder(POLYGLOT_LANGUAGE) + .allowHostAccess(getHostAccess()) + .resourceLimits(getResourceLimits()) + .option("engine.WarnInterpreterOnly", "false") + .build(); + + Value bindings = context.getBindings(POLYGLOT_LANGUAGE); + bindings.putMember(JS_FUNC_SELECT_ACR_FROM, new GraalSelectAcrFromFunction()); + bindings.putMember(JS_LOG, new JsLogger()); + return context; + } + + public ResourceLimits getResourceLimits() { + + ResourceLimits.Builder resourceLimitsBuilder = ResourceLimits.newBuilder(); + resourceLimitsBuilder.statementLimit(javascriptResourceLimit, null); + return resourceLimitsBuilder.build(); + } + + public HostAccess getHostAccess() { + + /* + * We need to map the graaljs proxy objects be exposed as their abstract classes to be able to use the current + * functional interfaces we have for existing conditional authentication functions. + */ + return HostAccess.newBuilder(HostAccess.EXPLICIT) + .targetTypeMapping(Value.class, JsAuthenticationContext.class, + (v) -> v.asProxyObject() instanceof JsAuthenticationContext, + (v) -> (JsAuthenticationContext) v.asProxyObject()) + .targetTypeMapping(Value.class, JsAuthenticatedUser.class, + (v) -> v.asProxyObject() instanceof JsGraalAuthenticatedUser, + (v) -> (JsAuthenticatedUser) v.asProxyObject()) + .targetTypeMapping(Value.class, JsServletRequest.class, + (v) -> v.asProxyObject() instanceof JsServletRequest, + (v) -> (JsServletRequest) v.asProxyObject()) + .targetTypeMapping(Value.class, JsServletResponse.class, + (v) -> v.asProxyObject() instanceof JsServletResponse, + (v) -> (JsServletResponse) v.asProxyObject()) + .build(); + } + + @Override + public JsGenericSerializer getJsUtil() { + + return GraalSerializer.getInstance(); + } + + @Override + public JsBaseGraphBuilder getCurrentBuilder() { + + return JsGraalGraphBuilder.getCurrentBuilder(); + } + + public JsGraalGraphBuilder createBuilder(AuthenticationContext authenticationContext, + Map stepConfigMap) { + + return new JsGraalGraphBuilder(authenticationContext, stepConfigMap, createEngine(authenticationContext)); + } + + public JsGraalGraphBuilder createBuilder(AuthenticationContext authenticationContext, + Map stepConfigMap, AuthGraphNode currentNode) { + + return new JsGraalGraphBuilder(authenticationContext, stepConfigMap, createEngine(authenticationContext), + currentNode); + } + + private void setJavascriptResourceLimit() { + + /* + * This sets the number of javascript statements that can be executed in a single execution. + * The default value is set to 0 which is equivalent to unlimited number of statement. + */ + String statementLimit = IdentityUtil.getProperty(GRAALJS_SCRIPT_STATEMENTS_LIMIT); + if (statementLimit != null) { + try { + javascriptResourceLimit = Integer.parseInt(statementLimit); + } catch (NumberFormatException e) { + LOG.warn("Error while parsing the script statement limit. Defaulting to " + + DEFAULT_GRAALJS_SCRIPT_STATEMENTS_LIMIT, e); + javascriptResourceLimit = DEFAULT_GRAALJS_SCRIPT_STATEMENTS_LIMIT; + } + } else { + javascriptResourceLimit = DEFAULT_GRAALJS_SCRIPT_STATEMENTS_LIMIT; + } + } +} diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/graaljs/JsGraalWrapperFactory.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/graaljs/JsGraalWrapperFactory.java new file mode 100644 index 000000000000..7222f58f2261 --- /dev/null +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/graaljs/JsGraalWrapperFactory.java @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.application.authentication.framework.config.model.graph.graaljs; + +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.JsWrapperBaseFactory; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.graaljs.JsGraalAuthenticatedUser; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.graaljs.JsGraalAuthenticationContext; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.graaljs.JsGraalClaims; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.graaljs.JsGraalCookie; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.graaljs.JsGraalHeaders; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.graaljs.JsGraalParameters; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.graaljs.JsGraalRuntimeClaims; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.graaljs.JsGraalServletRequest; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.graaljs.JsGraalServletResponse; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.graaljs.JsGraalStep; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.graaljs.JsGraalSteps; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.graaljs.JsGraalWritableParameters; +import org.wso2.carbon.identity.application.authentication.framework.context.AuthenticationContext; +import org.wso2.carbon.identity.application.authentication.framework.context.TransientObjectWrapper; +import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser; + +import java.util.Map; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * Factory to create a Javascript Object Wrappers for GraalJS execution. + * Since Nashorn is deprecated in JDK 11 and onwards. We are introducing GraalJS engine. + */ +public class JsGraalWrapperFactory implements JsWrapperBaseFactory { + + @Override + public JsGraalAuthenticatedUser createJsAuthenticatedUser(AuthenticatedUser authenticatedUser) { + + return new JsGraalAuthenticatedUser(authenticatedUser); + } + + @Override + public JsGraalAuthenticatedUser createJsAuthenticatedUser(AuthenticationContext authenticationContext, + AuthenticatedUser authenticatedUser) { + + return new JsGraalAuthenticatedUser(authenticationContext, authenticatedUser); + } + + @Override + public JsGraalAuthenticatedUser createJsAuthenticatedUser(AuthenticationContext context, + AuthenticatedUser wrappedUser, int step, String idp) { + + return new JsGraalAuthenticatedUser(context, wrappedUser, step, idp); + } + + @Override + public JsGraalAuthenticationContext createJsAuthenticationContext + (AuthenticationContext authenticationContext) { + + return new JsGraalAuthenticationContext(authenticationContext); + } + + @Override + public JsGraalCookie createJsCookie(Cookie cookie) { + + return new JsGraalCookie(cookie); + } + + @Override + public JsGraalParameters createJsParameters(Map parameters) { + + return new JsGraalParameters(parameters); + } + + @Override + public JsGraalWritableParameters createJsWritableParameters(Map data) { + + return new JsGraalWritableParameters(data); + } + + @Override + public JsGraalServletRequest createJsServletRequest(TransientObjectWrapper wrapped) { + + return new JsGraalServletRequest(wrapped); + } + + @Override + public JsGraalServletResponse createJsServletResponse(TransientObjectWrapper wrapped) { + + return new JsGraalServletResponse(wrapped); + } + + @Override + public JsGraalClaims createJsClaims(AuthenticationContext context, int step, String idp, + boolean isRemoteClaimRequest) { + + return new JsGraalClaims(context, step, idp, isRemoteClaimRequest); + } + + @Override + public JsGraalClaims createJsClaims(AuthenticationContext context, AuthenticatedUser user, + boolean isRemoteClaimRequest) { + + return new JsGraalClaims(context, user, isRemoteClaimRequest); + } + + @Override + public JsGraalRuntimeClaims createJsRuntimeClaims(AuthenticationContext context, int step, String idp) { + + return new JsGraalRuntimeClaims(context, step, idp); + } + + @Override + public JsGraalRuntimeClaims createJsRuntimeClaims(AuthenticationContext context, AuthenticatedUser user) { + + return new JsGraalRuntimeClaims(context, user); + } + + @Override + public JsGraalStep createJsStep(AuthenticationContext context, int step, String authenticatedIdp, + String authenticatedAuthenticator) { + + return new JsGraalStep(context, step, authenticatedIdp, authenticatedAuthenticator); + } + + @Override + public JsGraalHeaders createJsHeaders(Map wrapped, HttpServletResponse response) { + + return new JsGraalHeaders(wrapped, response); + } + + @Override + public JsGraalSteps createJsSteps(AuthenticationContext context) { + + return new JsGraalSteps(context); + } + +} diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/AbstractJSContextMemberObject.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/AbstractJSContextMemberObject.java index 0f918d307e8d..7247bf0dacdc 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/AbstractJSContextMemberObject.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/AbstractJSContextMemberObject.java @@ -1,7 +1,7 @@ /* - * Copyright (c) 2018, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2018-2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. * - * WSO2 Inc. licenses this file to you under the Apache License, + * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at @@ -21,6 +21,7 @@ import org.wso2.carbon.identity.application.authentication.framework.context.AuthenticationContext; import java.io.Serializable; +import java.util.Objects; /** * Represents the abstract class for all context objects @@ -48,4 +49,10 @@ public AuthenticationContext getContext() { return context; } + + public boolean hasMember (String name) { + + Objects.requireNonNull(name); + return false; + } } diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/CommonJsHeaders.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/CommonJsHeaders.java new file mode 100644 index 000000000000..aaca0d5e5a01 --- /dev/null +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/CommonJsHeaders.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js; + +import java.util.Map; + +import javax.servlet.http.HttpServletResponse; + +/** + * Javascript wrapper for Java level HashMap of HTTP headers. + * This provides controlled access to HTTPServletResponse object's headers via provided javascript native syntax. + * Also it prevents writing an arbitrary values to the respective fields, keeping consistency on runtime. + */ +public class CommonJsHeaders { + + private final Map wrapped; + private final HttpServletResponse response; + + public CommonJsHeaders(Map wrapped, HttpServletResponse response) { + + this.wrapped = wrapped; + this.response = response; + } + + public Object getMember(String name) { + + if (wrapped == null) { + return null; + } + return wrapped.get(name); + } + + public Object getMemberKeys() { + + return wrapped.keySet().toArray(); + } + + public boolean hasMember(String name) { + + if (wrapped == null) { + return false; + } + return wrapped.get(name) != null; + } + + public boolean removeMemberObject(String name) { + + if (wrapped != null) { + wrapped.remove(name); + return true; + } + return false; + } + + public boolean setMemberObject(String name, Object value) { + + if (wrapped != null) { + wrapped.put(name, value); + // Adds a new header to the response. + response.addHeader(name, String.valueOf(value)); + return true; + } + return false; + } +} diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/JsAuthenticatedUser.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/JsAuthenticatedUser.java index d31a9b51ae22..c543da0d4bfd 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/JsAuthenticatedUser.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/JsAuthenticatedUser.java @@ -19,8 +19,10 @@ package org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js; import org.apache.commons.lang.ArrayUtils; +import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.JsWrapperFactoryProvider; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.base.JsBaseAuthenticatedUser; import org.wso2.carbon.identity.application.authentication.framework.context.AuthenticationContext; import org.wso2.carbon.identity.application.authentication.framework.exception.UserIdNotFoundException; @@ -101,6 +103,71 @@ public JsAuthenticatedUser(AuthenticationContext context, AuthenticatedUser wrap initializeContext(context); } + public Object getMember(String name) { + + switch (name) { + case FrameworkConstants.JSAttributes.JS_AUTHENTICATED_SUBJECT_IDENTIFIER: + return getWrapped().getAuthenticatedSubjectIdentifier(); + case FrameworkConstants.JSAttributes.JS_USERNAME: + return getWrapped().getUserName(); + case FrameworkConstants.JSAttributes.JS_UNIQUE_ID: + Object userId = null; + try { + userId = getWrapped().getUserId(); + } catch (UserIdNotFoundException e) { + LOG.error("Error while retrieving user Id of user : " + getWrapped().getLoggableUserId(), e); + } + return userId; + case FrameworkConstants.JSAttributes.JS_USER_STORE_DOMAIN: + return getWrapped().getUserStoreDomain(); + case FrameworkConstants.JSAttributes.JS_TENANT_DOMAIN: + return getWrapped().getTenantDomain(); + case FrameworkConstants.JSAttributes.JS_LOCAL_CLAIMS: + if (StringUtils.isNotBlank(idp)) { + return JsWrapperFactoryProvider.getInstance().getWrapperFactory() + .createJsClaims(getContext(), step, idp, false); + } + // Represent step independent user + return JsWrapperFactoryProvider.getInstance().getWrapperFactory() + .createJsClaims(getContext(), getWrapped(), false); + case FrameworkConstants.JSAttributes.JS_REMOTE_CLAIMS: + if (StringUtils.isNotBlank(idp)) { + return JsWrapperFactoryProvider.getInstance().getWrapperFactory() + .createJsClaims(getContext(), step, idp, true); + } + // Represent step independent user + return JsWrapperFactoryProvider.getInstance().getWrapperFactory() + .createJsClaims(getContext(), getWrapped(), true); + + case FrameworkConstants.JSAttributes.JS_LOCAL_ROLES: + return getLocalRoles(); + case FrameworkConstants.JSAttributes.JS_CLAIMS: + if (StringUtils.isNotBlank(idp)) { + return JsWrapperFactoryProvider.getInstance().getWrapperFactory() + .createJsRuntimeClaims(getContext(), step, idp); + } + // Represent step independent user + return JsWrapperFactoryProvider.getInstance().getWrapperFactory() + .createJsRuntimeClaims(getContext(), getWrapped()); + default: + return super.getMember(name); + } + } + + public Object getMemberKeys() { + + return new String[]{ + FrameworkConstants.JSAttributes.JS_AUTHENTICATED_SUBJECT_IDENTIFIER, + FrameworkConstants.JSAttributes.JS_USERNAME, + FrameworkConstants.JSAttributes.JS_USER_STORE_DOMAIN, + FrameworkConstants.JSAttributes.JS_TENANT_DOMAIN, + FrameworkConstants.JSAttributes.JS_LOCAL_CLAIMS, + FrameworkConstants.JSAttributes.JS_REMOTE_CLAIMS, + FrameworkConstants.JSAttributes.JS_LOCAL_ROLES, + FrameworkConstants.JSAttributes.JS_CLAIMS + }; + } + public void setMember(String name, Object value) { switch (name) { @@ -126,10 +193,10 @@ public boolean hasMember(String name) { return getWrapped().getUserStoreDomain() != null; case FrameworkConstants.JSAttributes.JS_TENANT_DOMAIN: return getWrapped().getTenantDomain() != null; + case FrameworkConstants.JSAttributes.JS_CLAIMS: case FrameworkConstants.JSAttributes.JS_LOCAL_CLAIMS: - return idp != null; case FrameworkConstants.JSAttributes.JS_REMOTE_CLAIMS: - return idp != null && !FrameworkConstants.LOCAL.equals(idp); + return true; default: return super.hasMember(name); } diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/JsAuthenticationContext.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/JsAuthenticationContext.java index ca88bee0e9cc..e988d7508798 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/JsAuthenticationContext.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/JsAuthenticationContext.java @@ -19,9 +19,12 @@ package org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js; import org.wso2.carbon.identity.application.authentication.framework.config.model.StepConfig; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.JsWrapperFactoryProvider; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.base.JsBaseAuthenticatedUser; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.base.JsBaseAuthenticationContext; import org.wso2.carbon.identity.application.authentication.framework.context.AuthenticationContext; import org.wso2.carbon.identity.application.authentication.framework.context.TransientObjectWrapper; +import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser; import org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants; import java.util.Map; @@ -70,21 +73,91 @@ public boolean hasMember(String name) { return !getWrapped().getSequenceConfig().getStepMap().isEmpty(); case FrameworkConstants.JSAttributes.JS_ENDPOINT_PARAMS: return getWrapped().getEndpointParams() != null; + case FrameworkConstants.JSAttributes.JS_CURRENT_STEP: + case FrameworkConstants.JSAttributes.JS_RETRY_STEP: + return true; + case FrameworkConstants.JSAttributes.JS_CURRENT_KNOWN_SUBJECT: + return getCurrentSubjectIdentifierStep() != null; default: return super.hasMember(name); } } - @Override - public void setMember(String name, Object value) { + public Object getMember(String name) { switch (name) { - case FrameworkConstants.JSAttributes.JS_SELECTED_ACR: - getWrapped().setSelectedAcr(String.valueOf(value)); - break; + case FrameworkConstants.JSAttributes.JS_REQUESTED_ACR: + return getWrapped().getRequestedAcr(); + case FrameworkConstants.JSAttributes.JS_TENANT_DOMAIN: + return getWrapped().getTenantDomain(); + case FrameworkConstants.JSAttributes.JS_SERVICE_PROVIDER_NAME: + return getWrapped().getServiceProviderName(); + case FrameworkConstants.JSAttributes.JS_LAST_LOGIN_FAILED_USER: + return getLastLoginFailedUserFromWrappedContext(); + case FrameworkConstants.JSAttributes.JS_REQUEST: + return JsWrapperFactoryProvider.getInstance().getWrapperFactory() + .createJsServletRequest((TransientObjectWrapper) getWrapped() + .getParameter(FrameworkConstants.RequestAttribute.HTTP_REQUEST)); + case FrameworkConstants.JSAttributes.JS_RESPONSE: + return JsWrapperFactoryProvider.getInstance().getWrapperFactory() + .createJsServletResponse((TransientObjectWrapper) getWrapped() + .getParameter(FrameworkConstants.RequestAttribute.HTTP_RESPONSE)); + case FrameworkConstants.JSAttributes.JS_STEPS: + return JsWrapperFactoryProvider.getInstance().getWrapperFactory().createJsSteps(getWrapped()); + case FrameworkConstants.JSAttributes.JS_CURRENT_STEP: + return JsWrapperFactoryProvider.getInstance().getWrapperFactory() + .createJsStep(getContext(), getContext().getCurrentStep(), getAuthenticatedIdPOfCurrentStep(), + getAuthenticatedAuthenticatorOfCurrentStep()); + case FrameworkConstants.JSAttributes.JS_CURRENT_KNOWN_SUBJECT: + StepConfig stepConfig = getCurrentSubjectIdentifierStep(); + if (stepConfig != null) { + return JsWrapperFactoryProvider.getInstance().getWrapperFactory() + .createJsAuthenticatedUser(this.getContext(), stepConfig.getAuthenticatedUser(), + stepConfig.getOrder(), stepConfig.getAuthenticatedIdP()); + } + return null; + case FrameworkConstants.JSAttributes.JS_RETRY_STEP: + return getWrapped().isRetrying(); + case FrameworkConstants.JSAttributes.JS_ENDPOINT_PARAMS: + return JsWrapperFactoryProvider.getInstance().getWrapperFactory() + .createJsWritableParameters(getContext().getEndpointParams()); default: - super.setMember(name, value); + return super.getMember(name); + } + } + + public Object getMemberKeys() { + + return new String[]{ + FrameworkConstants.JSAttributes.JS_REQUESTED_ACR, + FrameworkConstants.JSAttributes.JS_TENANT_DOMAIN, + FrameworkConstants.JSAttributes.JS_SERVICE_PROVIDER_NAME, + FrameworkConstants.JSAttributes.JS_LAST_LOGIN_FAILED_USER, + FrameworkConstants.JSAttributes.JS_REQUEST, + FrameworkConstants.JSAttributes.JS_RESPONSE, + FrameworkConstants.JSAttributes.JS_STEPS, + FrameworkConstants.JSAttributes.JS_CURRENT_STEP, + FrameworkConstants.JSAttributes.JS_CURRENT_KNOWN_SUBJECT, + FrameworkConstants.JSAttributes.JS_RETRY_STEP}; + } + + public boolean setMemberObject(String name, Object value) { + + if (name.equals(FrameworkConstants.JSAttributes.JS_SELECTED_ACR)) { + getWrapped().setSelectedAcr(String.valueOf(value)); + } else { + super.setMember(name, value); + } + return true; + } + + public boolean removeMemberObject(String name) { + + if (name.equals(FrameworkConstants.JSAttributes.JS_SELECTED_ACR)) { + getWrapped().setSelectedAcr(null); + return true; } + return false; } private boolean hasTransientValueInParameters(String key) { @@ -93,18 +166,6 @@ private boolean hasTransientValueInParameters(String key) { return transientObjectWrapper != null && transientObjectWrapper.getWrapped() != null; } -// protected JsAbstractAuthenticatedUser getLastLoginFailedUserFromWrappedContext() { -// -// Object lastLoginFailedUser -// = getWrapped().getProperty(FrameworkConstants.JSAttributes.JS_LAST_LOGIN_FAILED_USER); -// if (lastLoginFailedUser instanceof AuthenticatedUser) { -// return new JsAbstractAuthenticatedUser(getWrapped(), (AuthenticatedUser) lastLoginFailedUser); -// } else { -// return null; -// } -// } - - protected String getAuthenticatedIdPOfCurrentStep() { if (getContext().getSequenceConfig() == null) { @@ -121,6 +182,19 @@ protected String getAuthenticatedIdPOfCurrentStep() { } + protected String getAuthenticatedAuthenticatorOfCurrentStep() { + + if (getContext().getSequenceConfig() == null) { + // Sequence config is not yet initialized. + return null; + } + + StepConfig stepConfig = getContext().getSequenceConfig().getStepMap() + .get(getContext().getCurrentStep()); + + return stepConfig != null ? stepConfig.getAuthenticatedAutenticator().getName() : null; + } + protected StepConfig getCurrentSubjectIdentifierStep() { if (getContext().getSequenceConfig() == null) { @@ -136,8 +210,18 @@ protected StepConfig getCurrentSubjectIdentifierStep() { return subjectIdentifierStep.get(); } else if (getContext().getCurrentStep() > 0) { return stepConfigs.get(getContext().getCurrentStep()); - } else { - return null; } + return null; + } + + protected JsBaseAuthenticatedUser getLastLoginFailedUserFromWrappedContext() { + + Object lastLoginFailedUser + = getWrapped().getProperty(FrameworkConstants.JSAttributes.JS_LAST_LOGIN_FAILED_USER); + if (lastLoginFailedUser instanceof AuthenticatedUser) { + return JsWrapperFactoryProvider.getInstance().getWrapperFactory() + .createJsAuthenticatedUser(getWrapped(), (AuthenticatedUser) lastLoginFailedUser); + } + return null; } } diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/JsClaims.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/JsClaims.java new file mode 100644 index 000000000000..99b82ba49fa0 --- /dev/null +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/JsClaims.java @@ -0,0 +1,474 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js; + +import org.apache.commons.lang.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.identity.application.authentication.framework.ApplicationAuthenticator; +import org.wso2.carbon.identity.application.authentication.framework.config.ConfigurationFacade; +import org.wso2.carbon.identity.application.authentication.framework.config.model.ExternalIdPConfig; +import org.wso2.carbon.identity.application.authentication.framework.config.model.StepConfig; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.base.JsBaseClaims; +import org.wso2.carbon.identity.application.authentication.framework.context.AuthenticationContext; +import org.wso2.carbon.identity.application.authentication.framework.exception.UserIdNotFoundException; +import org.wso2.carbon.identity.application.authentication.framework.internal.FrameworkServiceDataHolder; +import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser; +import org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants; +import org.wso2.carbon.identity.application.authentication.framework.util.FrameworkUtils; +import org.wso2.carbon.identity.application.common.model.ClaimMapping; +import org.wso2.carbon.identity.application.mgt.ApplicationConstants; +import org.wso2.carbon.identity.base.IdentityException; +import org.wso2.carbon.identity.claim.metadata.mgt.ClaimMetadataHandler; +import org.wso2.carbon.identity.claim.metadata.mgt.exception.ClaimMetadataException; +import org.wso2.carbon.identity.core.IdentityClaimManager; +import org.wso2.carbon.identity.core.util.IdentityTenantUtil; +import org.wso2.carbon.idp.mgt.IdentityProviderManagementException; +import org.wso2.carbon.idp.mgt.IdentityProviderManager; +import org.wso2.carbon.user.api.UserRealm; +import org.wso2.carbon.user.api.UserStoreException; +import org.wso2.carbon.user.core.UserStoreClientException; +import org.wso2.carbon.user.core.claim.Claim; +import org.wso2.carbon.user.core.common.AbstractUserStoreManager; +import org.wso2.carbon.user.core.service.RealmService; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +/** + * Represent the user's claim for Javascript Execution. This contains the common methods for all script engines. + */ +public abstract class JsClaims extends AbstractJSContextMemberObject implements JsBaseClaims { + + private static final Log LOG = LogFactory.getLog(JsClaims.class); + private String idp; + private boolean isRemoteClaimRequest; + private int step; + protected transient AuthenticatedUser authenticatedUser; + + /** + * Constructor to get the user authenticated in step 'n'. + * + * @param step The authentication step + * @param idp The authenticated IdP + * @param isRemoteClaimRequest Whether the request is for remote claim (false for local claim request) + */ + public JsClaims(AuthenticationContext context, int step, String idp, boolean isRemoteClaimRequest) { + + this(step, idp, isRemoteClaimRequest); + initializeContext(context); + } + + public JsClaims(int step, String idp, boolean isRemoteClaimRequest) { + + this.isRemoteClaimRequest = isRemoteClaimRequest; + this.idp = idp; + this.step = step; + } + + public JsClaims() { + + } + + @Override + public void initializeContext(AuthenticationContext context) { + + super.initializeContext(context); + if (StringUtils.isNotBlank(idp) && getContext().getCurrentAuthenticatedIdPs().containsKey(idp)) { + this.authenticatedUser = getContext().getCurrentAuthenticatedIdPs().get(idp).getUser(); + } else { + this.authenticatedUser = getAuthenticatedUserFromSubjectIdentifierStep(); + } + } + + /** + * Get authenticated user from step config of current subject identifier. + * + * @return AuthenticatedUser. + */ + private AuthenticatedUser getAuthenticatedUserFromSubjectIdentifierStep() { + + AuthenticatedUser authenticatedUser = null; + StepConfig stepConfig = getCurrentSubjectIdentifierStep(); + if (stepConfig != null) { + authenticatedUser = getCurrentSubjectIdentifierStep().getAuthenticatedUser(); + } + return authenticatedUser; + } + + /** + * Retrieve step config of current subject identifier. + * + * @return StepConfig. + */ + private StepConfig getCurrentSubjectIdentifierStep() { + + if (getContext().getSequenceConfig() == null) { + // Sequence config is not yet initialized. + return null; + } + Map stepConfigs = getContext().getSequenceConfig().getStepMap(); + Optional subjectIdentifierStep = stepConfigs.values().stream() + .filter(stepConfig -> (stepConfig.isCompleted() && stepConfig.isSubjectIdentifierStep())).findFirst(); + if (subjectIdentifierStep.isPresent()) { + return subjectIdentifierStep.get(); + } else if (getContext().getCurrentStep() > 0) { + return stepConfigs.get(getContext().getCurrentStep()); + } + return null; + } + + /** + * Constructor to get user who is not directly from an authentication step. E.g. Associated user of authenticated + * federated user in an authentication step. + * + * @param authenticatedUser Authenticated user + * @param isRemoteClaimRequest Whether the request is for remote claim (false for local claim request) + */ + public JsClaims(AuthenticatedUser authenticatedUser, boolean isRemoteClaimRequest) { + + this.isRemoteClaimRequest = isRemoteClaimRequest; + this.authenticatedUser = authenticatedUser; + } + + public JsClaims(AuthenticationContext context, AuthenticatedUser authenticatedUser, boolean isRemoteClaimRequest) { + + this(authenticatedUser, isRemoteClaimRequest); + initializeContext(context); + } + + public Object getMember(String claimUri) { + + if (authenticatedUser != null) { + if (isRemoteClaimRequest) { + return getFederatedClaim(claimUri); + } else { + return getLocalClaim(claimUri); + } + } + return null; + } + + public boolean hasMember(String claimUri) { + + if (authenticatedUser != null) { + if (isRemoteClaimRequest) { + return hasFederatedClaim(claimUri); + } else { + return hasLocalClaim(claimUri); + } + } + return false; + } + + public boolean setMemberObject(String claimUri, Object claimValue) { + + if (authenticatedUser != null) { + if (isRemoteClaimRequest) { + setFederatedClaim(claimUri, String.valueOf(claimValue)); + } else { + setLocalClaim(claimUri, String.valueOf(claimValue)); + } + return true; + } + return false; + } + + /** + * Get the claim by local claim URI. + * + * @param claimUri Local claim URI + * @param claimValue Claim Value + */ + private void setLocalClaim(String claimUri, String claimValue) { + + if (isFederatedIdP()) { + setLocalMappedClaim(claimUri, claimValue); + } else { + /* This covers step with a local authenticator, and the scenarios where step/idp is not set + if the step/idp is not set, user is assumed to be a local user. */ + setLocalUserClaim(claimUri, claimValue); + } + } + + /** + * Sets the remote claim value that is mapped to the give local claim. + * + * @param localClaimURI Local claim URI + * @param claimValue Value to be set + */ + private void setLocalMappedClaim(String localClaimURI, String claimValue) { + + Map idpAttributesMap = authenticatedUser.getUserAttributes(); + Map remoteMapping = FrameworkUtils.getClaimMappings(idpAttributesMap, false); + String mappedRemoteClaim = getRemoteClaimMappedToLocalClaim(localClaimURI, remoteMapping); + if (mappedRemoteClaim != null) { + setFederatedClaim(mappedRemoteClaim, claimValue); + } + } + + /** + * Sets a local claim directly at the userstore for the given user by given claim uri. + * + * @param claimUri Local claim URI + * @param claimValue Claim value + */ + private void setLocalUserClaim(String claimUri, Object claimValue) { + + int usersTenantId = IdentityTenantUtil.getTenantId(authenticatedUser.getTenantDomain()); + RealmService realmService = FrameworkServiceDataHolder.getInstance().getRealmService(); + try { + UserRealm userRealm = realmService.getTenantUserRealm(usersTenantId); + Map claimUriMap = new HashMap<>(); + claimUriMap.put(claimUri, String.valueOf(claimValue)); + ((AbstractUserStoreManager) userRealm.getUserStoreManager()) + .setUserClaimValuesWithID(authenticatedUser.getUserId(), claimUriMap, null); + } catch (UserStoreClientException e) { + if (LOG.isDebugEnabled()) { + LOG.debug(String.format("Error when setting claim : %s of user: %s to value: %s. Error Message: %s", + claimUri, authenticatedUser, claimValue, e.getMessage())); + } + } catch (UserStoreException e) { + LOG.error(String.format("Error when setting claim : %s of user: %s to value: %s", claimUri, + authenticatedUser, claimValue), e); + } catch (UserIdNotFoundException e) { + LOG.error("User id is not available for the user: " + authenticatedUser.getLoggableMaskedUserId(), e); + } + } + + /** + * Gets the remote claim that is mapped to the given local claim. + * + * @param localClaim local claim URI + * @param remoteClaimsMap Remote claim URI - value map + * @return Mapped remote claim URI if present. null otherwise + */ + private String getRemoteClaimMappedToLocalClaim(String localClaim, Map remoteClaimsMap) { + + String authenticatorDialect = null; + Map localToIdpClaimMapping; + String tenantDomain = getContext().getTenantDomain(); + try { + /* Check if the IDP use a standard dialect (like oidc), If it does, dialect claim mapping are + prioritized over IdP claim mapping. */ + ApplicationAuthenticator authenticator = + getContext().getSequenceConfig().getStepMap().get(step).getAuthenticatedAutenticator() + .getApplicationAuthenticator(); + authenticatorDialect = authenticator.getClaimDialectURI(); + ExternalIdPConfig idPConfig = ConfigurationFacade.getInstance().getIdPConfigByName(idp, tenantDomain); + boolean useDefaultIdpDialect = idPConfig.useDefaultLocalIdpDialect(); + + if (authenticatorDialect != null || useDefaultIdpDialect) { + if (authenticatorDialect == null) { + authenticatorDialect = ApplicationConstants.LOCAL_IDP_DEFAULT_CLAIM_DIALECT; + } + localToIdpClaimMapping = ClaimMetadataHandler.getInstance() + .getMappingsMapFromOtherDialectToCarbon(authenticatorDialect, remoteClaimsMap.keySet(), + tenantDomain, true); + } else { + localToIdpClaimMapping = IdentityProviderManager.getInstance() + .getMappedIdPClaimsMap(idp, tenantDomain, Collections.singletonList(localClaim)); + } + if (localToIdpClaimMapping != null) { + return localToIdpClaimMapping.get(localClaim); + } + } catch (IdentityProviderManagementException e) { + LOG.error(String.format("Error when getting claim : %s of user: %s", localClaim, authenticatedUser), e); + } catch (ClaimMetadataException e) { + LOG.error("Error when getting claim mappings from " + authenticatorDialect + " for tenant domain: " + + tenantDomain); + } + return null; + } + + protected boolean hasLocalClaim(String claimUri) { + + int usersTenantId = IdentityTenantUtil.getTenantId(authenticatedUser.getTenantDomain()); + RealmService realmService = FrameworkServiceDataHolder.getInstance().getRealmService(); + try { + UserRealm userRealm = realmService.getTenantUserRealm(usersTenantId); + Claim[] supportedClaims = IdentityClaimManager.getInstance() + .getAllSupportedClaims((org.wso2.carbon.user.core.UserRealm) userRealm); + for (Claim claim : supportedClaims) { + if (claim.getClaimUri().equals(claimUri)) { + return true; + } + } + } catch (UserStoreException e) { + LOG.error("Error when retrieving user realm for tenant : " + usersTenantId, e); + } catch (IdentityException e) { + LOG.error("Error when initializing identity claim manager.", e); + } + return false; + } + + /** + * Check if the user has a federated claim with given name. + * + * @param claimUri Federated claim URI + * @return true if the IdP is federated, and it has a claim for user with given URI. + * false otherwise + */ + protected boolean hasFederatedClaim(String claimUri) { + + if (isFederatedIdP()) { + Map attributesMap = authenticatedUser.getUserAttributes(); + Map remoteMapping = FrameworkUtils.getClaimMappings(attributesMap, false); + return remoteMapping.containsKey(claimUri); + } + // Can be a case where step is not set (e.g. associated local user) + return false; + } + + /** + * Get the claim by federated claim URI. + * + * @param claimUri Federated claim URI + * @return Claim value if the Idp is a federated Idp, and has a claim by given url for the user. + * null otherwise. + */ + protected String getFederatedClaim(String claimUri) { + + // If the idp is local, return null + if (isFederatedIdP()) { + Map attributesMap = authenticatedUser.getUserAttributes(); + Map remoteMapping = FrameworkUtils.getClaimMappings(attributesMap, false); + return remoteMapping.get(claimUri); + } + // Can be a case where step is not set (e.g. associated local user) + return null; + } + + /** + * Get the claim by local claim URI. + * + * @param claimUri Local claim URI + * @return Local user's claim value if the Idp is local, Mapped remote claim if the Idp is federated. + */ + protected String getLocalClaim(String claimUri) { + + if (isFederatedIdP()) { + return getLocalMappedClaim(claimUri); + } + /* This covers step with a local authenticator, and the scenarios where step/idp is not set + if the step/idp is not set, user is assumed to be a local user. */ + return getLocalUserClaim(claimUri); + } + + /** + * Check if step's IdP is a federated IDP. + * + * @return true if the idp is federated + */ + protected boolean isFederatedIdP() { + + return StringUtils.isNotBlank(idp) && !FrameworkConstants.LOCAL.equals(idp); + } + + /** + * Sets a custom remote claim to the user. + * + * @param claimUri Remote claim uri + * @param claimValue Claim value + */ + private void setFederatedClaim(String claimUri, String claimValue) { + + if (claimValue == null) { + claimValue = StringUtils.EMPTY; + } + ClaimMapping newClaimMapping = ClaimMapping.build(claimUri, claimUri, null, false); + authenticatedUser.getUserAttributes().put(newClaimMapping, claimValue); + } + + /** + * Gets the mapped remote claim value for the given local claim URI. + * + * @param claimUri Local claim URI + * @return Mapped remote claim value from IdP + */ + private String getLocalMappedClaim(String claimUri) { + + Map idpAttributesMap = authenticatedUser.getUserAttributes(); + Map remoteMapping = FrameworkUtils.getClaimMappings(idpAttributesMap, false); + + String remoteMappedClaim = getRemoteClaimMappedToLocalClaim(claimUri, remoteMapping); + if (remoteMappedClaim != null) { + return remoteMapping.get(remoteMappedClaim); + } + return null; + } + + /** + * Get the local user claim value specified by the Claim URI. + * + * @param claimUri Local claim URI + * @return Claim value of the given claim URI for the local user if available. Null Otherwise. + */ + private String getLocalUserClaim(String claimUri) { + + int usersTenantId = IdentityTenantUtil.getTenantId(authenticatedUser.getTenantDomain()); + RealmService realmService = FrameworkServiceDataHolder.getInstance().getRealmService(); + try { + UserRealm userRealm = realmService.getTenantUserRealm(usersTenantId); + Map claimValues = + ((AbstractUserStoreManager) userRealm.getUserStoreManager()) + .getUserClaimValuesWithID(authenticatedUser.getUserId(), new String[] {claimUri}, null); + return claimValues.get(claimUri); + } catch (UserStoreException e) { + LOG.error(String.format("Error when getting claim : %s of user: %s", claimUri, authenticatedUser), e); + } catch (UserIdNotFoundException e) { + LOG.error("User id is not available for the user: " + authenticatedUser.getLoggableMaskedUserId(), e); + } + return null; + } + + protected Object getRuntimeClaim(String claimUri) { + + String runtimeClaimValue = getContext().getRuntimeClaim(claimUri); + if (runtimeClaimValue != null) { + return runtimeClaimValue; + } + if (isFederatedIdP()) { + return getFederatedClaim(claimUri); + } + return getLocalClaim(claimUri); + } + + protected boolean hasRuntimeClaim(String claimUri) { + + String claim = getContext().getRuntimeClaim(claimUri); + if (claim != null) { + return true; + } + if (isFederatedIdP()) { + return hasFederatedClaim(claimUri); + } + return hasLocalClaim(claimUri); + } + + protected void setRuntimeClaim(String claimUri, Object claimValue) { + + if (claimValue == null) { + claimValue = StringUtils.EMPTY; + } + getContext().addRuntimeClaim(claimUri, String.valueOf(claimValue)); + } +} diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/JsCookie.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/JsCookie.java index 487ce4404b5d..0f145a960e5f 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/JsCookie.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/JsCookie.java @@ -18,9 +18,13 @@ package org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.base.JsBaseCookie; import org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants; +import java.util.Arrays; + import javax.servlet.http.Cookie; /** @@ -32,6 +36,8 @@ */ public abstract class JsCookie extends AbstractJSObjectWrapper implements JsBaseCookie { + protected static final Log LOG = LogFactory.getLog(JsCookie.class); + public JsCookie(Cookie cookie) { super(cookie); } @@ -63,6 +69,17 @@ public Object getMember(String name) { } } + public Object getMemberKeys() { + + String[] cookieProperties = new String[]{ + FrameworkConstants.JSAttributes.JS_COOKIE_NAME, FrameworkConstants.JSAttributes.JS_COOKIE_VALUE, + FrameworkConstants.JSAttributes.JS_COOKIE_COMMENT, FrameworkConstants.JSAttributes.JS_COOKIE_DOMAIN, + FrameworkConstants.JSAttributes.JS_COOKIE_MAX_AGE, FrameworkConstants.JSAttributes.JS_COOKIE_PATH, + FrameworkConstants.JSAttributes.JS_COOKIE_SECURE, FrameworkConstants.JSAttributes.JS_COOKIE_VERSION, + FrameworkConstants.JSAttributes.JS_COOKIE_HTTP_ONLY}; + return Arrays.stream(cookieProperties).filter(this::hasMember).toArray(); + } + @Override public boolean hasMember(String name) { @@ -81,8 +98,16 @@ public boolean hasMember(String name) { return getWrapped().getPath() != null; case FrameworkConstants.JSAttributes.JS_COOKIE_VERSION: return getWrapped().getVersion() != 0; + case FrameworkConstants.JSAttributes.JS_COOKIE_SECURE: + case FrameworkConstants.JSAttributes.JS_COOKIE_HTTP_ONLY: + return true; default: return super.hasMember(name); } } + + public void setMember(String name, Object value) { + + LOG.warn("Unsupported operation. Cookie is read only. Can't remove parameter " + name); + } } diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/JsLogger.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/JsLogger.java index 5b121e9848fc..609cb0f96b38 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/JsLogger.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/JsLogger.java @@ -20,6 +20,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.graalvm.polyglot.HostAccess; import org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants; import org.wso2.carbon.identity.central.log.mgt.utils.LoggerUtils; import org.wso2.carbon.utils.DiagnosticLog; @@ -49,6 +50,7 @@ public static JsLogger getInstance() { * * @param values */ + @HostAccess.Export public void log(Object... values) { if (values != null) { @@ -78,6 +80,7 @@ public void log(Object... values) { } } + @HostAccess.Export public void debug(String value) { logger.debug(value); @@ -91,6 +94,7 @@ public void debug(String value) { } } + @HostAccess.Export public void info(String value) { logger.info(value); @@ -104,6 +108,7 @@ public void info(String value) { } } + @HostAccess.Export public void error(String value) { logger.error(value); @@ -117,6 +122,7 @@ public void error(String value) { } } + @HostAccess.Export public void log(String message, Object... values) { } diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/JsParameters.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/JsParameters.java index 872e71ebf9d2..0a6fdc9fcab1 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/JsParameters.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/JsParameters.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, WSO2 LLC. (http://www.wso2.com). + * Copyright (c) 2022-2024, WSO2 LLC. (http://www.wso2.com). * * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except @@ -20,6 +20,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.JsWrapperFactoryProvider; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.base.JsBaseParameters; import java.util.Map; @@ -32,7 +33,7 @@ */ public abstract class JsParameters extends AbstractJSObjectWrapper implements JsBaseParameters { - private static final Log LOG = LogFactory.getLog(JsParameters.class); + protected static final Log LOG = LogFactory.getLog(JsParameters.class); public JsParameters(Map wrapped) { @@ -42,7 +43,17 @@ public JsParameters(Map wrapped) { @Override public Object getMember(String name) { - return getWrapped().get(name); + Object member = getWrapped().get(name); + if (member instanceof Map) { + return JsWrapperFactoryProvider.getInstance().getWrapperFactory() + .createJsParameters((Map) member); + } + return member; + } + + public Object getMemberKeys() { + + return getWrapped().keySet().toArray(); } @Override @@ -57,4 +68,9 @@ public void setMember(String name, Object value) { LOG.warn("Unsupported operation. Parameters are read only. Can't set parameter " + name + " to value: " + value); } + + public void removeMemberObject(String name) { + + getWrapped().remove(name); + } } diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/JsServletRequest.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/JsServletRequest.java index 09db927fb3a0..085d5a25268f 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/JsServletRequest.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/JsServletRequest.java @@ -18,10 +18,20 @@ package org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.JsWrapperFactoryProvider; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.base.JsBaseServletRequest; import org.wso2.carbon.identity.application.authentication.framework.context.TransientObjectWrapper; import org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants; +import org.wso2.carbon.identity.core.util.IdentityUtil; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; + +import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; /** @@ -40,6 +50,8 @@ public abstract class JsServletRequest extends AbstractJSObjectWrapper> implements JsBaseServletRequest { + private static final Log LOG = LogFactory.getLog(JsServletRequest.class); + public JsServletRequest(TransientObjectWrapper wrapped) { super(wrapped); @@ -54,6 +66,8 @@ public boolean hasMember(String name) { } switch (name) { + case FrameworkConstants.JSAttributes.JS_REQUEST_IP: + return true; case FrameworkConstants.JSAttributes.JS_HEADERS: return getRequest().getHeaderNames() != null; case FrameworkConstants.JSAttributes.JS_PARAMS: @@ -65,6 +79,53 @@ public boolean hasMember(String name) { } } + public Object getMemberKeys() { + + String[] servletRequestProperties = + new String[]{FrameworkConstants.JSAttributes.JS_HEADERS, FrameworkConstants.JSAttributes.JS_COOKIES, + FrameworkConstants.JSAttributes.JS_REQUEST_IP, FrameworkConstants.JSAttributes.JS_PARAMS}; + + return Arrays.stream(servletRequestProperties).filter(this::hasMember).toArray(); + } + + public Object getMember(String name) { + + switch (name) { + case FrameworkConstants.JSAttributes.JS_HEADERS: + Map headers = new HashMap(); + Enumeration headerNames = getRequest().getHeaderNames(); + if (headerNames != null) { + while (headerNames.hasMoreElements()) { + String headerName = headerNames.nextElement(); + headers.put(headerName, getRequest().getHeader(headerName)); + } + } + return JsWrapperFactoryProvider.getInstance().getWrapperFactory().createJsWritableParameters(headers); + case FrameworkConstants.JSAttributes.JS_PARAMS: + return JsWrapperFactoryProvider.getInstance().getWrapperFactory() + .createJsParameters(getRequest().getParameterMap()); + case FrameworkConstants.JSAttributes.JS_COOKIES: + Map cookies = new HashMap(); + Cookie[] cookieArr = getRequest().getCookies(); + if (cookieArr != null) { + for (Cookie cookie : cookieArr) { + cookies.put(cookie.getName(), + JsWrapperFactoryProvider.getInstance().getWrapperFactory().createJsCookie(cookie)); + } + } + return JsWrapperFactoryProvider.getInstance().getWrapperFactory().createJsWritableParameters(cookies); + case FrameworkConstants.JSAttributes.JS_REQUEST_IP: + return IdentityUtil.getClientIpAddress(getRequest()); + default: + return super.getMember(name); + } + } + + public void setMember(String name, Object value) { + + LOG.warn("Unsupported operation. Servlet Request is read only. Can't add parameter " + value); + } + protected HttpServletRequest getRequest() { TransientObjectWrapper transientObjectWrapper = getWrapped(); diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/JsServletResponse.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/JsServletResponse.java index 17b9fda2a82b..88d72e08dfba 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/JsServletResponse.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/JsServletResponse.java @@ -18,10 +18,17 @@ package org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.JsWrapperFactoryProvider; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.base.JsBaseServletResponse; import org.wso2.carbon.identity.application.authentication.framework.context.TransientObjectWrapper; import org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletResponse; @@ -41,11 +48,18 @@ public abstract class JsServletResponse extends AbstractJSObjectWrapper> implements JsBaseServletResponse { + private static final Log LOG = LogFactory.getLog(JsServletResponse.class); + public JsServletResponse(TransientObjectWrapper wrapped) { super(wrapped); } + public Object getMemberKeys() { + + return new String[]{FrameworkConstants.JSAttributes.JS_HEADERS}; + } + @Override public boolean hasMember(String name) { @@ -62,7 +76,31 @@ public boolean hasMember(String name) { } } - protected HttpServletResponse getResponse() { + public Object getMember(String name) { + + switch (name) { + case FrameworkConstants.JSAttributes.JS_HEADERS: + Map headers = new HashMap(); + Collection headerNames = getResponse().getHeaderNames(); + if (headerNames != null) { + for (String element : headerNames) { + headers.put(element, getResponse().getHeader(element)); + } + } + return JsWrapperFactoryProvider.getInstance().getWrapperFactory() + .createJsHeaders(headers, getResponse()); + default: + return super.getMember(name); + } + } + + public void setMember(String name, Object value) { + + LOG.warn("Unsupported operation. Servlet Response is read only. Can't set parameter " + name + " to value: " + + value); + } + + private HttpServletResponse getResponse() { TransientObjectWrapper transientObjectWrapper = getWrapped(); return transientObjectWrapper.getWrapped(); diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/JsStep.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/JsStep.java new file mode 100644 index 000000000000..b54b8a74c1f0 --- /dev/null +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/JsStep.java @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.identity.application.authentication.framework.config.model.StepConfig; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.JsWrapperFactoryProvider; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.base.JsBaseStep; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.graaljs.JsGraalStep; +import org.wso2.carbon.identity.application.authentication.framework.context.AuthenticationContext; +import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedIdPData; +import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser; +import org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +/** + * Represents a authentication step. + * This is the abstract wrapper used for all script engine implementations. + */ +public abstract class JsStep extends AbstractJSContextMemberObject implements JsBaseStep { + + private static final Log LOG = LogFactory.getLog(JsGraalStep.class); + + private final int step; + private final String authenticatedIdp; + private String authenticatedAuthenticator; + + @Deprecated + public JsStep(int step, String authenticatedIdp) { + + this.step = step; + this.authenticatedIdp = authenticatedIdp; + } + + public JsStep(int step, String authenticatedIdp, String authenticatedAuthenticator) { + + this.step = step; + this.authenticatedIdp = authenticatedIdp; + this.authenticatedAuthenticator = authenticatedAuthenticator; + } + + @Deprecated + public JsStep(AuthenticationContext context, int step, String authenticatedIdp) { + + this(step, authenticatedIdp, null); + initializeContext(context); + } + + public JsStep(AuthenticationContext context, int step, String authenticatedIdp, + String authenticatedAuthenticator) { + + this(step, authenticatedIdp, authenticatedAuthenticator); + initializeContext(context); + } + + public Object getMember(String name) { + + switch (name) { + case FrameworkConstants.JSAttributes.JS_AUTHENTICATED_SUBJECT: + return JsWrapperFactoryProvider.getInstance().getWrapperFactory() + .createJsAuthenticatedUser(getContext(), getSubject(), step, authenticatedIdp); + case FrameworkConstants.JSAttributes.JS_AUTHENTICATED_IDP: + return authenticatedIdp; + case FrameworkConstants.JSAttributes.JS_AUTHENTICATOR: + return authenticatedAuthenticator; + case FrameworkConstants.JSAttributes.JS_AUTHENTICATION_OPTIONS: + return getOptions(); + default: + return null; + } + } + + public Object getMemberKeys() { + + return new String[]{FrameworkConstants.JSAttributes.JS_AUTHENTICATED_SUBJECT, + FrameworkConstants.JSAttributes.JS_AUTHENTICATION_OPTIONS, + FrameworkConstants.JSAttributes.JS_AUTHENTICATOR, + FrameworkConstants.JSAttributes.JS_AUTHENTICATED_IDP}; + } + + public boolean hasMember(String name) { + + switch (name) { + case FrameworkConstants.JSAttributes.JS_AUTHENTICATED_SUBJECT: + case FrameworkConstants.JSAttributes.JS_AUTHENTICATED_IDP: + case FrameworkConstants.JSAttributes.JS_AUTHENTICATOR: + case FrameworkConstants.JSAttributes.JS_AUTHENTICATION_OPTIONS: + return true; + default: + return false; + } + } + + public void removeMemberObject(String name) { + + LOG.warn("Step is readonly, hence the can't remove the member."); + } + + public void setMember(String name, Object value) { + + LOG.warn("Step is readonly, hence the setter is ignored."); + } + + private AuthenticatedUser getSubject() { + + if (authenticatedIdp != null) { + AuthenticatedIdPData idPData = getContext().getCurrentAuthenticatedIdPs().get(authenticatedIdp); + if (idPData == null) { + idPData = getContext().getPreviousAuthenticatedIdPs().get(authenticatedIdp); + } + if (idPData != null) { + return idPData.getUser(); + } + } + return null; + } + + private List> getOptions() { + + List> optionsList = new ArrayList<>(); + Optional optionalStepConfig = getContext().getSequenceConfig().getStepMap().values().stream() + .filter(stepConfig -> stepConfig.getOrder() == step).findFirst(); + optionalStepConfig.ifPresent(stepConfig -> stepConfig.getAuthenticatorList().forEach( + authConfig -> authConfig.getIdpNames().forEach(name -> { + Map option = new HashMap<>(); + option.put(FrameworkConstants.JSAttributes.IDP, name); + option.put(FrameworkConstants.JSAttributes.AUTHENTICATOR, authConfig.getApplicationAuthenticator() + .getName()); + optionsList.add(option); + }))); + return optionsList; + } +} diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/JsSteps.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/JsSteps.java new file mode 100644 index 000000000000..388af3b6d59c --- /dev/null +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/JsSteps.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js; + +import org.wso2.carbon.identity.application.authentication.framework.config.model.AuthenticatorConfig; +import org.wso2.carbon.identity.application.authentication.framework.config.model.StepConfig; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.JsWrapperFactoryProvider; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.base.JsBaseSteps; +import org.wso2.carbon.identity.application.authentication.framework.context.AuthenticationContext; + +import java.util.Optional; + +/** + * Returns when context.steps[step_number] is called. + * This is the abstract wrapper used for all script engine implementations. + */ +public abstract class JsSteps extends AbstractJSContextMemberObject implements JsBaseSteps { + + public JsSteps() { + + } + + public JsSteps(AuthenticationContext context) { + + initializeContext(context); + } + + public boolean hasSlot(int step) { + + if (getContext() == null) { + return false; + } + return getContext().getSequenceConfig().getStepMap().containsKey(step); + } + + public Object getSlot(int step) { + + if (getContext() == null) { + return null; + } + return JsWrapperFactoryProvider.getInstance().getWrapperFactory() + .createJsStep(getContext(), step, getAuthenticatedIdPOfStep(step), + getAuthenticatedAuthenticatorOfStep(step)); + } + + public Object get(long index) { + + return getSlot((int) index); + } + + public long getSize() { + + if (getContext() == null) { + return 0; + } + return getContext().getSequenceConfig().getStepMap().size(); + } + + private String getAuthenticatedIdPOfStep(int step) { + + if (getContext().getSequenceConfig() == null) { + //Sequence config is not yet initialized + return null; + } + + Optional optionalStepConfig = getContext().getSequenceConfig().getStepMap().values().stream() + .filter(stepConfig -> stepConfig.getOrder() == step).findFirst(); + return optionalStepConfig.map(StepConfig::getAuthenticatedIdP).orElse(null); + } + + private String getAuthenticatedAuthenticatorOfStep(int step) { + + if (getContext().getSequenceConfig() == null) { + // Sequence config is not yet initialized. + return null; + } + + Optional optionalStepConfig = getContext().getSequenceConfig().getStepMap().values().stream() + .filter(stepConfig -> stepConfig.getOrder() == step).findFirst(); + AuthenticatorConfig authenticatorConfig = optionalStepConfig.map(StepConfig::getAuthenticatedAutenticator) + .orElse(null); + return authenticatorConfig != null ? authenticatorConfig.getName() : null; + } +} diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/base/JsBaseAuthenticatedUser.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/base/JsBaseAuthenticatedUser.java index 56560100ae56..2c56c85960f9 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/base/JsBaseAuthenticatedUser.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/base/JsBaseAuthenticatedUser.java @@ -18,15 +18,19 @@ package org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.base; +import org.graalvm.polyglot.HostAccess; +import org.wso2.carbon.identity.application.authentication.framework.context.AuthenticationContext; import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser; /** * Interface for JavaScript Authenticated User Wrapper. */ +@HostAccess.Implementable public interface JsBaseAuthenticatedUser { /** * @return Wrapped Authenticated User */ AuthenticatedUser getWrapped(); + AuthenticationContext getContext(); } diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/base/JsBaseAuthenticationContext.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/base/JsBaseAuthenticationContext.java index 4cf97f12587c..7a2f7bf6c789 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/base/JsBaseAuthenticationContext.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/base/JsBaseAuthenticationContext.java @@ -18,11 +18,13 @@ package org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.base; +import org.graalvm.polyglot.HostAccess; import org.wso2.carbon.identity.application.authentication.framework.context.AuthenticationContext; /** * Interface for JavaScript Authentication Context Wrapper. */ +@HostAccess.Implementable public interface JsBaseAuthenticationContext { AuthenticationContext getWrapped(); diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/base/JsBaseClaims.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/base/JsBaseClaims.java new file mode 100644 index 000000000000..2568ce9a6e85 --- /dev/null +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/base/JsBaseClaims.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.base; + +import org.wso2.carbon.identity.application.authentication.framework.context.AuthenticationContext; + +/** + * Interface for Javascript wrapper for Java level Claims. + * This provides controlled access to Claims List via provided javascript native syntax. + */ +public interface JsBaseClaims { + + /** + * Get the Authentication Context. + * @return Authentication Context. + */ + AuthenticationContext getContext(); +} diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/base/JsBaseCookie.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/base/JsBaseCookie.java index 64d23123bff7..28e0773eb76d 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/base/JsBaseCookie.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/base/JsBaseCookie.java @@ -18,6 +18,8 @@ package org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.base; +import org.graalvm.polyglot.HostAccess; + import javax.servlet.http.Cookie; /** @@ -27,6 +29,7 @@ * var commonAuthIdDomain = context.request.cookies.commonAuthId.domain * Also it prevents writing an arbitrary values to the respective fields, keeping consistency on runtime Cookie. */ +@HostAccess.Implementable public interface JsBaseCookie { Cookie getWrapped(); diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/base/JsBaseParameters.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/base/JsBaseParameters.java index 6fb03974a1e0..2d13adcf6197 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/base/JsBaseParameters.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/base/JsBaseParameters.java @@ -18,6 +18,8 @@ package org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.base; +import org.graalvm.polyglot.HostAccess; + import java.util.Map; /** @@ -26,6 +28,7 @@ * syntax. * Also it prevents writing an arbitrary values to the respective fields, keeping consistency on runtime. */ +@HostAccess.Implementable public interface JsBaseParameters { /** diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/base/JsBaseRuntimeClaims.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/base/JsBaseRuntimeClaims.java new file mode 100644 index 000000000000..50373754850a --- /dev/null +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/base/JsBaseRuntimeClaims.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.base; + +import org.wso2.carbon.identity.application.authentication.framework.context.AuthenticationContext; + +/** + * Interface to represent the user's runtime claims in javascript. + */ +public interface JsBaseRuntimeClaims { + + /** + * Get the Authentication Context. + * @return Authentication Context. + */ + AuthenticationContext getContext(); +} diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/base/JsBaseServletRequest.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/base/JsBaseServletRequest.java index 22216f906bad..b1a5b9690c52 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/base/JsBaseServletRequest.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/base/JsBaseServletRequest.java @@ -18,6 +18,7 @@ package org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.base; +import org.graalvm.polyglot.HostAccess; import org.wso2.carbon.identity.application.authentication.framework.context.TransientObjectWrapper; import javax.servlet.http.HttpServletRequest; @@ -25,6 +26,7 @@ /** * Interface for JavaScript Servlet Request Wrapper. */ +@HostAccess.Implementable public interface JsBaseServletRequest { /** diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/base/JsBaseServletResponse.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/base/JsBaseServletResponse.java index d4e3c6ceb9a8..dafdb6678524 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/base/JsBaseServletResponse.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/base/JsBaseServletResponse.java @@ -18,6 +18,7 @@ package org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.base; +import org.graalvm.polyglot.HostAccess; import org.wso2.carbon.identity.application.authentication.framework.context.TransientObjectWrapper; import javax.servlet.http.Cookie; @@ -26,6 +27,7 @@ /** * Interface for JavaScript Servlet Response Wrapper. */ +@HostAccess.Implementable public interface JsBaseServletResponse { /** diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/base/JsBaseStep.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/base/JsBaseStep.java new file mode 100644 index 000000000000..93879f8d926c --- /dev/null +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/base/JsBaseStep.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.base; + +import org.wso2.carbon.identity.application.authentication.framework.context.AuthenticationContext; + +/** + * Interface for Javascript wrapper for Java level step. + */ +public interface JsBaseStep { + + /** + * Get the Authentication Context. + * @return Authentication Context. + */ + AuthenticationContext getContext(); +} diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/base/JsBaseSteps.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/base/JsBaseSteps.java new file mode 100644 index 000000000000..3e401776b410 --- /dev/null +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/base/JsBaseSteps.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.base; + +import org.wso2.carbon.identity.application.authentication.framework.context.AuthenticationContext; + +/** + * Interface for Javascript wrapper for Java level step. + */ +public interface JsBaseSteps { + + /** + * Get the Authentication Context. + * @return Authentication Context. + */ + AuthenticationContext getContext(); +} diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/graaljs/JsGraalAuthenticatedUser.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/graaljs/JsGraalAuthenticatedUser.java new file mode 100644 index 000000000000..8c808145b21e --- /dev/null +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/graaljs/JsGraalAuthenticatedUser.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.graaljs; + +import org.graalvm.polyglot.Value; +import org.graalvm.polyglot.proxy.ProxyArray; +import org.graalvm.polyglot.proxy.ProxyObject; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.JsAuthenticatedUser; +import org.wso2.carbon.identity.application.authentication.framework.context.AuthenticationContext; +import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser; + +/** + * Javascript wrapper for Java level AuthenticatedUser. + * This wrapper uses GraalJS polyglot context. + * This provides controlled access to AuthenticatedUser object via provided javascript native syntax. + * e.g + * var userName = context.lastAuthenticatedUser.username + *

+ * instead of + * var userName = context.getLastAuthenticatedUser().getUserName() + *

+ * Also it prevents writing an arbitrary values to the respective fields, keeping consistency on runtime + * AuthenticatedUser. + * + * @see AuthenticatedUser + */ +public class JsGraalAuthenticatedUser extends JsAuthenticatedUser implements ProxyObject { + + public JsGraalAuthenticatedUser(AuthenticationContext context, AuthenticatedUser wrappedUser, int step, + String idp) { + + super(context, wrappedUser, step, idp); + } + + public JsGraalAuthenticatedUser(AuthenticatedUser wrappedUser) { + + super(wrappedUser); + } + + public JsGraalAuthenticatedUser(AuthenticationContext context, AuthenticatedUser wrappedUser) { + + super(context, wrappedUser); + } + + @Override + public Object getMemberKeys() { + + return ProxyArray.fromArray(super.getMemberKeys()); + } + + public void putMember(String name, Value value) { + + String valueAsString = value.isString() ? value.asString() : String.valueOf(value); + setMember(name, valueAsString); + } + + public boolean hasMember(String name) { + + return true; + } +} diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/graaljs/JsGraalAuthenticationContext.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/graaljs/JsGraalAuthenticationContext.java new file mode 100644 index 000000000000..59b9f77c6ee3 --- /dev/null +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/graaljs/JsGraalAuthenticationContext.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.graaljs; + +import org.graalvm.polyglot.Value; +import org.graalvm.polyglot.proxy.ProxyArray; +import org.graalvm.polyglot.proxy.ProxyObject; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.JsAuthenticationContext; +import org.wso2.carbon.identity.application.authentication.framework.context.AuthenticationContext; + +/** + * Javascript wrapper for Java level AuthenticationContext. + * This wrapper uses GraalJS polyglot context. + * This provides controlled access to AuthenticationContext object via provided javascript native syntax. + * e.g + * var requestedAcr = context.requestedAcr + *

+ * instead of + * var requestedAcr = context.getRequestedAcr() + *

+ * Also it prevents writing an arbitrary values to the respective fields, keeping consistency on runtime + * AuthenticationContext. + * + * @see AuthenticationContext + */ +public class JsGraalAuthenticationContext extends JsAuthenticationContext implements ProxyObject { + + public JsGraalAuthenticationContext(AuthenticationContext wrapped) { + + super(wrapped); + } + + @Override + public Object getMemberKeys() { + + return ProxyArray.fromArray(super.getMemberKeys()); + } + + @Override + public void putMember(String key, Value value) { + + String valueAsString = value.isString() ? value.asString() : String.valueOf(value); + super.setMemberObject(key, valueAsString); + } + + @Override + public boolean removeMember(String name) { + + return super.removeMemberObject(name); + } + + public boolean hasMember(String name) { + + return true; + } +} diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/graaljs/JsGraalClaims.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/graaljs/JsGraalClaims.java new file mode 100644 index 000000000000..12e98ca75469 --- /dev/null +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/graaljs/JsGraalClaims.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.graaljs; + +import org.graalvm.polyglot.Value; +import org.graalvm.polyglot.proxy.ProxyArray; +import org.graalvm.polyglot.proxy.ProxyObject; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.JsClaims; +import org.wso2.carbon.identity.application.authentication.framework.context.AuthenticationContext; +import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser; + +/** + * Represent the user's claim for GraalJs Execution. Can be either remote or local. + */ +public class JsGraalClaims extends JsClaims implements ProxyObject { + + public JsGraalClaims(AuthenticationContext context, int step, String idp, boolean isRemoteClaimRequest) { + + super(context, step, idp, isRemoteClaimRequest); + } + + public JsGraalClaims(AuthenticationContext context, AuthenticatedUser authenticatedUser, + boolean isRemoteClaimRequest) { + + super(context, authenticatedUser, isRemoteClaimRequest); + } + + @Override + public Object getMemberKeys() { + + return ProxyArray.fromArray(); + } + + @Override + public void putMember(String claimUri, Value value) { + + String valueAsString = value.isString() ? value.asString() : String.valueOf(value); + setMemberObject(claimUri, valueAsString); + } + + public boolean hasMember(String name) { + + return true; + } +} diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/graaljs/JsGraalCookie.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/graaljs/JsGraalCookie.java new file mode 100644 index 000000000000..f0a390ec859e --- /dev/null +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/graaljs/JsGraalCookie.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.graaljs; + +import org.graalvm.polyglot.Value; +import org.graalvm.polyglot.proxy.ProxyArray; +import org.graalvm.polyglot.proxy.ProxyObject; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.JsCookie; + +import javax.servlet.http.Cookie; + +/** + * Javascript wrapper for Java level Cookie. + * This wrapper uses GraalJS polyglot context. + * This provides controlled access to Cookie object via provided javascript native syntax. + * e.g + * var commonAuthIdDomain = context.request.cookies.commonAuthId.domain + * Also it prevents writing an arbitrary values to the respective fields, keeping consistency on runtime Cookie. + */ +public class JsGraalCookie extends JsCookie implements ProxyObject { + + public JsGraalCookie(Cookie cookie) { + + super(cookie); + } + + @Override + public Object getMemberKeys() { + + return ProxyArray.fromArray(super.getMemberKeys()); + } + + public void putMember(String key, Value value) { + + String valueAsString = value.isString() ? value.asString() : String.valueOf(value); + super.setMember(key, valueAsString); + } + + public boolean hasMember(String name) { + + return true; + } +} diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/graaljs/JsGraalHeaders.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/graaljs/JsGraalHeaders.java new file mode 100644 index 000000000000..6dfa32a533c9 --- /dev/null +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/graaljs/JsGraalHeaders.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.graaljs; + +import org.graalvm.polyglot.Value; +import org.graalvm.polyglot.proxy.ProxyArray; +import org.graalvm.polyglot.proxy.ProxyObject; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.CommonJsHeaders; + +import java.util.Map; + +import javax.servlet.http.HttpServletResponse; + +/** + * Javascript wrapper for Java level HashMap of HTTP headers. + * This provides controlled access to HTTPServletResponse object's headers via provided javascript native syntax. + * Also, it prevents writing an arbitrary values to the respective fields, keeping consistency on runtime. + */ +public class JsGraalHeaders extends CommonJsHeaders implements ProxyObject { + + public JsGraalHeaders(Map wrapped, HttpServletResponse response) { + + super(wrapped, response); + } + + @Override + public Object getMemberKeys() { + + return ProxyArray.fromArray(super.getMemberKeys()); + } + + @Override + public boolean removeMember(String name) { + + return super.removeMemberObject(name); + } + + @Override + public void putMember(String name, Value value) { + + String valueAsString = value.isString() ? value.asString() : String.valueOf(value); + super.setMemberObject(name, valueAsString); + } + + public boolean hasMember(String name) { + + return true; + } +} diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/graaljs/JsGraalParameters.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/graaljs/JsGraalParameters.java new file mode 100644 index 000000000000..d2fb1710dffa --- /dev/null +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/graaljs/JsGraalParameters.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.graaljs; + +import org.graalvm.polyglot.Value; +import org.graalvm.polyglot.proxy.ProxyArray; +import org.graalvm.polyglot.proxy.ProxyObject; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.JsWrapperFactoryProvider; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.JsParameters; + +import java.util.Map; + +/** + * Javascript wrapper for Java level HashMap of HTTP headers/cookies. + * This wrapper uses GraalJS polyglot context. + * This provides controlled access to HTTPServletRequest object's headers and cookies via provided javascript native + * syntax. + * Also it prevents writing an arbitrary values to the respective fields, keeping consistency on runtime. + */ +public class JsGraalParameters extends JsParameters implements ProxyObject { + + public JsGraalParameters(Map wrapped) { + + super(wrapped); + } + + @Override + public Object getMember(String name) { + + Object member = getWrapped().get(name); + if (member instanceof Map) { + return JsWrapperFactoryProvider.getInstance().getWrapperFactory() + .createJsParameters((Map) member); + } + if (member != null && member.getClass().isArray()) { + return ProxyArray.fromArray((Object[]) member); + } else { + return member; + } + } + + @Override + public Object getMemberKeys() { + + return ProxyArray.fromArray(super.getMemberKeys()); + } + + public void putMember(String key, Value value) { + + String valueAsString = value.isString() ? value.asString() : String.valueOf(value); + super.setMember(key, valueAsString); + } + + @Override + public boolean removeMember(String name) { + + LOG.warn("Unsupported operation. Parameters are read only. Can't remove parameter " + name); + return false; + } + + public boolean hasMember(String name) { + + return true; + } +} diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/graaljs/JsGraalRuntimeClaims.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/graaljs/JsGraalRuntimeClaims.java new file mode 100644 index 000000000000..7376e9cb621e --- /dev/null +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/graaljs/JsGraalRuntimeClaims.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.graaljs; + +import org.graalvm.polyglot.Value; +import org.graalvm.polyglot.proxy.ProxyArray; +import org.graalvm.polyglot.proxy.ProxyObject; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.base.JsBaseRuntimeClaims; +import org.wso2.carbon.identity.application.authentication.framework.context.AuthenticationContext; +import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser; + +/** + * Represent the user's runtime claims for GraalJs Execution. + */ +public class JsGraalRuntimeClaims extends JsGraalClaims implements JsBaseRuntimeClaims, ProxyObject { + + public JsGraalRuntimeClaims(AuthenticationContext context, int step, String idp) { + + super(context, step, idp, false); + } + + public JsGraalRuntimeClaims(AuthenticationContext context, AuthenticatedUser user) { + + super(context, user, false); + } + + @Override + public Object getMemberKeys() { + + return ProxyArray.fromArray(); + } + + @Override + public void putMember(String claimUri, Value value) { + + String valueAsString = value.isString() ? value.asString() : String.valueOf(value); + setMember(claimUri, valueAsString); + } + + public Object getMember(String claimUri) { + + if (authenticatedUser != null) { + return getRuntimeClaim(claimUri); + } + return null; + } + + public boolean hasMember(String name) { + + return true; + } + + public void setMember(String claimUri, Object claimValue) { + + if (authenticatedUser != null) { + setRuntimeClaim(claimUri, claimValue); + } + } +} diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/graaljs/JsGraalServletRequest.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/graaljs/JsGraalServletRequest.java new file mode 100644 index 000000000000..f21aff64663e --- /dev/null +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/graaljs/JsGraalServletRequest.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.graaljs; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.graalvm.polyglot.Value; +import org.graalvm.polyglot.proxy.ProxyArray; +import org.graalvm.polyglot.proxy.ProxyObject; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.JsServletRequest; +import org.wso2.carbon.identity.application.authentication.framework.context.TransientObjectWrapper; + +import javax.servlet.http.HttpServletRequest; + +/** + * Javascript wrapper for Java level HTTPServletRequest. + * This wrapper uses GraalJS polyglot context. + * This provides controlled access to HTTPServletRequest object via provided javascript native syntax. + * e.g + * var redirect_uri = context.request.params.redirect_uri + *

+ * instead of + * var userName = context.getRequest().getParameter("redirect_uri) + *

+ * Also it prevents writing an arbitrary values to the respective fields, keeping consistency on runtime + * HTTPServletRequest. + */ +public class JsGraalServletRequest extends JsServletRequest implements ProxyObject { + + protected static final Log LOG = LogFactory.getLog(JsGraalServletRequest.class); + + public JsGraalServletRequest(TransientObjectWrapper wrapped) { + + super(wrapped); + } + + @Override + public Object getMemberKeys() { + + return ProxyArray.fromArray(super.getMemberKeys()); + } + + @Override + public void putMember(String key, Value value) { + + String valueAsString = value.isString() ? value.asString() : String.valueOf(value); + super.setMember(key, valueAsString); + } + + @Override + public boolean removeMember(String key) { + + return false; + } + + public boolean hasMember(String name) { + + return true; + } +} diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/graaljs/JsGraalServletResponse.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/graaljs/JsGraalServletResponse.java new file mode 100644 index 000000000000..6732881be102 --- /dev/null +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/graaljs/JsGraalServletResponse.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.graaljs; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.graalvm.polyglot.Value; +import org.graalvm.polyglot.proxy.ProxyArray; +import org.graalvm.polyglot.proxy.ProxyObject; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.JsServletResponse; +import org.wso2.carbon.identity.application.authentication.framework.context.TransientObjectWrapper; + +import javax.servlet.http.HttpServletResponse; + +/** + * Javascript wrapper for Java level HttpServletResponse. + * This wrapper uses GraalJS polyglot context. + * This provides controlled access to HttpServletResponse object via provided javascript native syntax. + * e.g + * response.headers.["Set-Cookie"] = ['crsftoken=xxxxxssometokenxxxxx'] + *

+ * instead of + * context.getResponse().addCookie(cookie); + *

+ * Also, it prevents writing an arbitrary values to the respective fields, keeping consistency on runtime + * HttpServletResponse. + */ +public class JsGraalServletResponse extends JsServletResponse implements ProxyObject { + + Log log = LogFactory.getLog(JsGraalServletResponse.class); + + public JsGraalServletResponse(TransientObjectWrapper wrapped) { + + super(wrapped); + } + + @Override + public Object getMemberKeys() { + + return ProxyArray.fromArray(super.getMemberKeys()); + } + + @Override + public void putMember(String key, Value value) { + + String valueAsString = value.isString() ? value.asString() : String.valueOf(value); + super.setMember(key, valueAsString); + } + + public boolean hasMember(String name) { + + return true; + } +} diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/graaljs/JsGraalStep.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/graaljs/JsGraalStep.java new file mode 100644 index 000000000000..a392b6f1b84f --- /dev/null +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/graaljs/JsGraalStep.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.graaljs; + +import org.graalvm.polyglot.Value; +import org.graalvm.polyglot.proxy.ProxyArray; +import org.graalvm.polyglot.proxy.ProxyObject; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.JsStep; +import org.wso2.carbon.identity.application.authentication.framework.context.AuthenticationContext; + +/** + * Represents a authentication step. + * This wrapper uses GraalJS polyglot context. + */ +public class JsGraalStep extends JsStep implements ProxyObject { + + public JsGraalStep(int step, String authenticatedIdp, String authenticatedAuthenticator) { + + super(step, authenticatedIdp, authenticatedAuthenticator); + } + + public JsGraalStep(AuthenticationContext context, int step, String authenticatedIdp, + String authenticatedAuthenticator) { + + super(context, step, authenticatedIdp, authenticatedAuthenticator); + } + + @Override + public Object getMemberKeys() { + + return ProxyArray.fromArray(super.getMemberKeys()); + } + + @Override + public void putMember(String key, Value value) { + + super.setMember(key, value); + } + + @Override + public boolean removeMember(String name) { + + super.removeMemberObject(name); + return true; + } + + public boolean hasMember(String name) { + + return true; + } +} diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/graaljs/JsGraalSteps.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/graaljs/JsGraalSteps.java new file mode 100644 index 000000000000..2888de12b3f3 --- /dev/null +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/graaljs/JsGraalSteps.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.graaljs; + +import org.graalvm.polyglot.Value; +import org.graalvm.polyglot.proxy.ProxyArray; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.JsSteps; +import org.wso2.carbon.identity.application.authentication.framework.context.AuthenticationContext; + +/** + * Returns when context.steps[step_number] is called + * This wrapper uses GraalJS polyglot context. + */ +public class JsGraalSteps extends JsSteps implements ProxyArray { + + public JsGraalSteps() { + + super(); + } + + public JsGraalSteps(AuthenticationContext context) { + + super(context); + } + + @Override + public void set(long index, Value value) { + //Steps can not be set with script. + } + + public boolean hasMember(String name) { + + return true; + } +} diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/graaljs/JsGraalWritableParameters.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/graaljs/JsGraalWritableParameters.java new file mode 100644 index 000000000000..dc5802d57101 --- /dev/null +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/graaljs/JsGraalWritableParameters.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.graaljs; + +import org.graalvm.polyglot.Value; +import org.graalvm.polyglot.proxy.ProxyObject; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.JsWrapperFactoryProvider; + +import java.util.List; +import java.util.Map; + +/** + * Parameters that can be modified from the authentication script. + * This wrapper uses GraalJS polyglot context. + */ +public class JsGraalWritableParameters extends JsGraalParameters implements ProxyObject { + + public JsGraalWritableParameters(Map wrapped) { + + super(wrapped); + } + + public Object getMember(String name) { + + Object member = getWrapped().get(name); + if (member instanceof Map) { + return JsWrapperFactoryProvider.getInstance().getWrapperFactory() + .createJsWritableParameters((Map) member); + } + return member; + } + + public boolean removeMember(String name) { + + super.removeMemberObject(name); + return true; + } + + public void putMember(String key, Value value) { + + setMember(key, value.as(List.class)); + } + + public boolean hasMember(String name) { + + return true; + } + + public void setMember(String name, Object value) { + + getWrapped().put(name, value); + } +} diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/nashorn/JsHeaders.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/nashorn/JsHeaders.java index 2a1803d9fb67..ef13be939aef 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/nashorn/JsHeaders.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/nashorn/JsHeaders.java @@ -18,7 +18,7 @@ package org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.nashorn; -import jdk.nashorn.api.scripting.AbstractJSObject; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.CommonJsHeaders; import java.util.Map; @@ -29,58 +29,35 @@ * This provides controlled access to HTTPServletResponse object's headers via provided javascript native syntax. * Also it prevents writing an arbitrary values to the respective fields, keeping consistency on runtime. */ -public class JsHeaders extends AbstractJSObject { - - private Map wrapped; - private HttpServletResponse response; +public class JsHeaders extends CommonJsHeaders implements AbstractJsObject { public JsHeaders(Map wrapped, HttpServletResponse response) { - this.wrapped = wrapped; - this.response = response; + super(wrapped, response); } @Override public Object getMember(String name) { - if (wrapped == null) { - return super.getMember(name); - } else { - return wrapped.get(name); - } - } - - @Override - public boolean hasMember(String name) { - - if (wrapped == null) { - return false; - } else { - return wrapped.get(name) != null; - } + Object member = super.getMember(name); + return member != null ? member : AbstractJsObject.super.getMember(name); } @Override public void removeMember(String name) { - if (wrapped == null) { - super.removeMember(name); - } else { - if (wrapped.containsKey(name)) { - wrapped.remove(name); - } + boolean isRemoved = super.removeMemberObject(name); + if (!isRemoved) { + AbstractJsObject.super.removeMember(name); } } @Override public void setMember(String name, Object value) { - if (wrapped == null) { - super.setMember(name, value); - } else { - wrapped.put(name, value); - //adds a new header to the response. - response.addHeader(name, String.valueOf(value)); + boolean isSet = super.setMemberObject(name, value); + if (!isSet) { + AbstractJsObject.super.setMember(name, value); } } } diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/nashorn/JsNashornAuthenticatedUser.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/nashorn/JsNashornAuthenticatedUser.java index 1f6a405114db..cd2f3f4ded2a 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/nashorn/JsNashornAuthenticatedUser.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/nashorn/JsNashornAuthenticatedUser.java @@ -18,15 +18,9 @@ package org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.nashorn; - -import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.JsAuthenticatedUser; import org.wso2.carbon.identity.application.authentication.framework.context.AuthenticationContext; -import org.wso2.carbon.identity.application.authentication.framework.exception.UserIdNotFoundException; import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser; -import org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants; /** * Javascript wrapper for Java level AuthenticatedUser. @@ -45,8 +39,6 @@ */ public class JsNashornAuthenticatedUser extends JsAuthenticatedUser implements AbstractJsObject { - private static final Log LOG = LogFactory.getLog(JsNashornAuthenticatedUser.class); - /** * Constructor to be used when required to access step specific user details. * @@ -87,52 +79,4 @@ public JsNashornAuthenticatedUser(AuthenticationContext context, AuthenticatedUs super(context, wrappedUser); } - - @Override - public Object getMember(String name) { - - switch (name) { - case FrameworkConstants.JSAttributes.JS_AUTHENTICATED_SUBJECT_IDENTIFIER: - return getWrapped().getAuthenticatedSubjectIdentifier(); - case FrameworkConstants.JSAttributes.JS_USERNAME: - return getWrapped().getUserName(); - case FrameworkConstants.JSAttributes.JS_UNIQUE_ID: - Object userId = null; - try { - userId = getWrapped().getUserId(); - } catch (UserIdNotFoundException e) { - LOG.error("Error while retrieving user Id of user : " + getWrapped().getLoggableUserId(), e); - } - return userId; - case FrameworkConstants.JSAttributes.JS_USER_STORE_DOMAIN: - return getWrapped().getUserStoreDomain(); - case FrameworkConstants.JSAttributes.JS_TENANT_DOMAIN: - return getWrapped().getTenantDomain(); - case FrameworkConstants.JSAttributes.JS_LOCAL_CLAIMS: - if (StringUtils.isNotBlank(idp)) { - return new JsNashornClaims(getContext(), step, idp, false); - } else { - // Represent step independent user - return new JsNashornClaims(getContext(), getWrapped(), false); - } - case FrameworkConstants.JSAttributes.JS_REMOTE_CLAIMS: - if (StringUtils.isNotBlank(idp)) { - return new JsNashornClaims(getContext(), step, idp, true); - } else { - // Represent step independent user - return new JsNashornClaims(getContext(), getWrapped(), true); - } - case FrameworkConstants.JSAttributes.JS_LOCAL_ROLES: - return getLocalRoles(); - case FrameworkConstants.JSAttributes.JS_CLAIMS: - if (StringUtils.isNotBlank(idp)) { - return new JsNashornRuntimeClaims(getContext(), step, idp); - } else { - // Represent step independent user - return new JsNashornRuntimeClaims(getContext(), getWrapped()); - } - default: - return super.getMember(name); - } - } } diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/nashorn/JsNashornAuthenticationContext.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/nashorn/JsNashornAuthenticationContext.java index 86571be07ebb..08018349c77f 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/nashorn/JsNashornAuthenticationContext.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/nashorn/JsNashornAuthenticationContext.java @@ -18,15 +18,8 @@ package org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.nashorn; -import org.wso2.carbon.identity.application.authentication.framework.config.model.StepConfig; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.JsAuthenticationContext; import org.wso2.carbon.identity.application.authentication.framework.context.AuthenticationContext; -import org.wso2.carbon.identity.application.authentication.framework.context.TransientObjectWrapper; -import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser; -import org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants; - -import java.util.Map; -import java.util.Optional; /** * Javascript wrapper for Java level AuthenticationContext. @@ -51,158 +44,16 @@ public JsNashornAuthenticationContext(AuthenticationContext wrapped) { initializeContext(wrapped); } - @Override - public Object getMember(String name) { - - switch (name) { - case FrameworkConstants.JSAttributes.JS_REQUESTED_ACR: - return getWrapped().getRequestedAcr(); - case FrameworkConstants.JSAttributes.JS_TENANT_DOMAIN: - return getWrapped().getTenantDomain(); - case FrameworkConstants.JSAttributes.JS_SERVICE_PROVIDER_NAME: - return getWrapped().getServiceProviderName(); - case FrameworkConstants.JSAttributes.JS_LAST_LOGIN_FAILED_USER: - return getLastLoginFailedUserFromWrappedContext(); - case FrameworkConstants.JSAttributes.JS_REQUEST: - return new JsNashornServletRequest((TransientObjectWrapper) getWrapped() - .getParameter(FrameworkConstants.RequestAttribute.HTTP_REQUEST)); - case FrameworkConstants.JSAttributes.JS_RESPONSE: - return new JsNashornServletResponse((TransientObjectWrapper) getWrapped() - .getParameter(FrameworkConstants.RequestAttribute.HTTP_RESPONSE)); - case FrameworkConstants.JSAttributes.JS_STEPS: - return new JsNashornSteps(getWrapped()); - case FrameworkConstants.JSAttributes.JS_CURRENT_STEP: - return new JsNashornStep(getContext(), getContext().getCurrentStep(), - getAuthenticatedIdPOfCurrentStep(), getAuthenticatedAuthenticatorOfCurrentStep()); - case FrameworkConstants.JSAttributes.JS_CURRENT_KNOWN_SUBJECT: - StepConfig stepConfig = getCurrentSubjectIdentifierStep(); - if (stepConfig != null) { - return new JsNashornAuthenticatedUser(this.getContext(), stepConfig.getAuthenticatedUser(), - stepConfig.getOrder(), stepConfig.getAuthenticatedIdP()); - } else { - return null; - } - case FrameworkConstants.JSAttributes.JS_RETRY_STEP: - return getWrapped().isRetrying(); - case FrameworkConstants.JSAttributes.JS_ENDPOINT_PARAMS: - return new JsNashornWritableParameters(getContext().getEndpointParams()); - default: - return super.getMember(name); - } - } - - @Override - public boolean hasMember(String name) { + public void setMember(String name, Object value) { - switch (name) { - case FrameworkConstants.JSAttributes.JS_REQUESTED_ACR: - return getWrapped().getRequestedAcr() != null; - case FrameworkConstants.JSAttributes.JS_TENANT_DOMAIN: - return getWrapped().getTenantDomain() != null; - case FrameworkConstants.JSAttributes.JS_SERVICE_PROVIDER_NAME: - return getWrapped().getServiceProviderName() != null; - case FrameworkConstants.JSAttributes.JS_LAST_LOGIN_FAILED_USER: - return getWrapped().getProperty(FrameworkConstants.JSAttributes.JS_LAST_LOGIN_FAILED_USER) != null; - case FrameworkConstants.JSAttributes.JS_REQUEST: - return hasTransientValueInParameters(FrameworkConstants.RequestAttribute.HTTP_REQUEST); - case FrameworkConstants.JSAttributes.JS_RESPONSE: - return hasTransientValueInParameters(FrameworkConstants.RequestAttribute.HTTP_RESPONSE); - case FrameworkConstants.JSAttributes.JS_STEPS: - return !getWrapped().getSequenceConfig().getStepMap().isEmpty(); - case FrameworkConstants.JSAttributes.JS_ENDPOINT_PARAMS: - return getWrapped().getEndpointParams() != null; - default: - return super.hasMember(name); - } + super.setMemberObject(name, value); } @Override public void removeMember(String name) { - switch (name) { - case FrameworkConstants.JSAttributes.JS_SELECTED_ACR: - getWrapped().setSelectedAcr(null); - break; - default: - AbstractJsObject.super.removeMember(name); - } - } - - @Override - public void setMember(String name, Object value) { - - switch (name) { - case FrameworkConstants.JSAttributes.JS_SELECTED_ACR: - getWrapped().setSelectedAcr(String.valueOf(value)); - break; - default: - super.setMember(name, value); - } - } - - private boolean hasTransientValueInParameters(String key) { - - TransientObjectWrapper transientObjectWrapper = (TransientObjectWrapper) getWrapped().getParameter(key); - return transientObjectWrapper != null && transientObjectWrapper.getWrapped() != null; - } - - protected JsNashornAuthenticatedUser getLastLoginFailedUserFromWrappedContext() { - - Object lastLoginFailedUser - = getWrapped().getProperty(FrameworkConstants.JSAttributes.JS_LAST_LOGIN_FAILED_USER); - if (lastLoginFailedUser instanceof AuthenticatedUser) { - return new JsNashornAuthenticatedUser(getWrapped(), (AuthenticatedUser) lastLoginFailedUser); - } else { - return null; - } - } - - protected String getAuthenticatedIdPOfCurrentStep() { - - if (getContext().getSequenceConfig() == null) { - //Sequence config is not yet initialized - return null; - } - - StepConfig stepConfig = getContext().getSequenceConfig().getStepMap() - .get(getContext().getCurrentStep()); - if (stepConfig != null) { - return stepConfig.getAuthenticatedIdP(); - } - return null; - - } - - protected String getAuthenticatedAuthenticatorOfCurrentStep() { - - if (getContext().getSequenceConfig() == null) { - // Sequence config is not yet initialized. - return null; - } - - StepConfig stepConfig = getContext().getSequenceConfig().getStepMap() - .get(getContext().getCurrentStep()); - - return stepConfig != null ? stepConfig.getAuthenticatedAutenticator().getName() : null; - } - - protected StepConfig getCurrentSubjectIdentifierStep() { - - if (getContext().getSequenceConfig() == null) { - //Sequence config is not yet initialized - return null; - } - - Map stepConfigs = getContext().getSequenceConfig().getStepMap(); - Optional subjectIdentifierStep = stepConfigs.values().stream() - .filter(stepConfig -> (stepConfig.isCompleted() && stepConfig.isSubjectIdentifierStep())).findFirst(); - - if (subjectIdentifierStep.isPresent()) { - return subjectIdentifierStep.get(); - } else if (getContext().getCurrentStep() > 0) { - return stepConfigs.get(getContext().getCurrentStep()); - } else { - return null; + if (!super.removeMemberObject(name)) { + AbstractJsObject.super.removeMember(name); } } } diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/nashorn/JsNashornClaims.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/nashorn/JsNashornClaims.java index 28b973dbb7dd..49a4c37d5b93 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/nashorn/JsNashornClaims.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/nashorn/JsNashornClaims.java @@ -18,52 +18,15 @@ package org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.nashorn; -import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.wso2.carbon.identity.application.authentication.framework.ApplicationAuthenticator; -import org.wso2.carbon.identity.application.authentication.framework.config.ConfigurationFacade; -import org.wso2.carbon.identity.application.authentication.framework.config.model.ExternalIdPConfig; -import org.wso2.carbon.identity.application.authentication.framework.config.model.StepConfig; -import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.AbstractJSContextMemberObject; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.JsClaims; import org.wso2.carbon.identity.application.authentication.framework.context.AuthenticationContext; -import org.wso2.carbon.identity.application.authentication.framework.exception.UserIdNotFoundException; -import org.wso2.carbon.identity.application.authentication.framework.internal.FrameworkServiceDataHolder; import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser; -import org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants; -import org.wso2.carbon.identity.application.authentication.framework.util.FrameworkUtils; -import org.wso2.carbon.identity.application.common.model.ClaimMapping; -import org.wso2.carbon.identity.application.mgt.ApplicationConstants; -import org.wso2.carbon.identity.base.IdentityException; -import org.wso2.carbon.identity.claim.metadata.mgt.ClaimMetadataHandler; -import org.wso2.carbon.identity.claim.metadata.mgt.exception.ClaimMetadataException; -import org.wso2.carbon.identity.core.IdentityClaimManager; -import org.wso2.carbon.identity.core.util.IdentityTenantUtil; -import org.wso2.carbon.idp.mgt.IdentityProviderManagementException; -import org.wso2.carbon.idp.mgt.IdentityProviderManager; -import org.wso2.carbon.user.api.UserRealm; -import org.wso2.carbon.user.api.UserStoreException; -import org.wso2.carbon.user.core.UserStoreClientException; -import org.wso2.carbon.user.core.claim.Claim; -import org.wso2.carbon.user.core.common.AbstractUserStoreManager; -import org.wso2.carbon.user.core.service.RealmService; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; /** * Represent the user's claim. Can be either remote or local. * This wrapper uses jdk.nashorn engine. */ -public class JsNashornClaims extends AbstractJSContextMemberObject implements AbstractJsObject { - - private static final Log LOG = LogFactory.getLog(JsNashornClaims.class); - private String idp; - private boolean isRemoteClaimRequest; - private int step; - protected transient AuthenticatedUser authenticatedUser; +public class JsNashornClaims extends JsClaims implements AbstractJsObject { /** * Constructor to get the user authenticated in step 'n' @@ -74,66 +37,12 @@ public class JsNashornClaims extends AbstractJSContextMemberObject implements Ab */ public JsNashornClaims(AuthenticationContext context, int step, String idp, boolean isRemoteClaimRequest) { - this(step, idp, isRemoteClaimRequest); - initializeContext(context); + super(context, step, idp, isRemoteClaimRequest); } public JsNashornClaims(int step, String idp, boolean isRemoteClaimRequest) { - this.isRemoteClaimRequest = isRemoteClaimRequest; - this.idp = idp; - this.step = step; - } - - @Override - public void initializeContext(AuthenticationContext context) { - - super.initializeContext(context); - if (this.authenticatedUser == null) { - if (StringUtils.isNotBlank(idp) && getContext().getCurrentAuthenticatedIdPs().containsKey(idp)) { - this.authenticatedUser = getContext().getCurrentAuthenticatedIdPs().get(idp).getUser(); - } else { - this.authenticatedUser = getAuthenticatedUserFromSubjectIdentifierStep(); - } - } - } - - /** - * Get authenticated user from step config of current subject identifier. - * - * @return AuthenticatedUser. - */ - private AuthenticatedUser getAuthenticatedUserFromSubjectIdentifierStep() { - - AuthenticatedUser authenticatedUser = null; - StepConfig stepConfig = getCurrentSubjectIdentifierStep(); - if (stepConfig != null) { - authenticatedUser = getCurrentSubjectIdentifierStep().getAuthenticatedUser(); - } - return authenticatedUser; - } - - /** - * Retrieve step config of current subject identifier. - * - * @return StepConfig. - */ - private StepConfig getCurrentSubjectIdentifierStep() { - - if (getContext().getSequenceConfig() == null) { - // Sequence config is not yet initialized. - return null; - } - Map stepConfigs = getContext().getSequenceConfig().getStepMap(); - Optional subjectIdentifierStep = stepConfigs.values().stream() - .filter(stepConfig -> (stepConfig.isCompleted() && stepConfig.isSubjectIdentifierStep())).findFirst(); - if (subjectIdentifierStep.isPresent()) { - return subjectIdentifierStep.get(); - } else if (getContext().getCurrentStep() > 0) { - return stepConfigs.get(getContext().getCurrentStep()); - } else { - return null; - } + super(step, idp, isRemoteClaimRequest); } /** @@ -145,310 +54,22 @@ private StepConfig getCurrentSubjectIdentifierStep() { */ public JsNashornClaims(AuthenticatedUser authenticatedUser, boolean isRemoteClaimRequest) { - this.isRemoteClaimRequest = isRemoteClaimRequest; - this.authenticatedUser = authenticatedUser; + super(authenticatedUser, isRemoteClaimRequest); } public JsNashornClaims(AuthenticationContext context, AuthenticatedUser authenticatedUser, boolean isRemoteClaimRequest) { - this(authenticatedUser, isRemoteClaimRequest); - initializeContext(context); - } - - @Override - public Object getMember(String claimUri) { - - if (authenticatedUser != null) { - if (isRemoteClaimRequest) { - return getFederatedClaim(claimUri); - } else { - return getLocalClaim(claimUri); - } - } - return null; - } - - @Override - public boolean hasMember(String claimUri) { - - if (authenticatedUser != null) { - if (isRemoteClaimRequest) { - return hasFederatedClaim(claimUri); - } else { - return hasLocalClaim(claimUri); - } - } - return false; + super(context, authenticatedUser, isRemoteClaimRequest); } @Override public void setMember(String claimUri, Object claimValue) { - if (authenticatedUser != null) { - if (isRemoteClaimRequest) { - setFederatedClaim(claimUri, claimValue); - return; - } else { - setLocalClaim(claimUri, claimValue); - return; - } + boolean isClaimSet = setMemberObject(claimUri, claimValue); + if (isClaimSet) { + return; } AbstractJsObject.super.setMember(claimUri, claimValue); } - - /** - * Get the claim by local claim URI. - * - * @param claimUri Local claim URI - * @param claimValue Claim Value - */ - private void setLocalClaim(String claimUri, Object claimValue) { - - if (isFederatedIdP()) { - setLocalMappedClaim(claimUri, claimValue); - } else { - // This covers step with a local authenticator, and the scenarios where step/idp is not set - // if the step/idp is not set, user is assumed to be a local user - setLocalUserClaim(claimUri, claimValue); - } - } - - /** - * Sets the remote claim value that is mapped to the give local claim - * - * @param localClaimURI Local claim URI - * @param claimValue Value to be set - */ - private void setLocalMappedClaim(String localClaimURI, Object claimValue) { - - Map idpAttributesMap = authenticatedUser.getUserAttributes(); - Map remoteMapping = FrameworkUtils.getClaimMappings(idpAttributesMap, false); - String mappedRemoteClaim = getRemoteClaimMappedToLocalClaim(localClaimURI, remoteMapping); - if (mappedRemoteClaim != null) { - setFederatedClaim(mappedRemoteClaim, String.valueOf(claimValue)); - } - } - - /** - * Sets a local claim directly at the userstore for the given user by given claim uri - * - * @param claimUri Local claim URI - * @param claimValue Claim value - */ - private void setLocalUserClaim(String claimUri, Object claimValue) { - - int usersTenantId = IdentityTenantUtil.getTenantId(authenticatedUser.getTenantDomain()); - RealmService realmService = FrameworkServiceDataHolder.getInstance().getRealmService(); - try { - UserRealm userRealm = realmService.getTenantUserRealm(usersTenantId); - Map claimUriMap = new HashMap<>(); - claimUriMap.put(claimUri, String.valueOf(claimValue)); - ((AbstractUserStoreManager) userRealm.getUserStoreManager()) - .setUserClaimValuesWithID(authenticatedUser.getUserId(), claimUriMap, null); - } catch (UserStoreClientException e) { - if (LOG.isDebugEnabled()) { - LOG.debug(String.format("Error when setting claim : %s of user: %s to value: %s. Error Message: %s", - claimUri, authenticatedUser, String.valueOf(claimValue), e.getMessage())); - } - } catch (UserStoreException e) { - LOG.error(String.format("Error when setting claim : %s of user: %s to value: %s", claimUri, - authenticatedUser, String.valueOf(claimValue)), e); - } catch (UserIdNotFoundException e) { - LOG.error("User id is not available for the user: " + authenticatedUser.getLoggableUserId(), e); - } - } - - /** - * Gets the remote claim that is mapped to the given local claim - * - * @param localClaim local claim URI - * @param remoteClaimsMap Remote claim URI - value map - * @return Mapped remote claim URI if present. null otherwise - */ - private String getRemoteClaimMappedToLocalClaim(String localClaim, Map remoteClaimsMap) { - - String authenticatorDialect = null; - Map localToIdpClaimMapping = null; - String tenantDomain = getContext().getTenantDomain(); - try { - // Check if the IDP use an standard dialect (like oidc), If it does, dialect claim mapping are - // prioritized over IdP claim mapping - ApplicationAuthenticator authenticator = getContext().getSequenceConfig().getStepMap().get(step) - .getAuthenticatedAutenticator().getApplicationAuthenticator(); - authenticatorDialect = authenticator.getClaimDialectURI(); - ExternalIdPConfig idPConfig = ConfigurationFacade.getInstance().getIdPConfigByName(idp, tenantDomain); - boolean useDefaultIdpDialect = idPConfig.useDefaultLocalIdpDialect(); - - if (authenticatorDialect != null || useDefaultIdpDialect) { - if (authenticatorDialect == null) { - authenticatorDialect = ApplicationConstants.LOCAL_IDP_DEFAULT_CLAIM_DIALECT; - } - localToIdpClaimMapping = ClaimMetadataHandler.getInstance().getMappingsMapFromOtherDialectToCarbon - (authenticatorDialect, remoteClaimsMap.keySet(), tenantDomain, true); - } else { - localToIdpClaimMapping = IdentityProviderManager.getInstance().getMappedIdPClaimsMap - (idp, tenantDomain, Collections - .singletonList(localClaim)); - - } - if (localToIdpClaimMapping != null) { - return localToIdpClaimMapping.get(localClaim); - } - } catch (IdentityProviderManagementException e) { - LOG.error(String.format("Error when getting claim : %s of user: %s", localClaim, authenticatedUser), e); - } catch (ClaimMetadataException e) { - LOG.error("Error when getting claim mappings from " + authenticatorDialect + " for tenant domain: " + - tenantDomain); - } - return null; - } - - /** - * Check if the user has a federated claim with given name. - * - * @param claimUri Federated claim URI - * @return true if the IdP is federated and it has a claim for user with given URI. - * false otherwise - */ - protected boolean hasFederatedClaim(String claimUri) { - - if (isFederatedIdP()) { - Map attributesMap = authenticatedUser.getUserAttributes(); - Map remoteMapping = FrameworkUtils.getClaimMappings(attributesMap, false); - return remoteMapping.containsKey(claimUri); - } - // Can be a case where step is not set (e.g. associated local user) - return false; - } - - /** - * Check if there is a local claim by given name. - * - * @param claimUri The local claim URI - * @return Claim value of the user authenticated by the indicated IdP - */ - protected boolean hasLocalClaim(String claimUri) { - - int usersTenantId = IdentityTenantUtil.getTenantId(authenticatedUser.getTenantDomain()); - RealmService realmService = FrameworkServiceDataHolder.getInstance().getRealmService(); - try { - UserRealm userRealm = realmService.getTenantUserRealm(usersTenantId); - Claim[] supportedClaims = IdentityClaimManager.getInstance().getAllSupportedClaims((org.wso2.carbon.user - .core.UserRealm) userRealm); - for (Claim claim : supportedClaims) { - if (claim.getClaimUri().equals(claimUri)) { - return true; - } - } - } catch (UserStoreException e) { - LOG.error("Error when retrieving user realm for tenant : " + usersTenantId, e); - } catch (IdentityException e) { - LOG.error("Error when initializing identity claim manager.", e); - } - return false; - } - - /** - * Get the claim by federated claim URI. - * - * @param claimUri Federated claim URI - * @return Claim value if the Idp is a federated Idp, and has a claim by given url for the user. - * null otherwise. - */ - protected String getFederatedClaim(String claimUri) { - - // If the idp is local, return null - if (isFederatedIdP()) { - Map attributesMap = authenticatedUser.getUserAttributes(); - Map remoteMapping = FrameworkUtils.getClaimMappings(attributesMap, false); - return remoteMapping.get(claimUri); - } - // Can be a case where step is not set (e.g. associated local user) - return null; - } - - /** - * Get the claim by local claim URI. - * - * @param claimUri Local claim URI - * @return Local user's claim value if the Idp is local, Mapped remote claim if the Idp is federated. - */ - protected String getLocalClaim(String claimUri) { - - if (isFederatedIdP()) { - return getLocalMappedClaim(claimUri); - } else { - // This covers step with a local authenticator, and the scenarios where step/idp is not set - // if the step/idp is not set, user is assumed to be a local user - return getLocalUserClaim(claimUri); - } - } - - /** - * Check if step's IdP is a federated IDP - * - * @return true if the idp is federated - */ - protected boolean isFederatedIdP() { - - return StringUtils.isNotBlank(idp) && !FrameworkConstants.LOCAL.equals(idp); - } - - /** - * Sets a custom remote claim to the user. - * - * @param claimUri Remote claim uri - * @param claimValue Claim value - */ - private void setFederatedClaim(String claimUri, Object claimValue) { - - if (claimValue == null) { - claimValue = StringUtils.EMPTY; - } - ClaimMapping newClaimMapping = ClaimMapping.build(claimUri, claimUri, null, false); - authenticatedUser.getUserAttributes().put(newClaimMapping, String.valueOf(claimValue)); - } - - /** - * Gets the mapped remote claim value for the given local claim URI - * - * @param claimUri Local claim URI - * @return Mapped remote claim value from IdP - */ - private String getLocalMappedClaim(String claimUri) { - - Map idpAttributesMap = authenticatedUser.getUserAttributes(); - Map remoteMapping = FrameworkUtils.getClaimMappings(idpAttributesMap, false); - - String remoteMappedClaim = getRemoteClaimMappedToLocalClaim(claimUri, remoteMapping); - if (remoteMappedClaim != null) { - return remoteMapping.get(remoteMappedClaim); - } - return null; - } - - /** - * Get the local user claim value specified by the Claim URI. - * - * @param claimUri Local claim URI - * @return Claim value of the given claim URI for the local user if available. Null Otherwise. - */ - private String getLocalUserClaim(String claimUri) { - - int usersTenantId = IdentityTenantUtil.getTenantId(authenticatedUser.getTenantDomain()); - RealmService realmService = FrameworkServiceDataHolder.getInstance().getRealmService(); - try { - UserRealm userRealm = realmService.getTenantUserRealm(usersTenantId); - Map claimValues = - ((AbstractUserStoreManager) userRealm.getUserStoreManager()) - .getUserClaimValuesWithID(authenticatedUser.getUserId(), new String[] {claimUri}, null); - return claimValues.get(claimUri); - } catch (UserStoreException e) { - LOG.error(String.format("Error when getting claim : %s of user: %s", claimUri, authenticatedUser), e); - } catch (UserIdNotFoundException e) { - LOG.error("User id is not available for the user: " + authenticatedUser.getLoggableUserId(), e); - } - return null; - } } diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/nashorn/JsNashornParameters.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/nashorn/JsNashornParameters.java index c1d9b38a40f7..8b91a8afe48c 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/nashorn/JsNashornParameters.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/nashorn/JsNashornParameters.java @@ -18,8 +18,6 @@ package org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.nashorn; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.JsParameters; import java.util.Map; @@ -33,11 +31,8 @@ */ public class JsNashornParameters extends JsParameters implements AbstractJsObject { - private static final Log LOG = LogFactory.getLog(JsNashornParameters.class); - public JsNashornParameters(Map wrapped) { super(wrapped); } - } diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/nashorn/JsNashornRuntimeClaims.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/nashorn/JsNashornRuntimeClaims.java index d6a1ba6f3b65..eface7c11e51 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/nashorn/JsNashornRuntimeClaims.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/nashorn/JsNashornRuntimeClaims.java @@ -18,7 +18,7 @@ package org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.nashorn; -import org.apache.commons.lang.StringUtils; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.base.JsBaseRuntimeClaims; import org.wso2.carbon.identity.application.authentication.framework.context.AuthenticationContext; import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser; @@ -26,7 +26,7 @@ * Represent the user's runtime claims. * This wrapper uses jdk.nashorn engine. */ -public class JsNashornRuntimeClaims extends JsNashornClaims { +public class JsNashornRuntimeClaims extends JsNashornClaims implements JsBaseRuntimeClaims, AbstractJsObject { public JsNashornRuntimeClaims(AuthenticationContext context, int step, String idp) { @@ -38,7 +38,6 @@ public JsNashornRuntimeClaims(AuthenticationContext context, AuthenticatedUser u super(context, user, false); } - @Override public Object getMember(String claimUri) { if (authenticatedUser != null) { @@ -47,7 +46,6 @@ public Object getMember(String claimUri) { return null; } - @Override public boolean hasMember(String claimUri) { if (authenticatedUser != null) { @@ -56,43 +54,10 @@ public boolean hasMember(String claimUri) { return false; } - @Override public void setMember(String claimUri, Object claimValue) { if (authenticatedUser != null) { setRuntimeClaim(claimUri, claimValue); } } - - private Object getRuntimeClaim(String claimUri) { - - String runtimeClaimValue = getContext().getRuntimeClaim(claimUri); - if (runtimeClaimValue != null) { - return runtimeClaimValue; - } - if (isFederatedIdP()) { - return getFederatedClaim(claimUri); - } - return getLocalClaim(claimUri); - } - - private boolean hasRuntimeClaim(String claimUri) { - - String claim = getContext().getRuntimeClaim(claimUri); - if (claim != null) { - return true; - } - if (isFederatedIdP()) { - return hasFederatedClaim(claimUri); - } - return hasLocalClaim(claimUri); - } - - private void setRuntimeClaim(String claimUri, Object claimValue) { - - if (claimValue == null) { - claimValue = StringUtils.EMPTY; - } - getContext().addRuntimeClaim(claimUri, String.valueOf(claimValue)); - } } diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/nashorn/JsNashornServletRequest.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/nashorn/JsNashornServletRequest.java index b3dc3b87cb37..3a16e605be05 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/nashorn/JsNashornServletRequest.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/nashorn/JsNashornServletRequest.java @@ -20,14 +20,7 @@ import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.JsServletRequest; import org.wso2.carbon.identity.application.authentication.framework.context.TransientObjectWrapper; -import org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants; -import org.wso2.carbon.identity.core.util.IdentityUtil; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.Map; - -import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; /** @@ -49,37 +42,5 @@ public JsNashornServletRequest(TransientObjectWrapper wrappe super(wrapped); } - - @Override - public Object getMember(String name) { - - switch (name) { - case FrameworkConstants.JSAttributes.JS_HEADERS: - Map headers = new HashMap(); - Enumeration headerNames = getRequest().getHeaderNames(); - if (headerNames != null) { - while (headerNames.hasMoreElements()) { - String headerName = headerNames.nextElement(); - headers.put(headerName, getRequest().getHeader(headerName)); - } - } - return new JsNashornWritableParameters(headers); - case FrameworkConstants.JSAttributes.JS_PARAMS: - return new JsNashornParameters(getRequest().getParameterMap()); - case FrameworkConstants.JSAttributes.JS_COOKIES: - Map cookies = new HashMap(); - Cookie[] cookieArr = getRequest().getCookies(); - if (cookieArr != null) { - for (Cookie cookie : cookieArr) { - cookies.put(cookie.getName(), new JsNashornCookie(cookie)); - } - } - return new JsNashornWritableParameters(cookies); - case FrameworkConstants.JSAttributes.JS_REQUEST_IP: - return IdentityUtil.getClientIpAddress(getRequest()); - default: - return super.getMember(name); - } - } } diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/nashorn/JsNashornServletResponse.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/nashorn/JsNashornServletResponse.java index 1a26b2b64dca..d25f8516e4b7 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/nashorn/JsNashornServletResponse.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/nashorn/JsNashornServletResponse.java @@ -20,11 +20,6 @@ import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.JsServletResponse; import org.wso2.carbon.identity.application.authentication.framework.context.TransientObjectWrapper; -import org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants; - -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; import javax.servlet.http.HttpServletResponse; @@ -48,21 +43,4 @@ public JsNashornServletResponse(TransientObjectWrapper wrap super(wrapped); } - @Override - public Object getMember(String name) { - - switch (name) { - case FrameworkConstants.JSAttributes.JS_HEADERS: - Map headers = new HashMap(); - Collection headerNames = getResponse().getHeaderNames(); - if (headerNames != null) { - for (String element : headerNames) { - headers.put(element, getResponse().getHeader(element)); - } - } - return new JsHeaders(headers, getResponse()); - default: - return super.getMember(name); - } - } } diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/nashorn/JsNashornStep.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/nashorn/JsNashornStep.java index 3e6f5dc3092b..6da745a6cdcd 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/nashorn/JsNashornStep.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/nashorn/JsNashornStep.java @@ -18,132 +18,53 @@ package org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.nashorn; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.wso2.carbon.identity.application.authentication.framework.config.model.StepConfig; -import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.AbstractJSContextMemberObject; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.JsStep; import org.wso2.carbon.identity.application.authentication.framework.context.AuthenticationContext; -import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedIdPData; -import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser; -import org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; /** * Represents a authentication step. * This wrapper uses jdk.nashorn engine. */ -public class JsNashornStep extends AbstractJSContextMemberObject implements AbstractJsObject { - - private static final Log LOG = LogFactory.getLog(JsNashornSteps.class); - - private int step; - private String authenticatedIdp; - private String authenticatedAuthenticator; +public class JsNashornStep extends JsStep implements AbstractJsObject { @Deprecated public JsNashornStep(int step, String authenticatedIdp) { - this.step = step; - this.authenticatedIdp = authenticatedIdp; + super(step, authenticatedIdp); } public JsNashornStep(int step, String authenticatedIdp, String authenticatedAuthenticator) { - this.step = step; - this.authenticatedIdp = authenticatedIdp; - this.authenticatedAuthenticator = authenticatedAuthenticator; + super(step, authenticatedIdp, authenticatedAuthenticator); } @Deprecated public JsNashornStep(AuthenticationContext context, int step, String authenticatedIdp) { - this(step, authenticatedIdp, null); - initializeContext(context); + super(context, step, authenticatedIdp); } public JsNashornStep(AuthenticationContext context, int step, String authenticatedIdp, String authenticatedAuthenticator) { - this(step, authenticatedIdp, authenticatedAuthenticator); - initializeContext(context); + super(context, step, authenticatedIdp, authenticatedAuthenticator); } @Override public Object getMember(String name) { - switch (name) { - case FrameworkConstants.JSAttributes.JS_AUTHENTICATED_SUBJECT: - return new JsNashornAuthenticatedUser(getContext(), getSubject(), step, authenticatedIdp); - case FrameworkConstants.JSAttributes.JS_AUTHENTICATED_IDP: - return authenticatedIdp; - case FrameworkConstants.JSAttributes.JS_AUTHENTICATOR: - return authenticatedAuthenticator; - case FrameworkConstants.JSAttributes.JS_AUTHENTICATION_OPTIONS: - return getOptions(); - default: - return AbstractJsObject.super.getMember(name); - } + Object member = super.getMember(name); + return member != null ? member : AbstractJsObject.super.getMember(name); } @Override public boolean hasMember(String name) { - switch (name) { - case FrameworkConstants.JSAttributes.JS_AUTHENTICATED_SUBJECT: - return true; - case FrameworkConstants.JSAttributes.JS_AUTHENTICATED_IDP: - return true; - case FrameworkConstants.JSAttributes.JS_AUTHENTICATOR: - return true; - default: - return AbstractJsObject.super.hasMember(name); - } + return super.hasMember(name) || AbstractJsObject.super.hasMember(name); } - @Override public void removeMember(String name) { - LOG.warn("Step is readonly, hence the can't remove the member."); - } - - @Override - public void setMember(String name, Object value) { - - LOG.warn("Step is readonly, hence the setter is ignored."); - } - - private AuthenticatedUser getSubject() { - - if (authenticatedIdp != null) { - AuthenticatedIdPData idPData = getContext().getCurrentAuthenticatedIdPs().get(authenticatedIdp); - if (idPData == null) { - idPData = getContext().getPreviousAuthenticatedIdPs().get(authenticatedIdp); - } - if (idPData != null) { - return idPData.getUser(); - } - } - return null; - } - - private List> getOptions() { - - List> optionsList = new ArrayList<>(); - Optional optionalStepConfig = getContext().getSequenceConfig().getStepMap().values().stream() - .filter(stepConfig -> stepConfig.getOrder() == step).findFirst(); - optionalStepConfig.ifPresent(stepConfig -> stepConfig.getAuthenticatorList().forEach( - authConfig -> authConfig.getIdpNames().forEach(name -> { - Map option = new HashMap<>(); - option.put(FrameworkConstants.JSAttributes.IDP, name); - option.put(FrameworkConstants.JSAttributes.AUTHENTICATOR, authConfig.getApplicationAuthenticator() - .getName()); - optionsList.add(option); - }))); - return optionsList; + super.removeMemberObject(name); } } diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/nashorn/JsNashornSteps.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/nashorn/JsNashornSteps.java index f4c5e602c032..aeea5c6c6341 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/nashorn/JsNashornSteps.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/nashorn/JsNashornSteps.java @@ -18,76 +18,32 @@ package org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.nashorn; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.wso2.carbon.identity.application.authentication.framework.config.model.AuthenticatorConfig; -import org.wso2.carbon.identity.application.authentication.framework.config.model.StepConfig; -import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.AbstractJSContextMemberObject; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.JsSteps; import org.wso2.carbon.identity.application.authentication.framework.context.AuthenticationContext; -import java.util.Optional; +import java.util.Objects; /** - * Returns when context.steps[ optionalStepConfig = getContext().getSequenceConfig().getStepMap().values().stream() - .filter(stepConfig -> stepConfig.getOrder() == step).findFirst(); - return optionalStepConfig.map(StepConfig::getAuthenticatedIdP).orElse(null); - } - - private String getAuthenticatedAuthenticatorOfStep(int step) { - - if (getContext().getSequenceConfig() == null) { - // Sequence config is not yet initialized. - return null; - } - - Optional optionalStepConfig = getContext().getSequenceConfig().getStepMap().values().stream() - .filter(stepConfig -> stepConfig.getOrder() == step).findFirst(); - AuthenticatorConfig authenticatorConfig = optionalStepConfig.map(StepConfig::getAuthenticatedAutenticator) - .orElse(null); - return authenticatorConfig != null ? authenticatorConfig.getName() : null; + Object jsStep = super.getSlot(step); + return Objects.nonNull(jsStep) ? jsStep : AbstractJsObject.super.getSlot(step); } } diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/nashorn/JsNashornWritableParameters.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/nashorn/JsNashornWritableParameters.java index 17b88ff83427..70b6ccb596dc 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/nashorn/JsNashornWritableParameters.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/nashorn/JsNashornWritableParameters.java @@ -18,6 +18,7 @@ package org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.nashorn; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.JsWrapperFactoryProvider; import java.util.Map; @@ -25,22 +26,28 @@ * Parameters that can be modified from the authentication script. * This wrapper uses jdk.nashorn engine. */ -public class JsNashornWritableParameters extends JsNashornParameters { +public class JsNashornWritableParameters extends JsNashornParameters implements AbstractJsObject { public JsNashornWritableParameters(Map wrapped) { super(wrapped); } - @Override - public void removeMember(String name) { + public Object getMember(String name) { - if (getWrapped().containsKey(name)) { - getWrapped().remove(name); + Object member = getWrapped().get(name); + if (member instanceof Map) { + return JsWrapperFactoryProvider.getInstance().getWrapperFactory() + .createJsWritableParameters((Map) member); } + return member; + } + + public void removeMember(String name) { + + super.removeMemberObject(name); } - @Override public void setMember(String name, Object value) { getWrapped().put(name, value); diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/openjdk/nashorn/JsOpenJdkNashornAuthenticatedUser.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/openjdk/nashorn/JsOpenJdkNashornAuthenticatedUser.java index ff420b7167d9..a3e03b704f24 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/openjdk/nashorn/JsOpenJdkNashornAuthenticatedUser.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/openjdk/nashorn/JsOpenJdkNashornAuthenticatedUser.java @@ -18,15 +18,9 @@ package org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.openjdk.nashorn; - -import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.JsAuthenticatedUser; import org.wso2.carbon.identity.application.authentication.framework.context.AuthenticationContext; -import org.wso2.carbon.identity.application.authentication.framework.exception.UserIdNotFoundException; import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser; -import org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants; /** * Javascript wrapper for Java level AuthenticatedUser. @@ -46,8 +40,6 @@ public class JsOpenJdkNashornAuthenticatedUser extends JsAuthenticatedUser implements AbstractOpenJdkNashornJsObject { - private static final Log LOG = LogFactory.getLog(JsOpenJdkNashornAuthenticatedUser.class); - /** * Constructor to be used when required to access step specific user details. * @@ -88,52 +80,4 @@ public JsOpenJdkNashornAuthenticatedUser(AuthenticationContext context, Authenti super(context, wrappedUser); } - - @Override - public Object getMember(String name) { - - switch (name) { - case FrameworkConstants.JSAttributes.JS_AUTHENTICATED_SUBJECT_IDENTIFIER: - return getWrapped().getAuthenticatedSubjectIdentifier(); - case FrameworkConstants.JSAttributes.JS_USERNAME: - return getWrapped().getUserName(); - case FrameworkConstants.JSAttributes.JS_UNIQUE_ID: - Object userId = null; - try { - userId = getWrapped().getUserId(); - } catch (UserIdNotFoundException e) { - LOG.error("Error while retrieving user Id of user : " + getWrapped().getLoggableUserId(), e); - } - return userId; - case FrameworkConstants.JSAttributes.JS_USER_STORE_DOMAIN: - return getWrapped().getUserStoreDomain(); - case FrameworkConstants.JSAttributes.JS_TENANT_DOMAIN: - return getWrapped().getTenantDomain(); - case FrameworkConstants.JSAttributes.JS_LOCAL_CLAIMS: - if (StringUtils.isNotBlank(idp)) { - return new JsOpenJdkNashornClaims(getContext(), step, idp, false); - } else { - // Represent step independent user - return new JsOpenJdkNashornClaims(getContext(), getWrapped(), false); - } - case FrameworkConstants.JSAttributes.JS_REMOTE_CLAIMS: - if (StringUtils.isNotBlank(idp)) { - return new JsOpenJdkNashornClaims(getContext(), step, idp, true); - } else { - // Represent step independent user - return new JsOpenJdkNashornClaims(getContext(), getWrapped(), true); - } - case FrameworkConstants.JSAttributes.JS_LOCAL_ROLES: - return getLocalRoles(); - case FrameworkConstants.JSAttributes.JS_CLAIMS: - if (StringUtils.isNotBlank(idp)) { - return new JsOpenJdkNashornRuntimeClaims(getContext(), step, idp); - } else { - // Represent step independent user - return new JsOpenJdkNashornRuntimeClaims(getContext(), getWrapped()); - } - default: - return super.getMember(name); - } - } } diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/openjdk/nashorn/JsOpenJdkNashornAuthenticationContext.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/openjdk/nashorn/JsOpenJdkNashornAuthenticationContext.java index 028050490d6d..71ac923a256e 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/openjdk/nashorn/JsOpenJdkNashornAuthenticationContext.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/openjdk/nashorn/JsOpenJdkNashornAuthenticationContext.java @@ -18,15 +18,8 @@ package org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.openjdk.nashorn; -import org.wso2.carbon.identity.application.authentication.framework.config.model.StepConfig; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.JsAuthenticationContext; import org.wso2.carbon.identity.application.authentication.framework.context.AuthenticationContext; -import org.wso2.carbon.identity.application.authentication.framework.context.TransientObjectWrapper; -import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser; -import org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants; - -import java.util.Map; -import java.util.Optional; /** * Javascript wrapper for Java level AuthenticationContext. @@ -52,157 +45,16 @@ public JsOpenJdkNashornAuthenticationContext(AuthenticationContext wrapped) { initializeContext(wrapped); } - @Override - public Object getMember(String name) { - - switch (name) { - case FrameworkConstants.JSAttributes.JS_REQUESTED_ACR: - return getWrapped().getRequestedAcr(); - case FrameworkConstants.JSAttributes.JS_TENANT_DOMAIN: - return getWrapped().getTenantDomain(); - case FrameworkConstants.JSAttributes.JS_SERVICE_PROVIDER_NAME: - return getWrapped().getServiceProviderName(); - case FrameworkConstants.JSAttributes.JS_LAST_LOGIN_FAILED_USER: - return getLastLoginFailedUserFromWrappedContext(); - case FrameworkConstants.JSAttributes.JS_REQUEST: - return new JsOpenJdkNashornServletRequest((TransientObjectWrapper) getWrapped() - .getParameter(FrameworkConstants.RequestAttribute.HTTP_REQUEST)); - case FrameworkConstants.JSAttributes.JS_RESPONSE: - return new JsOpenJdkNashornServletResponse((TransientObjectWrapper) getWrapped() - .getParameter(FrameworkConstants.RequestAttribute.HTTP_RESPONSE)); - case FrameworkConstants.JSAttributes.JS_STEPS: - return new JsOpenJdkNashornSteps(getWrapped()); - case FrameworkConstants.JSAttributes.JS_CURRENT_STEP: - return new JsOpenJdkNashornStep(getContext(), getContext().getCurrentStep(), - getAuthenticatedIdPOfCurrentStep(), getAuthenticatedAuthenticatorOfCurrentStep()); - case FrameworkConstants.JSAttributes.JS_CURRENT_KNOWN_SUBJECT: - StepConfig stepConfig = getCurrentSubjectIdentifierStep(); - if (stepConfig != null) { - return new JsOpenJdkNashornAuthenticatedUser(this.getContext(), stepConfig.getAuthenticatedUser(), - stepConfig.getOrder(), stepConfig.getAuthenticatedIdP()); - } else { - return null; - } - case FrameworkConstants.JSAttributes.JS_RETRY_STEP: - return getWrapped().isRetrying(); - case FrameworkConstants.JSAttributes.JS_ENDPOINT_PARAMS: - return new JsOpenJdkNashornWritableParameters(getContext().getEndpointParams()); - default: - return super.getMember(name); - } - } - - @Override - public boolean hasMember(String name) { + public void setMember(String name, Object value) { - switch (name) { - case FrameworkConstants.JSAttributes.JS_REQUESTED_ACR: - return getWrapped().getRequestedAcr() != null; - case FrameworkConstants.JSAttributes.JS_TENANT_DOMAIN: - return getWrapped().getTenantDomain() != null; - case FrameworkConstants.JSAttributes.JS_SERVICE_PROVIDER_NAME: - return getWrapped().getServiceProviderName() != null; - case FrameworkConstants.JSAttributes.JS_LAST_LOGIN_FAILED_USER: - return getWrapped().getProperty(FrameworkConstants.JSAttributes.JS_LAST_LOGIN_FAILED_USER) != null; - case FrameworkConstants.JSAttributes.JS_REQUEST: - return hasTransientValueInParameters(FrameworkConstants.RequestAttribute.HTTP_REQUEST); - case FrameworkConstants.JSAttributes.JS_RESPONSE: - return hasTransientValueInParameters(FrameworkConstants.RequestAttribute.HTTP_RESPONSE); - case FrameworkConstants.JSAttributes.JS_STEPS: - return !getWrapped().getSequenceConfig().getStepMap().isEmpty(); - case FrameworkConstants.JSAttributes.JS_ENDPOINT_PARAMS: - return getWrapped().getEndpointParams() != null; - default: - return super.hasMember(name); - } + super.setMemberObject(name, value); } @Override public void removeMember(String name) { - switch (name) { - case FrameworkConstants.JSAttributes.JS_SELECTED_ACR: - getWrapped().setSelectedAcr(null); - break; - default: - AbstractOpenJdkNashornJsObject.super.removeMember(name); - } - } - - @Override - public void setMember(String name, Object value) { - - switch (name) { - case FrameworkConstants.JSAttributes.JS_SELECTED_ACR: - getWrapped().setSelectedAcr(String.valueOf(value)); - break; - default: - super.setMember(name, value); - } - } - - private boolean hasTransientValueInParameters(String key) { - - TransientObjectWrapper transientObjectWrapper = (TransientObjectWrapper) getWrapped().getParameter(key); - return transientObjectWrapper != null && transientObjectWrapper.getWrapped() != null; - } - - protected JsOpenJdkNashornAuthenticatedUser getLastLoginFailedUserFromWrappedContext() { - - Object lastLoginFailedUser - = getWrapped().getProperty(FrameworkConstants.JSAttributes.JS_LAST_LOGIN_FAILED_USER); - if (lastLoginFailedUser instanceof AuthenticatedUser) { - return new JsOpenJdkNashornAuthenticatedUser(getWrapped(), (AuthenticatedUser) lastLoginFailedUser); - } else { - return null; - } - } - - protected String getAuthenticatedIdPOfCurrentStep() { - - if (getContext().getSequenceConfig() == null) { - //Sequence config is not yet initialized - return null; - } - - StepConfig stepConfig = getContext().getSequenceConfig().getStepMap() - .get(getContext().getCurrentStep()); - if (stepConfig != null) { - return stepConfig.getAuthenticatedIdP(); - } - return null; - - } - - protected String getAuthenticatedAuthenticatorOfCurrentStep() { - - if (getContext().getSequenceConfig() == null) { - // Sequence config is not yet initialized. - return null; - } - - StepConfig stepConfig = getContext().getSequenceConfig().getStepMap() - .get(getContext().getCurrentStep()); - return stepConfig != null ? stepConfig.getAuthenticatedAutenticator().getName() : null; - } - - protected StepConfig getCurrentSubjectIdentifierStep() { - - if (getContext().getSequenceConfig() == null) { - //Sequence config is not yet initialized - return null; - } - - Map stepConfigs = getContext().getSequenceConfig().getStepMap(); - Optional subjectIdentifierStep = stepConfigs.values().stream() - .filter(stepConfig -> (stepConfig.isCompleted() && stepConfig.isSubjectIdentifierStep())).findFirst(); - - if (subjectIdentifierStep.isPresent()) { - return subjectIdentifierStep.get(); - } else if (getContext().getCurrentStep() > 0) { - return stepConfigs.get(getContext().getCurrentStep()); - } else { - return null; + if (!super.removeMemberObject(name)) { + AbstractOpenJdkNashornJsObject.super.removeMember(name); } } } diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/openjdk/nashorn/JsOpenJdkNashornClaims.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/openjdk/nashorn/JsOpenJdkNashornClaims.java index 96f0830e3865..65c691b7f33f 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/openjdk/nashorn/JsOpenJdkNashornClaims.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/openjdk/nashorn/JsOpenJdkNashornClaims.java @@ -18,53 +18,16 @@ package org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.openjdk.nashorn; -import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.wso2.carbon.identity.application.authentication.framework.ApplicationAuthenticator; -import org.wso2.carbon.identity.application.authentication.framework.config.ConfigurationFacade; -import org.wso2.carbon.identity.application.authentication.framework.config.model.ExternalIdPConfig; -import org.wso2.carbon.identity.application.authentication.framework.config.model.StepConfig; -import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.AbstractJSContextMemberObject; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.JsClaims; import org.wso2.carbon.identity.application.authentication.framework.context.AuthenticationContext; -import org.wso2.carbon.identity.application.authentication.framework.exception.UserIdNotFoundException; -import org.wso2.carbon.identity.application.authentication.framework.internal.FrameworkServiceDataHolder; import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser; -import org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants; -import org.wso2.carbon.identity.application.authentication.framework.util.FrameworkUtils; -import org.wso2.carbon.identity.application.common.model.ClaimMapping; -import org.wso2.carbon.identity.application.mgt.ApplicationConstants; -import org.wso2.carbon.identity.base.IdentityException; -import org.wso2.carbon.identity.claim.metadata.mgt.ClaimMetadataHandler; -import org.wso2.carbon.identity.claim.metadata.mgt.exception.ClaimMetadataException; -import org.wso2.carbon.identity.core.IdentityClaimManager; -import org.wso2.carbon.identity.core.util.IdentityTenantUtil; -import org.wso2.carbon.idp.mgt.IdentityProviderManagementException; -import org.wso2.carbon.idp.mgt.IdentityProviderManager; -import org.wso2.carbon.user.api.UserRealm; -import org.wso2.carbon.user.api.UserStoreException; -import org.wso2.carbon.user.core.UserStoreClientException; -import org.wso2.carbon.user.core.claim.Claim; -import org.wso2.carbon.user.core.common.AbstractUserStoreManager; -import org.wso2.carbon.user.core.service.RealmService; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; /** * Represent the user's claim. Can be either remote or local. * Since Nashorn is deprecated in JDK 11 and onwards. We are introducing OpenJDK Nashorn engine. * */ -public class JsOpenJdkNashornClaims extends AbstractJSContextMemberObject implements AbstractOpenJdkNashornJsObject { - - private static final Log LOG = LogFactory.getLog(JsOpenJdkNashornClaims.class); - private String idp; - private boolean isRemoteClaimRequest; - private int step; - protected transient AuthenticatedUser authenticatedUser; +public class JsOpenJdkNashornClaims extends JsClaims implements AbstractOpenJdkNashornJsObject { /** * Constructor to get the user authenticated in step 'n' @@ -75,66 +38,12 @@ public class JsOpenJdkNashornClaims extends AbstractJSContextMemberObject implem */ public JsOpenJdkNashornClaims(AuthenticationContext context, int step, String idp, boolean isRemoteClaimRequest) { - this(step, idp, isRemoteClaimRequest); - initializeContext(context); + super(context, step, idp, isRemoteClaimRequest); } public JsOpenJdkNashornClaims(int step, String idp, boolean isRemoteClaimRequest) { - this.isRemoteClaimRequest = isRemoteClaimRequest; - this.idp = idp; - this.step = step; - } - - @Override - public void initializeContext(AuthenticationContext context) { - - super.initializeContext(context); - if (this.authenticatedUser == null) { - if (StringUtils.isNotBlank(idp) && getContext().getCurrentAuthenticatedIdPs().containsKey(idp)) { - this.authenticatedUser = getContext().getCurrentAuthenticatedIdPs().get(idp).getUser(); - } else { - this.authenticatedUser = getAuthenticatedUserFromSubjectIdentifierStep(); - } - } - } - - /** - * Get authenticated user from step config of current subject identifier. - * - * @return AuthenticatedUser. - */ - private AuthenticatedUser getAuthenticatedUserFromSubjectIdentifierStep() { - - AuthenticatedUser authenticatedUser = null; - StepConfig stepConfig = getCurrentSubjectIdentifierStep(); - if (stepConfig != null) { - authenticatedUser = getCurrentSubjectIdentifierStep().getAuthenticatedUser(); - } - return authenticatedUser; - } - - /** - * Retrieve step config of current subject identifier. - * - * @return StepConfig. - */ - private StepConfig getCurrentSubjectIdentifierStep() { - - if (getContext().getSequenceConfig() == null) { - // Sequence config is not yet initialized. - return null; - } - Map stepConfigs = getContext().getSequenceConfig().getStepMap(); - Optional subjectIdentifierStep = stepConfigs.values().stream() - .filter(stepConfig -> (stepConfig.isCompleted() && stepConfig.isSubjectIdentifierStep())).findFirst(); - if (subjectIdentifierStep.isPresent()) { - return subjectIdentifierStep.get(); - } else if (getContext().getCurrentStep() > 0) { - return stepConfigs.get(getContext().getCurrentStep()); - } else { - return null; - } + super(step, idp, isRemoteClaimRequest); } /** @@ -146,310 +55,21 @@ private StepConfig getCurrentSubjectIdentifierStep() { */ public JsOpenJdkNashornClaims(AuthenticatedUser authenticatedUser, boolean isRemoteClaimRequest) { - this.isRemoteClaimRequest = isRemoteClaimRequest; - this.authenticatedUser = authenticatedUser; + super(authenticatedUser, isRemoteClaimRequest); } public JsOpenJdkNashornClaims(AuthenticationContext context, AuthenticatedUser authenticatedUser, boolean isRemoteClaimRequest) { - this(authenticatedUser, isRemoteClaimRequest); - initializeContext(context); - } - - @Override - public Object getMember(String claimUri) { - - if (authenticatedUser != null) { - if (isRemoteClaimRequest) { - return getFederatedClaim(claimUri); - } else { - return getLocalClaim(claimUri); - } - } - return null; - } - - @Override - public boolean hasMember(String claimUri) { - - if (authenticatedUser != null) { - if (isRemoteClaimRequest) { - return hasFederatedClaim(claimUri); - } else { - return hasLocalClaim(claimUri); - } - } - return false; + super(context, authenticatedUser, isRemoteClaimRequest); } @Override public void setMember(String claimUri, Object claimValue) { - if (authenticatedUser != null) { - if (isRemoteClaimRequest) { - setFederatedClaim(claimUri, claimValue); - return; - } else { - setLocalClaim(claimUri, claimValue); - return; - } - } - AbstractOpenJdkNashornJsObject.super.setMember(claimUri, claimValue); - } - - /** - * Get the claim by local claim URI. - * - * @param claimUri Local claim URI - * @param claimValue Claim Value - */ - private void setLocalClaim(String claimUri, Object claimValue) { - - if (isFederatedIdP()) { - setLocalMappedClaim(claimUri, claimValue); - } else { - // This covers step with a local authenticator, and the scenarios where step/idp is not set - // if the step/idp is not set, user is assumed to be a local user - setLocalUserClaim(claimUri, claimValue); - } - } - - /** - * Sets the remote claim value that is mapped to the give local claim - * - * @param localClaimURI Local claim URI - * @param claimValue Value to be set - */ - private void setLocalMappedClaim(String localClaimURI, Object claimValue) { - - Map idpAttributesMap = authenticatedUser.getUserAttributes(); - Map remoteMapping = FrameworkUtils.getClaimMappings(idpAttributesMap, false); - String mappedRemoteClaim = getRemoteClaimMappedToLocalClaim(localClaimURI, remoteMapping); - if (mappedRemoteClaim != null) { - setFederatedClaim(mappedRemoteClaim, String.valueOf(claimValue)); - } - } - - /** - * Sets a local claim directly at the userstore for the given user by given claim uri - * - * @param claimUri Local claim URI - * @param claimValue Claim value - */ - private void setLocalUserClaim(String claimUri, Object claimValue) { - - int usersTenantId = IdentityTenantUtil.getTenantId(authenticatedUser.getTenantDomain()); - RealmService realmService = FrameworkServiceDataHolder.getInstance().getRealmService(); - try { - UserRealm userRealm = realmService.getTenantUserRealm(usersTenantId); - Map claimUriMap = new HashMap<>(); - claimUriMap.put(claimUri, String.valueOf(claimValue)); - ((AbstractUserStoreManager) userRealm.getUserStoreManager()) - .setUserClaimValuesWithID(authenticatedUser.getUserId(), claimUriMap, null); - } catch (UserStoreClientException e) { - if (LOG.isDebugEnabled()) { - LOG.debug(String.format("Error when setting claim : %s of user: %s to value: %s. Error Message: %s", - claimUri, authenticatedUser, String.valueOf(claimValue), e.getMessage())); - } - } catch (UserStoreException e) { - LOG.error(String.format("Error when setting claim : %s of user: %s to value: %s", claimUri, - authenticatedUser, String.valueOf(claimValue)), e); - } catch (UserIdNotFoundException e) { - LOG.error("User id is not available for the user: " + authenticatedUser.getLoggableUserId(), e); - } - } - - /** - * Gets the remote claim that is mapped to the given local claim - * - * @param localClaim local claim URI - * @param remoteClaimsMap Remote claim URI - value map - * @return Mapped remote claim URI if present. null otherwise - */ - private String getRemoteClaimMappedToLocalClaim(String localClaim, Map remoteClaimsMap) { - - String authenticatorDialect = null; - Map localToIdpClaimMapping = null; - String tenantDomain = getContext().getTenantDomain(); - try { - // Check if the IDP use an standard dialect (like oidc), If it does, dialect claim mapping are - // prioritized over IdP claim mapping - ApplicationAuthenticator authenticator = getContext().getSequenceConfig().getStepMap().get(step) - .getAuthenticatedAutenticator().getApplicationAuthenticator(); - authenticatorDialect = authenticator.getClaimDialectURI(); - ExternalIdPConfig idPConfig = ConfigurationFacade.getInstance().getIdPConfigByName(idp, tenantDomain); - boolean useDefaultIdpDialect = idPConfig.useDefaultLocalIdpDialect(); - - if (authenticatorDialect != null || useDefaultIdpDialect) { - if (authenticatorDialect == null) { - authenticatorDialect = ApplicationConstants.LOCAL_IDP_DEFAULT_CLAIM_DIALECT; - } - localToIdpClaimMapping = ClaimMetadataHandler.getInstance().getMappingsMapFromOtherDialectToCarbon - (authenticatorDialect, remoteClaimsMap.keySet(), tenantDomain, true); - } else { - localToIdpClaimMapping = IdentityProviderManager.getInstance().getMappedIdPClaimsMap - (idp, tenantDomain, Collections - .singletonList(localClaim)); - - } - if (localToIdpClaimMapping != null) { - return localToIdpClaimMapping.get(localClaim); - } - } catch (IdentityProviderManagementException e) { - LOG.error(String.format("Error when getting claim : %s of user: %s", localClaim, authenticatedUser), e); - } catch (ClaimMetadataException e) { - LOG.error("Error when getting claim mappings from " + authenticatorDialect + " for tenant domain: " + - tenantDomain); - } - return null; - } - - /** - * Check if the user has a federated claim with given name. - * - * @param claimUri Federated claim URI - * @return true if the IdP is federated and it has a claim for user with given URI. - * false otherwise - */ - protected boolean hasFederatedClaim(String claimUri) { - - if (isFederatedIdP()) { - Map attributesMap = authenticatedUser.getUserAttributes(); - Map remoteMapping = FrameworkUtils.getClaimMappings(attributesMap, false); - return remoteMapping.containsKey(claimUri); - } - // Can be a case where step is not set (e.g. associated local user) - return false; - } - - /** - * Check if there is a local claim by given name. - * - * @param claimUri The local claim URI - * @return Claim value of the user authenticated by the indicated IdP - */ - protected boolean hasLocalClaim(String claimUri) { - - int usersTenantId = IdentityTenantUtil.getTenantId(authenticatedUser.getTenantDomain()); - RealmService realmService = FrameworkServiceDataHolder.getInstance().getRealmService(); - try { - UserRealm userRealm = realmService.getTenantUserRealm(usersTenantId); - Claim[] supportedClaims = IdentityClaimManager.getInstance().getAllSupportedClaims((org.wso2.carbon.user - .core.UserRealm) userRealm); - for (Claim claim : supportedClaims) { - if (claim.getClaimUri().equals(claimUri)) { - return true; - } - } - } catch (UserStoreException e) { - LOG.error("Error when retrieving user realm for tenant : " + usersTenantId, e); - } catch (IdentityException e) { - LOG.error("Error when initializing identity claim manager.", e); - } - return false; - } - - /** - * Get the claim by federated claim URI. - * - * @param claimUri Federated claim URI - * @return Claim value if the Idp is a federated Idp, and has a claim by given url for the user. - * null otherwise. - */ - protected String getFederatedClaim(String claimUri) { - - // If the idp is local, return null - if (isFederatedIdP()) { - Map attributesMap = authenticatedUser.getUserAttributes(); - Map remoteMapping = FrameworkUtils.getClaimMappings(attributesMap, false); - return remoteMapping.get(claimUri); - } - // Can be a case where step is not set (e.g. associated local user) - return null; - } - - /** - * Get the claim by local claim URI. - * - * @param claimUri Local claim URI - * @return Local user's claim value if the Idp is local, Mapped remote claim if the Idp is federated. - */ - protected String getLocalClaim(String claimUri) { - - if (isFederatedIdP()) { - return getLocalMappedClaim(claimUri); - } else { - // This covers step with a local authenticator, and the scenarios where step/idp is not set - // if the step/idp is not set, user is assumed to be a local user - return getLocalUserClaim(claimUri); - } - } - - /** - * Check if step's IdP is a federated IDP - * - * @return true if the idp is federated - */ - protected boolean isFederatedIdP() { - - return StringUtils.isNotBlank(idp) && !FrameworkConstants.LOCAL.equals(idp); - } - - /** - * Sets a custom remote claim to the user. - * - * @param claimUri Remote claim uri - * @param claimValue Claim value - */ - private void setFederatedClaim(String claimUri, Object claimValue) { - - if (claimValue == null) { - claimValue = StringUtils.EMPTY; - } - ClaimMapping newClaimMapping = ClaimMapping.build(claimUri, claimUri, null, false); - authenticatedUser.getUserAttributes().put(newClaimMapping, String.valueOf(claimValue)); - } - - /** - * Gets the mapped remote claim value for the given local claim URI - * - * @param claimUri Local claim URI - * @return Mapped remote claim value from IdP - */ - private String getLocalMappedClaim(String claimUri) { - - Map idpAttributesMap = authenticatedUser.getUserAttributes(); - Map remoteMapping = FrameworkUtils.getClaimMappings(idpAttributesMap, false); - - String remoteMappedClaim = getRemoteClaimMappedToLocalClaim(claimUri, remoteMapping); - if (remoteMappedClaim != null) { - return remoteMapping.get(remoteMappedClaim); - } - return null; - } - - /** - * Get the local user claim value specified by the Claim URI. - * - * @param claimUri Local claim URI - * @return Claim value of the given claim URI for the local user if available. Null Otherwise. - */ - private String getLocalUserClaim(String claimUri) { - - int usersTenantId = IdentityTenantUtil.getTenantId(authenticatedUser.getTenantDomain()); - RealmService realmService = FrameworkServiceDataHolder.getInstance().getRealmService(); - try { - UserRealm userRealm = realmService.getTenantUserRealm(usersTenantId); - Map claimValues = - ((AbstractUserStoreManager) userRealm.getUserStoreManager()) - .getUserClaimValuesWithID(authenticatedUser.getUserId(), new String[] {claimUri}, null); - return claimValues.get(claimUri); - } catch (UserStoreException e) { - LOG.error(String.format("Error when getting claim : %s of user: %s", claimUri, authenticatedUser), e); - } catch (UserIdNotFoundException e) { - LOG.error("User id is not available for the user: " + authenticatedUser.getLoggableUserId(), e); + boolean isClaimSet = setMemberObject(claimUri, claimValue); + if (!isClaimSet) { + AbstractOpenJdkNashornJsObject.super.setMember(claimUri, claimValue); } - return null; } } diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/openjdk/nashorn/JsOpenJdkNashornHeaders.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/openjdk/nashorn/JsOpenJdkNashornHeaders.java index 3a1ab717c192..6ca72912b209 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/openjdk/nashorn/JsOpenJdkNashornHeaders.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/openjdk/nashorn/JsOpenJdkNashornHeaders.java @@ -18,7 +18,7 @@ package org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.openjdk.nashorn; -import org.openjdk.nashorn.api.scripting.AbstractJSObject; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.CommonJsHeaders; import java.util.Map; @@ -31,58 +31,39 @@ * Also it prevents writing an arbitrary values to the respective fields, keeping consistency on runtime. * Since Nashorn is deprecated in JDK 11 and onwards. We are introducing OpenJDK Nashorn engine. */ -public class JsOpenJdkNashornHeaders extends AbstractJSObject { - - private Map wrapped; - private HttpServletResponse response; +public class JsOpenJdkNashornHeaders extends CommonJsHeaders implements AbstractOpenJdkNashornJsObject { public JsOpenJdkNashornHeaders(Map wrapped, HttpServletResponse response) { - this.wrapped = wrapped; - this.response = response; + super(wrapped, response); } @Override public Object getMember(String name) { - if (wrapped == null) { - return super.getMember(name); + Object member = super.getMember(name); + if (member != null) { + return member; } else { - return wrapped.get(name); - } - } - - @Override - public boolean hasMember(String name) { - - if (wrapped == null) { - return false; - } else { - return wrapped.get(name) != null; + return AbstractOpenJdkNashornJsObject.super.getMember(name); } } @Override public void removeMember(String name) { - if (wrapped == null) { - super.removeMember(name); - } else { - if (wrapped.containsKey(name)) { - wrapped.remove(name); - } + boolean isRemoved = super.removeMemberObject(name); + if (!isRemoved) { + AbstractOpenJdkNashornJsObject.super.removeMember(name); } } @Override public void setMember(String name, Object value) { - if (wrapped == null) { - super.setMember(name, value); - } else { - wrapped.put(name, value); - //adds a new header to the response. - response.addHeader(name, String.valueOf(value)); + boolean isSet = super.setMemberObject(name, value); + if (!isSet) { + AbstractOpenJdkNashornJsObject.super.setMember(name, value); } } } diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/openjdk/nashorn/JsOpenJdkNashornParameters.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/openjdk/nashorn/JsOpenJdkNashornParameters.java index 3d782ef1b996..e414064209ea 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/openjdk/nashorn/JsOpenJdkNashornParameters.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/openjdk/nashorn/JsOpenJdkNashornParameters.java @@ -18,8 +18,6 @@ package org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.openjdk.nashorn; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.JsParameters; import java.util.Map; @@ -33,8 +31,6 @@ */ public class JsOpenJdkNashornParameters extends JsParameters implements AbstractOpenJdkNashornJsObject { - private static final Log LOG = LogFactory.getLog(JsOpenJdkNashornParameters.class); - public JsOpenJdkNashornParameters(Map wrapped) { super(wrapped); diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/openjdk/nashorn/JsOpenJdkNashornRuntimeClaims.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/openjdk/nashorn/JsOpenJdkNashornRuntimeClaims.java index d7655836df61..089d108729a3 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/openjdk/nashorn/JsOpenJdkNashornRuntimeClaims.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/openjdk/nashorn/JsOpenJdkNashornRuntimeClaims.java @@ -18,7 +18,7 @@ package org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.openjdk.nashorn; -import org.apache.commons.lang.StringUtils; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.base.JsBaseRuntimeClaims; import org.wso2.carbon.identity.application.authentication.framework.context.AuthenticationContext; import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser; @@ -26,7 +26,8 @@ * Represent the user's runtime claims. * Since Nashorn is deprecated in JDK 11 and onwards. We are introducing OpenJDK Nashorn engine. */ -public class JsOpenJdkNashornRuntimeClaims extends JsOpenJdkNashornClaims { +public class JsOpenJdkNashornRuntimeClaims extends JsOpenJdkNashornClaims + implements JsBaseRuntimeClaims, AbstractOpenJdkNashornJsObject { public JsOpenJdkNashornRuntimeClaims(AuthenticationContext context, int step, String idp) { @@ -38,7 +39,6 @@ public JsOpenJdkNashornRuntimeClaims(AuthenticationContext context, Authenticate super(context, user, false); } - @Override public Object getMember(String claimUri) { if (authenticatedUser != null) { @@ -47,7 +47,6 @@ public Object getMember(String claimUri) { return null; } - @Override public boolean hasMember(String claimUri) { if (authenticatedUser != null) { @@ -56,43 +55,10 @@ public boolean hasMember(String claimUri) { return false; } - @Override public void setMember(String claimUri, Object claimValue) { if (authenticatedUser != null) { setRuntimeClaim(claimUri, claimValue); } } - - private Object getRuntimeClaim(String claimUri) { - - String runtimeClaimValue = getContext().getRuntimeClaim(claimUri); - if (runtimeClaimValue != null) { - return runtimeClaimValue; - } - if (isFederatedIdP()) { - return getFederatedClaim(claimUri); - } - return getLocalClaim(claimUri); - } - - private boolean hasRuntimeClaim(String claimUri) { - - String claim = getContext().getRuntimeClaim(claimUri); - if (claim != null) { - return true; - } - if (isFederatedIdP()) { - return hasFederatedClaim(claimUri); - } - return hasLocalClaim(claimUri); - } - - private void setRuntimeClaim(String claimUri, Object claimValue) { - - if (claimValue == null) { - claimValue = StringUtils.EMPTY; - } - getContext().addRuntimeClaim(claimUri, String.valueOf(claimValue)); - } } diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/openjdk/nashorn/JsOpenJdkNashornServletRequest.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/openjdk/nashorn/JsOpenJdkNashornServletRequest.java index 743576ca58ae..ed7a903547aa 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/openjdk/nashorn/JsOpenJdkNashornServletRequest.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/openjdk/nashorn/JsOpenJdkNashornServletRequest.java @@ -20,14 +20,7 @@ import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.JsServletRequest; import org.wso2.carbon.identity.application.authentication.framework.context.TransientObjectWrapper; -import org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants; -import org.wso2.carbon.identity.core.util.IdentityUtil; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.Map; - -import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; /** @@ -49,37 +42,5 @@ public JsOpenJdkNashornServletRequest(TransientObjectWrapper super(wrapped); } - - @Override - public Object getMember(String name) { - - switch (name) { - case FrameworkConstants.JSAttributes.JS_HEADERS: - Map headers = new HashMap(); - Enumeration headerNames = getRequest().getHeaderNames(); - if (headerNames != null) { - while (headerNames.hasMoreElements()) { - String headerName = headerNames.nextElement(); - headers.put(headerName, getRequest().getHeader(headerName)); - } - } - return new JsOpenJdkNashornWritableParameters(headers); - case FrameworkConstants.JSAttributes.JS_PARAMS: - return new JsOpenJdkNashornParameters(getRequest().getParameterMap()); - case FrameworkConstants.JSAttributes.JS_COOKIES: - Map cookies = new HashMap(); - Cookie[] cookieArr = getRequest().getCookies(); - if (cookieArr != null) { - for (Cookie cookie : cookieArr) { - cookies.put(cookie.getName(), new JsOpenJdkNashornCookie(cookie)); - } - } - return new JsOpenJdkNashornWritableParameters(cookies); - case FrameworkConstants.JSAttributes.JS_REQUEST_IP: - return IdentityUtil.getClientIpAddress(getRequest()); - default: - return super.getMember(name); - } - } } diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/openjdk/nashorn/JsOpenJdkNashornServletResponse.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/openjdk/nashorn/JsOpenJdkNashornServletResponse.java index a0b19d515ca1..5d03a944fc6a 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/openjdk/nashorn/JsOpenJdkNashornServletResponse.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/openjdk/nashorn/JsOpenJdkNashornServletResponse.java @@ -20,11 +20,6 @@ import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.JsServletResponse; import org.wso2.carbon.identity.application.authentication.framework.context.TransientObjectWrapper; -import org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants; - -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; import javax.servlet.http.HttpServletResponse; @@ -48,22 +43,4 @@ public JsOpenJdkNashornServletResponse(TransientObjectWrapper headerNames = getResponse().getHeaderNames(); - if (headerNames != null) { - for (String element : headerNames) { - headers.put(element, getResponse().getHeader(element)); - } - } - return new JsOpenJdkNashornHeaders(headers, getResponse()); - default: - return super.getMember(name); - } - } } diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/openjdk/nashorn/JsOpenJdkNashornStep.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/openjdk/nashorn/JsOpenJdkNashornStep.java index 5b3ff2b93050..590440f1de59 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/openjdk/nashorn/JsOpenJdkNashornStep.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/openjdk/nashorn/JsOpenJdkNashornStep.java @@ -18,132 +18,53 @@ package org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.openjdk.nashorn; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.wso2.carbon.identity.application.authentication.framework.config.model.StepConfig; -import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.AbstractJSContextMemberObject; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.JsStep; import org.wso2.carbon.identity.application.authentication.framework.context.AuthenticationContext; -import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedIdPData; -import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser; -import org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; /** * Represents a authentication step. * Since Nashorn is deprecated in JDK 11 and onwards. We are introducing OpenJDK Nashorn engine. */ -public class JsOpenJdkNashornStep extends AbstractJSContextMemberObject implements AbstractOpenJdkNashornJsObject { - - private static final Log LOG = LogFactory.getLog(JsOpenJdkNashornStep.class); - - private int step; - private String authenticatedIdp; - private String authenticatedAuthenticator; +public class JsOpenJdkNashornStep extends JsStep implements AbstractOpenJdkNashornJsObject { @Deprecated public JsOpenJdkNashornStep(int step, String authenticatedIdp) { - this.step = step; - this.authenticatedIdp = authenticatedIdp; + super(step, authenticatedIdp); } public JsOpenJdkNashornStep(int step, String authenticatedIdp, String authenticatedAuthenticator) { - this.step = step; - this.authenticatedIdp = authenticatedIdp; - this.authenticatedAuthenticator = authenticatedAuthenticator; + super(step, authenticatedIdp, authenticatedAuthenticator); } @Deprecated public JsOpenJdkNashornStep(AuthenticationContext context, int step, String authenticatedIdp) { - this(step, authenticatedIdp, null); - initializeContext(context); + super(context, step, authenticatedIdp); } public JsOpenJdkNashornStep(AuthenticationContext context, int step, String authenticatedIdp, String authenticatedAuthenticator) { - this(step, authenticatedIdp, authenticatedAuthenticator); - initializeContext(context); + super(context, step, authenticatedIdp, authenticatedAuthenticator); } @Override public Object getMember(String name) { - switch (name) { - case FrameworkConstants.JSAttributes.JS_AUTHENTICATED_SUBJECT: - return new JsOpenJdkNashornAuthenticatedUser(getContext(), getSubject(), step, authenticatedIdp); - case FrameworkConstants.JSAttributes.JS_AUTHENTICATED_IDP: - return authenticatedIdp; - case FrameworkConstants.JSAttributes.JS_AUTHENTICATOR: - return authenticatedAuthenticator; - case FrameworkConstants.JSAttributes.JS_AUTHENTICATION_OPTIONS: - return getOptions(); - default: - return AbstractOpenJdkNashornJsObject.super.getMember(name); - } + Object member = super.getMember(name); + return member != null ? member : AbstractOpenJdkNashornJsObject.super.getMember(name); } @Override public boolean hasMember(String name) { - switch (name) { - case FrameworkConstants.JSAttributes.JS_AUTHENTICATED_SUBJECT: - return true; - case FrameworkConstants.JSAttributes.JS_AUTHENTICATED_IDP: - return true; - case FrameworkConstants.JSAttributes.JS_AUTHENTICATOR: - return true; - default: - return AbstractOpenJdkNashornJsObject.super.hasMember(name); - } + return super.hasMember(name) || AbstractOpenJdkNashornJsObject.super.hasMember(name); } - @Override public void removeMember(String name) { - LOG.warn("Step is readonly, hence the can't remove the member."); - } - - @Override - public void setMember(String name, Object value) { - - LOG.warn("Step is readonly, hence the setter is ignored."); - } - - private AuthenticatedUser getSubject() { - - if (authenticatedIdp != null) { - AuthenticatedIdPData idPData = getContext().getCurrentAuthenticatedIdPs().get(authenticatedIdp); - if (idPData == null) { - idPData = getContext().getPreviousAuthenticatedIdPs().get(authenticatedIdp); - } - if (idPData != null) { - return idPData.getUser(); - } - } - return null; - } - - private List> getOptions() { - - List> optionsList = new ArrayList<>(); - Optional optionalStepConfig = getContext().getSequenceConfig().getStepMap().values().stream() - .filter(stepConfig -> stepConfig.getOrder() == step).findFirst(); - optionalStepConfig.ifPresent(stepConfig -> stepConfig.getAuthenticatorList().forEach( - authConfig -> authConfig.getIdpNames().forEach(name -> { - Map option = new HashMap<>(); - option.put(FrameworkConstants.JSAttributes.IDP, name); - option.put(FrameworkConstants.JSAttributes.AUTHENTICATOR, authConfig.getApplicationAuthenticator() - .getName()); - optionsList.add(option); - }))); - return optionsList; + super.removeMemberObject(name); } } diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/openjdk/nashorn/JsOpenJdkNashornSteps.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/openjdk/nashorn/JsOpenJdkNashornSteps.java index f54bb82bcef5..d99eb6786ad3 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/openjdk/nashorn/JsOpenJdkNashornSteps.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/openjdk/nashorn/JsOpenJdkNashornSteps.java @@ -20,74 +20,33 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.wso2.carbon.identity.application.authentication.framework.config.model.AuthenticatorConfig; -import org.wso2.carbon.identity.application.authentication.framework.config.model.StepConfig; -import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.AbstractJSContextMemberObject; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.JsSteps; import org.wso2.carbon.identity.application.authentication.framework.context.AuthenticationContext; -import java.util.Optional; +import java.util.Objects; /** - * Returns when context.steps[ optionalStepConfig = getContext().getSequenceConfig().getStepMap().values().stream() - .filter(stepConfig -> stepConfig.getOrder() == step).findFirst(); - return optionalStepConfig.map(StepConfig::getAuthenticatedIdP).orElse(null); - } - - private String getAuthenticatedAuthenticatorOfStep(int step) { - - if (getContext().getSequenceConfig() == null) { - // Sequence config is not yet initialized. - return null; - } - - Optional optionalStepConfig = getContext().getSequenceConfig().getStepMap().values().stream() - .filter(stepConfig -> stepConfig.getOrder() == step).findFirst(); - AuthenticatorConfig authenticatorConfig = optionalStepConfig.map(StepConfig::getAuthenticatedAutenticator) - .orElse(null); - return authenticatorConfig != null ? authenticatorConfig.getName() : null; + Object jsStep = super.getSlot(step); + return Objects.nonNull(jsStep) ? jsStep : AbstractOpenJdkNashornJsObject.super.getSlot(step); } } diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/openjdk/nashorn/JsOpenJdkNashornWritableParameters.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/openjdk/nashorn/JsOpenJdkNashornWritableParameters.java index 628dad6f8c14..52a77b6b4cef 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/openjdk/nashorn/JsOpenJdkNashornWritableParameters.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/openjdk/nashorn/JsOpenJdkNashornWritableParameters.java @@ -18,28 +18,37 @@ package org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.openjdk.nashorn; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.JsWrapperFactoryProvider; + import java.util.Map; /** * Parameters that can be modified from the authentication script. * Since Nashorn is deprecated in JDK 11 and onwards. We are introducing OpenJDK Nashorn engine. */ -public class JsOpenJdkNashornWritableParameters extends JsOpenJdkNashornParameters { +public class JsOpenJdkNashornWritableParameters extends JsOpenJdkNashornParameters + implements AbstractOpenJdkNashornJsObject { public JsOpenJdkNashornWritableParameters(Map wrapped) { super(wrapped); } - @Override - public void removeMember(String name) { + public Object getMember(String name) { - if (getWrapped().containsKey(name)) { - getWrapped().remove(name); + Object member = getWrapped().get(name); + if (member instanceof Map) { + return JsWrapperFactoryProvider.getInstance().getWrapperFactory() + .createJsWritableParameters((Map) member); } + return member; + } + + public void removeMember(String name) { + + super.removeMemberObject(name); } - @Override public void setMember(String name, Object value) { getWrapped().put(name, value); diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/openjdk/nashorn/JsOpenJdkNashornGraphBuilder.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/openjdk/nashorn/JsOpenJdkNashornGraphBuilder.java index 4ce548a5b932..105162ae4807 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/openjdk/nashorn/JsOpenJdkNashornGraphBuilder.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/openjdk/nashorn/JsOpenJdkNashornGraphBuilder.java @@ -35,6 +35,7 @@ import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.DynamicDecisionNode; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.EndStep; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.FailNode; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.GenericSerializableJsFunction; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.JSExecutionMonitorData; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.JSExecutionSupervisor; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.JsGraphBuilder; @@ -88,7 +89,6 @@ public class JsOpenJdkNashornGraphBuilder extends JsGraphBuilder { private Map stepNamedMap; private AuthenticationGraph result = new AuthenticationGraph(); private AuthGraphNode currentNode = null; - private AuthenticationContext authenticationContext; private ScriptEngine engine; private static ThreadLocal contextForJs = new ThreadLocal<>(); private static ThreadLocal dynamicallyBuiltBaseNode = new ThreadLocal<>(); @@ -162,6 +162,11 @@ public AuthenticationGraph build() { } @Override + public AuthenticationDecisionEvaluator getScriptEvaluator(GenericSerializableJsFunction fn) { + + return new JsBasedEvaluator((OpenJdkNashornSerializableJsFunction) fn); + } + public AuthenticationDecisionEvaluator getScriptEvaluator(BaseSerializableJsFunction fn) { return new JsBasedEvaluator((OpenJdkNashornSerializableJsFunction) fn); @@ -425,7 +430,7 @@ protected void handleOptionsAsyncEvent(Map options, StepConfig s * @param stepOptions Options provided from the script for the step. * @param stepConfigMap StepConfigs of each step as a map. */ - private void handleStepOptions(StepConfig stepConfig, Map stepOptions, + protected void handleStepOptions(StepConfig stepConfig, Map stepOptions, Map stepConfigMap) { stepConfig.setForced(Boolean.parseBoolean(stepOptions.get(FrameworkConstants.JSAttributes.FORCE_AUTH_PARAM))); @@ -869,7 +874,7 @@ private static void attachEventListeners(Map eventsMap, AuthGrap } DynamicDecisionNode decisionNode = new DynamicDecisionNode(); addEventListeners(decisionNode, eventsMap); - if (!decisionNode.getFunctionMap().isEmpty()) { + if (!decisionNode.getGenericFunctionMap().isEmpty()) { attachToLeaf(currentNode, decisionNode); } } @@ -881,7 +886,7 @@ private void attachEventListeners(Map eventsMap) { } DynamicDecisionNode decisionNode = new DynamicDecisionNode(); addEventListeners(decisionNode, eventsMap); - if (!decisionNode.getFunctionMap().isEmpty()) { + if (!decisionNode.getGenericFunctionMap().isEmpty()) { attachToLeaf(currentNode, decisionNode); currentNode = decisionNode; } @@ -924,12 +929,12 @@ private static void addHandlers(ShowPromptNode showPromptNode, Map * Since Nashorn is deprecated in JDK 11 and onwards. We are introducing OpenJDK Nashorn engine. */ public class JsOpenJdkNashornGraphBuilderFactory implements JsBaseGraphBuilderFactory { diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/openjdk/nashorn/JsOpenJdkNashornWrapperFactory.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/openjdk/nashorn/JsOpenJdkNashornWrapperFactory.java index 4f7fdb088b8d..0269a09518bf 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/openjdk/nashorn/JsOpenJdkNashornWrapperFactory.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/openjdk/nashorn/JsOpenJdkNashornWrapperFactory.java @@ -21,10 +21,15 @@ import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.JsWrapperBaseFactory; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.openjdk.nashorn.JsOpenJdkNashornAuthenticatedUser; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.openjdk.nashorn.JsOpenJdkNashornAuthenticationContext; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.openjdk.nashorn.JsOpenJdkNashornClaims; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.openjdk.nashorn.JsOpenJdkNashornCookie; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.openjdk.nashorn.JsOpenJdkNashornHeaders; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.openjdk.nashorn.JsOpenJdkNashornParameters; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.openjdk.nashorn.JsOpenJdkNashornRuntimeClaims; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.openjdk.nashorn.JsOpenJdkNashornServletRequest; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.openjdk.nashorn.JsOpenJdkNashornServletResponse; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.openjdk.nashorn.JsOpenJdkNashornStep; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.openjdk.nashorn.JsOpenJdkNashornSteps; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.openjdk.nashorn.JsOpenJdkNashornWritableParameters; import org.wso2.carbon.identity.application.authentication.framework.context.AuthenticationContext; import org.wso2.carbon.identity.application.authentication.framework.context.TransientObjectWrapper; @@ -55,6 +60,13 @@ public JsOpenJdkNashornAuthenticatedUser createJsAuthenticatedUser(Authenticatio return new JsOpenJdkNashornAuthenticatedUser(authenticationContext, authenticatedUser); } + @Override + public JsOpenJdkNashornAuthenticatedUser createJsAuthenticatedUser(AuthenticationContext context, + AuthenticatedUser wrappedUser, int step, String idp) { + + return new JsOpenJdkNashornAuthenticatedUser(context, wrappedUser, step, idp); + } + @Override public JsOpenJdkNashornAuthenticationContext createJsAuthenticationContext (AuthenticationContext authenticationContext) { @@ -92,4 +104,49 @@ public JsOpenJdkNashornServletRequest createJsServletRequest(TransientObjectWrap return new JsOpenJdkNashornServletResponse(wrapped); } + + @Override + public JsOpenJdkNashornClaims createJsClaims(AuthenticationContext context, int step, String idp, + boolean isRemoteClaimRequest) { + + return new JsOpenJdkNashornClaims(context, step, idp, isRemoteClaimRequest); + } + + @Override + public JsOpenJdkNashornClaims createJsClaims(AuthenticationContext context, AuthenticatedUser user, + boolean isRemoteClaimRequest) { + + return new JsOpenJdkNashornClaims(context, user, isRemoteClaimRequest); + } + + @Override + public JsOpenJdkNashornRuntimeClaims createJsRuntimeClaims(AuthenticationContext context, int step, String idp) { + + return new JsOpenJdkNashornRuntimeClaims(context, step, idp); + } + + @Override + public JsOpenJdkNashornRuntimeClaims createJsRuntimeClaims(AuthenticationContext context, AuthenticatedUser user) { + + return new JsOpenJdkNashornRuntimeClaims(context, user); + } + + @Override + public JsOpenJdkNashornStep createJsStep(AuthenticationContext context, int step, String authenticatedIdp, + String authenticatedAuthenticator) { + + return new JsOpenJdkNashornStep(context, step, authenticatedIdp, authenticatedAuthenticator); + } + + @Override + public JsOpenJdkNashornHeaders createJsHeaders(Map wrapped, HttpServletResponse response) { + + return new JsOpenJdkNashornHeaders(wrapped, response); + } + + @Override + public JsOpenJdkNashornSteps createJsSteps(AuthenticationContext context) { + + return new JsOpenJdkNashornSteps(context); + } } diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/handler/sequence/impl/GraalSelectAcrFromFunction.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/handler/sequence/impl/GraalSelectAcrFromFunction.java new file mode 100644 index 000000000000..c00268bb0574 --- /dev/null +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/handler/sequence/impl/GraalSelectAcrFromFunction.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.application.authentication.framework.handler.sequence.impl; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.graalvm.polyglot.HostAccess; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.JsAuthenticationContext; +import org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +/** + * Select the preferred acr value from the available list. + */ +public class GraalSelectAcrFromFunction implements SelectOneFunction { + + private static final Log log = LogFactory.getLog(SelectAcrFromFunction.class); + + @HostAccess.Export + public String evaluate(JsAuthenticationContext context, Object possibleOutcomesObj) { + + String[] possibleOutcomes = extractPossibleOutcomes(context, possibleOutcomesObj); + List acrListRequested = context.getWrapped().getRequestedAcr(); + if (acrListRequested == null || acrListRequested.isEmpty()) { + if (log.isDebugEnabled()) { + log.debug("ACR values from context is empty. Selecting the default outcome as null."); + } + return null; + } + if (possibleOutcomes.length > 0) { + return selectBestOutcome(acrListRequested, possibleOutcomes); + } + return null; + } + + private String[] extractPossibleOutcomes(JsAuthenticationContext context, Object possibleOutcomesObj) { + + String[] possibleOutcomes; + if (possibleOutcomesObj instanceof List) { + possibleOutcomes = ((List) possibleOutcomesObj).toArray(new String[0]); + } else { + log.error("Invalid argument provided for possible outcomes for " + + FrameworkConstants.JSAttributes.JS_FUNC_SELECT_ACR_FROM + " function in service provider: " + + context.getWrapped().getServiceProviderName() + ". Expected array of strings."); + possibleOutcomes = new String[0]; + } + return possibleOutcomes; + } + + private String selectBestOutcome(List acrListRequested, String[] possibleOutcomes) { + + Map acrRequestedWithPriority = + new TreeMap<>(Collections.reverseOrder((o1, o2) -> o2.compareTo(o1))); + String acrSelected = null; + + for (String acrChecked : acrListRequested) { + for (int x = 0; x < possibleOutcomes.length; x++) { + String outcomeToTest = possibleOutcomes[x]; + if (outcomeToTest.equals(acrChecked)) { + if (log.isDebugEnabled()) { + log.debug( + "Reassigning Best Match for the outcome : " + outcomeToTest + " with priority : " + x + + 1); + } + acrRequestedWithPriority.put(x + 1, acrChecked); + break; + } + } + } + if (!acrRequestedWithPriority.entrySet().isEmpty()) { + acrSelected = acrRequestedWithPriority.entrySet().iterator().next().getValue(); + } + return acrSelected; + } +} diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/handler/sequence/impl/GraphBasedSequenceHandler.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/handler/sequence/impl/GraphBasedSequenceHandler.java index 40854afb9ab7..f73f74884126 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/handler/sequence/impl/GraphBasedSequenceHandler.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/handler/sequence/impl/GraphBasedSequenceHandler.java @@ -31,12 +31,12 @@ import org.wso2.carbon.identity.application.authentication.framework.config.model.StepConfig; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.AuthGraphNode; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.AuthenticationGraph; -import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.BaseSerializableJsFunction; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.DynamicDecisionNode; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.EndStep; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.FailNode; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.GenericSerializableJsFunction; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.JsBaseGraphBuilder; -import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.JsBaseGraphBuilderFactory; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.JsGenericGraphBuilderFactory; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.JsWrapperFactoryProvider; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.LongWaitNode; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.ShowPromptNode; @@ -266,7 +266,7 @@ private boolean handlePrompt(HttpServletRequest request, HttpServletResponse res context.setProperty(FrameworkConstants.JSAttributes.PROP_CURRENT_NODE, nextNode); context.setReturning(false); } else { - if (promptNode.getHandlerMap().get(ShowPromptNode.PRE_HANDLER) != null) { + if (promptNode.getGenericHandlerMap().get(ShowPromptNode.PRE_HANDLER) != null) { Object result = evaluateHandler(ShowPromptNode.PRE_HANDLER, promptNode, context, promptNode .getParameters().get(STEP_IDENTIFIER_PARAM)); if (Boolean.TRUE.equals(result)) { @@ -749,7 +749,7 @@ private void handleDecisionPoint(HttpServletRequest request, HttpServletResponse executeFunction("onSuccess", dynamicDecisionNode, context); break; case FAIL_COMPLETED: - if (dynamicDecisionNode.getFunctionMap().get("onFail") != null) { + if (dynamicDecisionNode.getGenericFunctionMap().get("onFail") != null) { executeFunction("onFail", dynamicDecisionNode, context); } else { if (context.isRetrying()) { @@ -778,9 +778,9 @@ private void handleDecisionPoint(HttpServletRequest request, HttpServletResponse private void executeFunction(String outcomeName, DynamicDecisionNode dynamicDecisionNode, AuthenticationContext context) { - BaseSerializableJsFunction fn = dynamicDecisionNode.getFunctionMap().get(outcomeName); + GenericSerializableJsFunction fn = dynamicDecisionNode.getGenericFunctionMap().get(outcomeName); FrameworkServiceDataHolder dataHolder = FrameworkServiceDataHolder.getInstance(); - JsBaseGraphBuilderFactory jsGraphBuilderFactory = dataHolder.getJsGraphBuilderFactory(); + JsGenericGraphBuilderFactory jsGraphBuilderFactory = dataHolder.getJsGenericGraphBuilderFactory(); JsBaseGraphBuilder graphBuilder = jsGraphBuilderFactory.createBuilder(context, context .getSequenceConfig().getAuthenticationGraph().getStepMap(), dynamicDecisionNode); graphBuilder.getScriptEvaluator(fn).evaluate(context, JsWrapperFactoryProvider.getInstance() @@ -794,9 +794,9 @@ private void executeFunction(String outcomeName, DynamicDecisionNode dynamicDeci private void executeFunction(String outcomeName, DynamicDecisionNode dynamicDecisionNode, AuthenticationContext context, Map data) { - BaseSerializableJsFunction fn = dynamicDecisionNode.getFunctionMap().get(outcomeName); + GenericSerializableJsFunction fn = dynamicDecisionNode.getGenericFunctionMap().get(outcomeName); FrameworkServiceDataHolder dataHolder = FrameworkServiceDataHolder.getInstance(); - JsBaseGraphBuilderFactory jsGraphBuilderFactory = dataHolder.getJsGraphBuilderFactory(); + JsGenericGraphBuilderFactory jsGraphBuilderFactory = dataHolder.getJsGenericGraphBuilderFactory(); JsBaseGraphBuilder jsGraphBuilder = jsGraphBuilderFactory.createBuilder(context, context .getSequenceConfig().getAuthenticationGraph().getStepMap(), dynamicDecisionNode); jsGraphBuilder.getScriptEvaluator(fn).evaluate(context, JsWrapperFactoryProvider.getInstance() @@ -811,9 +811,9 @@ private void executeFunction(String outcomeName, DynamicDecisionNode dynamicDeci private Object evaluateHandler(String outcomeName, ShowPromptNode dynamicDecisionNode, AuthenticationContext context, Object stepId) { - BaseSerializableJsFunction fn = dynamicDecisionNode.getHandlerMap().get(outcomeName); + GenericSerializableJsFunction fn = dynamicDecisionNode.getGenericHandlerMap().get(outcomeName); FrameworkServiceDataHolder dataHolder = FrameworkServiceDataHolder.getInstance(); - JsBaseGraphBuilderFactory jsGraphBuilderFactory = dataHolder.getJsGraphBuilderFactory(); + JsGenericGraphBuilderFactory jsGraphBuilderFactory = dataHolder.getJsGenericGraphBuilderFactory(); JsBaseGraphBuilder graphBuilder = jsGraphBuilderFactory.createBuilder(context, context .getSequenceConfig().getAuthenticationGraph().getStepMap(), dynamicDecisionNode); return graphBuilder.getScriptEvaluator(fn).evaluate(context, JsWrapperFactoryProvider.getInstance() diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/internal/FrameworkServiceComponent.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/internal/FrameworkServiceComponent.java index 89465a790021..2c8f48bc7584 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/internal/FrameworkServiceComponent.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/internal/FrameworkServiceComponent.java @@ -49,8 +49,8 @@ import org.wso2.carbon.identity.application.authentication.framework.config.loader.UIBasedConfigurationLoader; import org.wso2.carbon.identity.application.authentication.framework.config.model.AuthenticatorConfig; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.JSExecutionSupervisor; -import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.JsBaseGraphBuilderFactory; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.JsFunctionRegistryImpl; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.JsGenericGraphBuilderFactory; import org.wso2.carbon.identity.application.authentication.framework.dao.impl.CacheBackedLongWaitStatusDAO; import org.wso2.carbon.identity.application.authentication.framework.dao.impl.LongWaitStatusDAOImpl; import org.wso2.carbon.identity.application.authentication.framework.exception.FrameworkException; @@ -276,12 +276,13 @@ protected void activate(ComponentContext ctxt) { UIBasedConfigurationLoader uiBasedConfigurationLoader = new UIBasedConfigurationLoader(); dataHolder.setSequenceLoader(uiBasedConfigurationLoader); - JsBaseGraphBuilderFactory jsGraphBuilderFactory = FrameworkUtils.createJsGraphBuilderFactoryFromConfig(); + JsGenericGraphBuilderFactory jsGraphBuilderFactory = + FrameworkUtils.createJsGenericGraphBuilderFactoryFromConfig(); if (jsGraphBuilderFactory != null) { bundleContext.registerService(JsFunctionRegistry.class, dataHolder.getJsFunctionRegistry(), null); dataHolder.setAdaptiveAuthenticationAvailable(true); jsGraphBuilderFactory.init(); - dataHolder.setJsGraphBuilderFactory(jsGraphBuilderFactory); + dataHolder.setJsGenericGraphBuilderFactory(jsGraphBuilderFactory); } else { dataHolder.setAdaptiveAuthenticationAvailable(false); log.warn("Adaptive authentication is disabled."); diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/internal/FrameworkServiceDataHolder.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/internal/FrameworkServiceDataHolder.java index 1d7373284b35..1455092ab4b0 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/internal/FrameworkServiceDataHolder.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/internal/FrameworkServiceDataHolder.java @@ -30,6 +30,7 @@ import org.wso2.carbon.identity.application.authentication.framework.config.loader.SequenceLoader; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.JSExecutionSupervisor; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.JsBaseGraphBuilderFactory; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.JsGenericGraphBuilderFactory; import org.wso2.carbon.identity.application.authentication.framework.exception.FrameworkException; import org.wso2.carbon.identity.application.authentication.framework.handler.approles.ApplicationRolesResolver; import org.wso2.carbon.identity.application.authentication.framework.handler.claims.ClaimFilter; @@ -83,7 +84,7 @@ public class FrameworkServiceDataHolder { private List httpIdentityResponseFactories = new ArrayList<>(); private AuthenticationDataPublisher authnDataPublisherProxy = null; private SequenceLoader sequenceLoader = null; - private JsBaseGraphBuilderFactory jsGraphBuilderFactory; + private JsGenericGraphBuilderFactory jsGraphBuilderFactory; private AuthenticationMethodNameTranslator authenticationMethodNameTranslator; private List postAuthenticationHandlers = new ArrayList<>(); private PostAuthenticationMgtService postAuthenticationMgtService = null; @@ -284,6 +285,14 @@ public void setAuthenticationMethodNameTranslator( public JsBaseGraphBuilderFactory getJsGraphBuilderFactory() { + if (jsGraphBuilderFactory instanceof JsBaseGraphBuilderFactory) { + return (JsBaseGraphBuilderFactory) jsGraphBuilderFactory; + } + return null; + } + + public JsGenericGraphBuilderFactory getJsGenericGraphBuilderFactory() { + return jsGraphBuilderFactory; } @@ -292,6 +301,11 @@ public void setJsGraphBuilderFactory(JsBaseGraphBuilderFactory jsGraphBuilderFac this.jsGraphBuilderFactory = jsGraphBuilderFactory; } + public void setJsGenericGraphBuilderFactory(JsGenericGraphBuilderFactory jsGraphBuilderFactory) { + + this.jsGraphBuilderFactory = jsGraphBuilderFactory; + } + public MultiAttributeLoginService getMultiAttributeLoginService() { return multiAttributeLoginService; diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/util/FrameworkConstants.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/util/FrameworkConstants.java index 44c9795fb67f..1b62b1078bb2 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/util/FrameworkConstants.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/util/FrameworkConstants.java @@ -228,6 +228,7 @@ public abstract class FrameworkConstants { public static final String THREAD_LOCAL_SCRIPT_ENGINE_CONFIG = "AdaptiveAuth.LimitScriptEngineCreation"; public static final String OPENJDK_NASHORN = "openjdkNashorn"; public static final String NASHORN = "nashorn"; + public static final String GRAAL_JS = "graaljs"; // Attribute sync related constants. public static final String ATTRIBUTE_SYNC_METHOD = "attributeSyncMethod"; @@ -608,6 +609,11 @@ public static class JSAttributes { public static final String SUBJECT_IDENTIFIER_PARAM = "markAsSubjectIdentifierStep"; public static final String SUBJECT_ATTRIBUTE_PARAM = "markAsSubjectAttributeStep"; public static final String SKIP_PROMPT = "skipPrompt"; + + public static final String POLYGLOT_SOURCE = "src.js"; + public static final String POLYGLOT_LANGUAGE = "js"; + public static final String GRAALJS = "graaljs"; + public static final String NASHORN = "nashorn"; } /** @@ -667,6 +673,9 @@ public static class AdaptiveAuthentication { = "AdaptiveAuthExecutionSupervisorResult"; public static final String AUTHENTICATOR_NAME_IN_AUTH_CONFIG = "AdaptiveAuth.AuthenticatorNameInAuthConfig.Enable"; + public static final String GRAALJS_SCRIPT_STATEMENTS_LIMIT + = "AdaptiveAuth.GraalJS.ScriptStatementsLimit"; + public static final int DEFAULT_GRAALJS_SCRIPT_STATEMENTS_LIMIT = 0; } /** diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/util/FrameworkUtils.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/util/FrameworkUtils.java index 98ca00514508..ad5da87a1d1c 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/util/FrameworkUtils.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/util/FrameworkUtils.java @@ -66,7 +66,9 @@ import org.wso2.carbon.identity.application.authentication.framework.config.model.SequenceConfig; import org.wso2.carbon.identity.application.authentication.framework.config.model.StepConfig; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.JsBaseGraphBuilderFactory; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.JsGenericGraphBuilderFactory; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.JsGraphBuilderFactory; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.graaljs.JsGraalGraphBuilderFactory; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.openjdk.nashorn.JsOpenJdkNashornGraphBuilderFactory; import org.wso2.carbon.identity.application.authentication.framework.context.AuthenticationContext; import org.wso2.carbon.identity.application.authentication.framework.context.SessionContext; @@ -252,6 +254,7 @@ public class FrameworkUtils { private static Boolean authenticatorNameInAuthConfigPreference; private static final String OPENJDK_SCRIPTER_CLASS_NAME = "org.openjdk.nashorn.api.scripting.ScriptObjectMirror"; private static final String JDK_SCRIPTER_CLASS_NAME = "jdk.nashorn.api.scripting.ScriptObjectMirror"; + private static final String GRAALJS_SCRIPTER_CLASS_NAME = "org.graalvm.polyglot.Context"; private FrameworkUtils() { } @@ -3013,7 +3016,8 @@ public static String getStandardDialect(String clientType, ApplicationConfig app */ public static Object toJsSerializable(Object value) { - return FrameworkServiceDataHolder.getInstance().getJsGraphBuilderFactory().getJsUtil().toJsSerializable(value); + return FrameworkServiceDataHolder.getInstance().getJsGenericGraphBuilderFactory() + .getJsUtil().toJsSerializable(value); } /** @@ -4001,22 +4005,40 @@ public static boolean isPreviousIdPAuthenticationFlowHandler(AuthenticationConte public static JsBaseGraphBuilderFactory createJsGraphBuilderFactoryFromConfig() { + JsGenericGraphBuilderFactory jsGenericGraphBuilderFactory = createJsGenericGraphBuilderFactoryFromConfig(); + if (jsGenericGraphBuilderFactory instanceof JsBaseGraphBuilderFactory) { + return (JsBaseGraphBuilderFactory) jsGenericGraphBuilderFactory; + } + return null; + } + + public static JsGenericGraphBuilderFactory createJsGenericGraphBuilderFactoryFromConfig() { + String scriptEngineName = IdentityUtil.getProperty(FrameworkConstants.SCRIPT_ENGINE_CONFIG); if (scriptEngineName != null) { - if (StringUtils.equalsIgnoreCase(FrameworkConstants.OPENJDK_NASHORN, scriptEngineName)) { + if (StringUtils.equalsIgnoreCase(FrameworkConstants.GRAAL_JS, scriptEngineName)) { + return new JsGraalGraphBuilderFactory(); + } else if (StringUtils.equalsIgnoreCase(FrameworkConstants.OPENJDK_NASHORN, scriptEngineName)) { return new JsOpenJdkNashornGraphBuilderFactory(); + } else if (StringUtils.equalsIgnoreCase(FrameworkConstants.NASHORN, scriptEngineName)) { + return new JsGraphBuilderFactory(); } } // Config is not set. Hence going with class for name approach. try { - Class.forName(OPENJDK_SCRIPTER_CLASS_NAME); - return new JsOpenJdkNashornGraphBuilderFactory(); + Class.forName(GRAALJS_SCRIPTER_CLASS_NAME); + return new JsGraalGraphBuilderFactory(); } catch (ClassNotFoundException e) { try { - Class.forName(JDK_SCRIPTER_CLASS_NAME); - return new JsGraphBuilderFactory(); + Class.forName(OPENJDK_SCRIPTER_CLASS_NAME); + return new JsOpenJdkNashornGraphBuilderFactory(); } catch (ClassNotFoundException classNotFoundException) { - return null; + try { + Class.forName(JDK_SCRIPTER_CLASS_NAME); + return new JsGraphBuilderFactory(); + } catch (ClassNotFoundException ex) { + return null; + } } } } diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/test/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/JsGraalGraphBuilderTest.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/test/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/JsGraalGraphBuilderTest.java new file mode 100644 index 000000000000..930f15f9b90d --- /dev/null +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/test/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/JsGraalGraphBuilderTest.java @@ -0,0 +1,375 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.application.authentication.framework.config.model.graph; + +import org.mockito.Mock; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import org.wso2.carbon.identity.application.authentication.framework.AbstractFrameworkTest; +import org.wso2.carbon.identity.application.authentication.framework.FederatedApplicationAuthenticator; +import org.wso2.carbon.identity.application.authentication.framework.LocalApplicationAuthenticator; +import org.wso2.carbon.identity.application.authentication.framework.config.model.AuthenticatorConfig; +import org.wso2.carbon.identity.application.authentication.framework.config.model.StepConfig; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.graaljs.JsGraalGraphBuilder; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.graaljs.JsGraalGraphBuilderFactory; +import org.wso2.carbon.identity.application.authentication.framework.context.AuthenticationContext; +import org.wso2.carbon.identity.application.authentication.framework.internal.FrameworkServiceDataHolder; +import org.wso2.carbon.identity.application.common.ApplicationAuthenticatorService; +import org.wso2.carbon.identity.application.common.model.FederatedAuthenticatorConfig; +import org.wso2.carbon.identity.application.common.model.IdentityProvider; +import org.wso2.carbon.identity.application.common.model.LocalAuthenticatorConfig; +import org.wso2.carbon.identity.application.common.model.ServiceProvider; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.initMocks; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertNull; +import static org.testng.Assert.assertTrue; + +@Test +public class JsGraalGraphBuilderTest extends AbstractFrameworkTest { + + private JsGraalGraphBuilderFactory jsGraphBuilderFactory; + @Mock + private LocalApplicationAuthenticator localApplicationAuthenticator; + + @Mock + private LocalApplicationAuthenticator totpApplicationAuthenticator; + + @Mock + private FederatedApplicationAuthenticator federatedApplicationAuthenticator; + + @BeforeTest + public void setUp() { + + initMocks(this); + jsGraphBuilderFactory = new JsGraalGraphBuilderFactory(); + JSExecutionSupervisor jsExecutionSupervisor = new JSExecutionSupervisor(1, 5000L); + FrameworkServiceDataHolder.getInstance().setJsExecutionSupervisor(jsExecutionSupervisor); + } + + @AfterTest + public void teardown() { + + FrameworkServiceDataHolder.getInstance().getJsExecutionSupervisor().shutdown(); + } + + @Test + public void testCreateDirectJavaInvalidStepId() throws Exception { + + ServiceProvider sp1 = getTestServiceProvider("js-sp-1.xml"); + AuthenticationContext context = getAuthenticationContext(sp1); + Map stepConfigMap = new HashMap<>(); + stepConfigMap.put(1, new StepConfig()); + JsGraalGraphBuilder jsGraphBuilder = jsGraphBuilderFactory.createBuilder(context, stepConfigMap); + jsGraphBuilder.executeStep(2); + + AuthenticationGraph graph = jsGraphBuilder.build(); + assertNull(graph.getStartNode()); + } + + @Test + public void testCreateDirectJava() throws Exception { + + ServiceProvider sp1 = getTestServiceProvider("js-sp-1.xml"); + AuthenticationContext context = getAuthenticationContext(sp1); + Map stepConfigMap = new HashMap<>(); + stepConfigMap.put(1, new StepConfig()); + stepConfigMap.put(2, new StepConfig()); + JsGraalGraphBuilder jsGraphBuilder = jsGraphBuilderFactory.createBuilder(context, stepConfigMap); + jsGraphBuilder.executeStep(1); + jsGraphBuilder.executeStep(2); + + AuthenticationGraph graph = jsGraphBuilder.build(); + assertNotNull(graph.getStartNode()); + assertTrue(graph.getStartNode() instanceof StepConfigGraphNode); + + StepConfigGraphNode firstStep = (StepConfigGraphNode) graph.getStartNode(); + assertNotNull(firstStep.getNext()); + assertTrue(firstStep.getNext() instanceof StepConfigGraphNode); + } + + @Test + public void testCreateJavascript() throws Exception { + + String script = "var onLoginRequest = function(context) { executeStep(1, { onSuccess : function(context) {" + + "executeStep(2);}})};"; + + ServiceProvider sp1 = getTestServiceProvider("js-sp-1.xml"); + AuthenticationContext context = getAuthenticationContext(sp1); + + Map stepConfigMap = new HashMap<>(); + stepConfigMap.put(1, new StepConfig()); + stepConfigMap.put(2, new StepConfig()); + + JsGraalGraphBuilder jsGraphBuilder = jsGraphBuilderFactory.createBuilder(context, stepConfigMap); + jsGraphBuilder.createWith(script); + + AuthenticationGraph graph = jsGraphBuilder.build(); + assertNotNull(graph.getStartNode()); + assertTrue(graph.getStartNode() instanceof StepConfigGraphNode); + + StepConfigGraphNode firstStep = (StepConfigGraphNode) graph.getStartNode(); + assertNotNull(firstStep.getNext()); + assertTrue(firstStep.getNext() instanceof DynamicDecisionNode); + } + + @Test(dataProvider = "filterOptionsDataProvider") + public void testFilterOptions(Map> options, StepConfig stepConfig, + int expectedStepsAfterFilter) throws Exception { + + ServiceProvider sp1 = getTestServiceProvider("js-sp-1.xml"); + AuthenticationContext context = getAuthenticationContext(sp1); + + Map stepConfigMap = new HashMap<>(); + stepConfigMap.put(1, stepConfig); + + JsGraalGraphBuilder jsGraphBuilder = jsGraphBuilderFactory.createBuilder(context, stepConfigMap); + jsGraphBuilder.filterOptions(options, stepConfig); + assertEquals(stepConfig.getAuthenticatorList().size(), expectedStepsAfterFilter, + "Authentication options after filtering mismatches expected. " + options); + } + + @DataProvider + public Object[][] filterOptionsDataProvider() { + + ApplicationAuthenticatorService.getInstance().getLocalAuthenticators().clear(); + LocalAuthenticatorConfig basic = new LocalAuthenticatorConfig(); + basic.setName("BasicAuthenticator"); + basic.setDisplayName("basic"); + LocalAuthenticatorConfig totp = new LocalAuthenticatorConfig(); + totp.setName("TOTPAuthenticator"); + totp.setDisplayName("totp"); + ApplicationAuthenticatorService.getInstance().getLocalAuthenticators().add(basic); + ApplicationAuthenticatorService.getInstance().getLocalAuthenticators().add(totp); + + IdentityProvider localIdp = new IdentityProvider(); + localIdp.setId("LOCAL"); + localIdp.setFederatedAuthenticatorConfigs(new FederatedAuthenticatorConfig[0]); + + FederatedAuthenticatorConfig samlFederated = new FederatedAuthenticatorConfig(); + samlFederated.setDisplayName("samlsso"); + samlFederated.setName("SAMLAuthenticator"); + + FederatedAuthenticatorConfig oidcFederated = new FederatedAuthenticatorConfig(); + oidcFederated.setDisplayName("oidc"); + oidcFederated.setName("OIDCAuthenticator"); + + FederatedAuthenticatorConfig twitterFederated = new FederatedAuthenticatorConfig(); + twitterFederated.setDisplayName("twitter"); + twitterFederated.setName("TwitterAuthenticator"); + + IdentityProvider customIdp1 = new IdentityProvider(); + customIdp1.setId("customIdp1"); + customIdp1.setFederatedAuthenticatorConfigs(new FederatedAuthenticatorConfig[]{samlFederated, oidcFederated}); + customIdp1.setDefaultAuthenticatorConfig(samlFederated); + + IdentityProvider customIdp2 = new IdentityProvider(); + customIdp2.setId("customIdp2"); + customIdp2.setFederatedAuthenticatorConfigs(new FederatedAuthenticatorConfig[]{twitterFederated}); + customIdp2.setDefaultAuthenticatorConfig(twitterFederated); + + AuthenticatorConfig basicAuthConfig = new AuthenticatorConfig(); + basicAuthConfig.setName("BasicAuthenticator"); + basicAuthConfig.setEnabled(true); + basicAuthConfig.getIdps().put("LOCAL", localIdp); + + AuthenticatorConfig totpAuthConfig = new AuthenticatorConfig(); + totpAuthConfig.setName("TOTPAuthenticator"); + totpAuthConfig.setEnabled(true); + totpAuthConfig.getIdps().put("LOCAL", localIdp); + + AuthenticatorConfig samlAuthConfig = new AuthenticatorConfig(); + samlAuthConfig.setName("SAMLAuthenticator"); + samlAuthConfig.setEnabled(true); + samlAuthConfig.getIdps().put("customIdp1", customIdp1); + + AuthenticatorConfig oidcAuthConfig = new AuthenticatorConfig(); + oidcAuthConfig.setName("OIDCAuthenticator"); + oidcAuthConfig.setEnabled(true); + oidcAuthConfig.getIdps().put("customIdp1", customIdp1); + + AuthenticatorConfig twitterAuthConfig = new AuthenticatorConfig(); + twitterAuthConfig.setName("TwitterAuthenticator"); + twitterAuthConfig.setEnabled(true); + twitterAuthConfig.getIdps().put("customIdp2", customIdp2); + + StepConfig stepWithSingleOption = new StepConfig(); + stepWithSingleOption.setAuthenticatorList(Collections.singletonList(basicAuthConfig)); + Map> singleOptionConfig = new HashMap<>(); + singleOptionConfig.put("0", Collections.singletonMap("authenticator", "basic")); + + StepConfig stepWithMultipleOptions = new StepConfig(); + stepWithMultipleOptions.setAuthenticatorList( + new ArrayList<>(Arrays.asList(basicAuthConfig, totpAuthConfig, oidcAuthConfig, twitterAuthConfig))); + + Map oidcOption = new HashMap<>(); + oidcOption.put("idp", "customIdp1"); + oidcOption.put("authenticator", "oidc"); + + Map twitterOption = new HashMap<>(); + twitterOption.put("idp", "customIdp2"); + twitterOption.put("authenticator", "twitter"); + + Map invalidOption = new HashMap<>(); + invalidOption.put("idp", "customIdp1"); + invalidOption.put("authenticator", "twitter"); + + Map> multipleOptionConfig = new HashMap<>(); + multipleOptionConfig.put("0", Collections.singletonMap("authenticator", "basic")); + multipleOptionConfig.put("1", oidcOption); + multipleOptionConfig.put("2", twitterOption); + + Map> multipleAndInvalidOptionConfig = new HashMap<>(); + multipleAndInvalidOptionConfig.put("0", Collections.singletonMap("authenticator", "basic")); + multipleAndInvalidOptionConfig.put("1", oidcOption); + multipleAndInvalidOptionConfig.put("2", invalidOption); + + Map> idpOnlyOptionConfig = new HashMap<>(); + idpOnlyOptionConfig.put("0", Collections.singletonMap("authenticator", "basic")); + idpOnlyOptionConfig.put("1", Collections.singletonMap("idp", "customIdp1")); + + Map> singleInvalidOptionConfig = new HashMap<>(); + singleInvalidOptionConfig.put("0", invalidOption); + + return new Object[][]{{singleOptionConfig, duplicateStepConfig(stepWithSingleOption), 1}, + {singleOptionConfig, duplicateStepConfig(stepWithMultipleOptions), 1}, + {multipleOptionConfig, duplicateStepConfig(stepWithMultipleOptions), 3}, + {multipleAndInvalidOptionConfig, duplicateStepConfig(stepWithMultipleOptions), 2}, + {singleInvalidOptionConfig, duplicateStepConfig(stepWithMultipleOptions), 4}, + {idpOnlyOptionConfig, duplicateStepConfig(stepWithMultipleOptions), 2}, }; + } + + private StepConfig duplicateStepConfig(StepConfig stepConfig) { + + StepConfig newStepConfig = new StepConfig(); + newStepConfig.setAuthenticatorList(new ArrayList<>(stepConfig.getAuthenticatorList())); + return newStepConfig; + } + + @Test(dataProvider = "filterParamsDataProvider", alwaysRun = true) + public void testParamsOptions(Map options, StepConfig stepConfig, String authenticatorName, + String key, String value) throws Exception { + + ServiceProvider sp1 = getTestServiceProvider("js-sp-1.xml"); + AuthenticationContext context = getAuthenticationContext(sp1); + + Map stepConfigMap = new HashMap<>(); + stepConfigMap.put(1, stepConfig); + + JsGraalGraphBuilder jsGraphBuilder = jsGraphBuilderFactory.createBuilder(context, stepConfigMap); + jsGraphBuilder.authenticatorParamsOptions(options, stepConfig); + assertEquals(context.getAuthenticatorParams(authenticatorName).get(key), value, "Params are not set expected"); + } + + @DataProvider + public Object[][] filterParamsDataProvider() { + + ApplicationAuthenticatorService.getInstance().getLocalAuthenticators().clear(); + LocalAuthenticatorConfig basic = new LocalAuthenticatorConfig(); + basic.setName("BasicAuthenticator"); + basic.setDisplayName("basic"); + + LocalAuthenticatorConfig totp = new LocalAuthenticatorConfig(); + totp.setName("TOTPAuthenticator"); + totp.setDisplayName("totp"); + + ApplicationAuthenticatorService.getInstance().getLocalAuthenticators().add(basic); + ApplicationAuthenticatorService.getInstance().getLocalAuthenticators().add(totp); + + FederatedAuthenticatorConfig twitterFederated = new FederatedAuthenticatorConfig(); + twitterFederated.setDisplayName("twitter"); + twitterFederated.setName("TwitterAuthenticator"); + + IdentityProvider localIdp = new IdentityProvider(); + localIdp.setId("local"); + localIdp.setFederatedAuthenticatorConfigs(new FederatedAuthenticatorConfig[0]); + + IdentityProvider customIdp2 = new IdentityProvider(); + customIdp2.setId("customIdp2"); + customIdp2.setFederatedAuthenticatorConfigs(new FederatedAuthenticatorConfig[]{twitterFederated}); + customIdp2.setDefaultAuthenticatorConfig(twitterFederated); + + AuthenticatorConfig basicAuthConfig = new AuthenticatorConfig(); + basicAuthConfig.setName("BasicAuthenticator"); + basicAuthConfig.setEnabled(true); + when(localApplicationAuthenticator.getName()).thenReturn("BasicAuthenticator"); + when(localApplicationAuthenticator.getFriendlyName()).thenReturn("basic"); + basicAuthConfig.setApplicationAuthenticator(localApplicationAuthenticator); + basicAuthConfig.getIdps().put("local", localIdp); + + AuthenticatorConfig totpAuthConfig = new AuthenticatorConfig(); + totpAuthConfig.setName("TOTPAuthenticator"); + totpAuthConfig.setEnabled(true); + when(totpApplicationAuthenticator.getName()).thenReturn("TOTPAuthenticator"); + when(totpApplicationAuthenticator.getFriendlyName()).thenReturn("totp"); + totpAuthConfig.setApplicationAuthenticator(totpApplicationAuthenticator); + totpAuthConfig.getIdps().put("local", localIdp); + + AuthenticatorConfig twitterAuthConfig = new AuthenticatorConfig(); + twitterAuthConfig.setName("TwitterAuthenticator"); + twitterAuthConfig.setEnabled(true); + when(federatedApplicationAuthenticator.getName()).thenReturn("TwitterAuthenticator"); + when(federatedApplicationAuthenticator.getFriendlyName()).thenReturn("twitter"); + twitterAuthConfig.setApplicationAuthenticator(federatedApplicationAuthenticator); + twitterAuthConfig.getIdps().put("customIdp2", customIdp2); + + StepConfig stepWithSingleOption = new StepConfig(); + stepWithSingleOption.setAuthenticatorList(Collections.singletonList(basicAuthConfig)); + + Map singleParamConfig = new HashMap<>(); + Map params = new HashMap<>(); + params.put("BasicAuthenticator", Collections.singletonMap("foo", "xyz")); + singleParamConfig.put("local", params); + + StepConfig stepWithMultipleOptions = new StepConfig(); + stepWithMultipleOptions.setAuthenticatorList( + new ArrayList<>(Arrays.asList(basicAuthConfig, totpAuthConfig, twitterAuthConfig))); + + Map localParams = new HashMap<>(); + localParams.put("BasicAuthenticator", Collections.singletonMap("foo", "xyz")); + localParams.put("TOTPAuthenticator", Collections.singletonMap("domain", "localhost")); + + Map federatedParams = new HashMap<>(); + federatedParams.put("customIdp2", Collections.singletonMap("foo", "user")); + + Map multiParamConfig = new HashMap<>(); + multiParamConfig.put("local", localParams); + multiParamConfig.put("federated", federatedParams); + + return new Object[][]{ + {singleParamConfig, duplicateStepConfig(stepWithSingleOption), "BasicAuthenticator", "foo", "xyz"}, + {singleParamConfig, duplicateStepConfig(stepWithSingleOption), "BasicAuthenticator", "foos", null}, + {singleParamConfig, duplicateStepConfig(stepWithMultipleOptions), "BasicAuthenticator", "foo", "xyz"}, + {multiParamConfig, duplicateStepConfig(stepWithMultipleOptions), "BasicAuthenticator", "domain", null}, + {multiParamConfig, duplicateStepConfig(stepWithMultipleOptions), "TwitterAuthenticator", "foo", "user"}, + {multiParamConfig, duplicateStepConfig(stepWithMultipleOptions), "TOTPAuthenticator", "domain", + "localhost"}}; + } + +} diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/test/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/JsGraphBuilderTest.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/test/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/JsNashornGraphBuilderTest.java similarity index 99% rename from components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/test/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/JsGraphBuilderTest.java rename to components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/test/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/JsNashornGraphBuilderTest.java index 61ab1ec27aab..42bc89340e8e 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/test/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/JsGraphBuilderTest.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/test/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/JsNashornGraphBuilderTest.java @@ -53,7 +53,7 @@ * Tests for graph builder with Javascript. */ @Test -public class JsGraphBuilderTest extends AbstractFrameworkTest { +public class JsNashornGraphBuilderTest extends AbstractFrameworkTest { private JsGraphBuilderFactory jsGraphBuilderFactory; diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/test/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/JsGraalAuthenticationContextTest.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/test/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/JsGraalAuthenticationContextTest.java new file mode 100644 index 000000000000..5f783b844b3e --- /dev/null +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/test/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/JsGraalAuthenticationContextTest.java @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js; + +import org.graalvm.polyglot.Context; +import org.graalvm.polyglot.HostAccess; +import org.graalvm.polyglot.Source; +import org.graalvm.polyglot.Value; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; +import org.wso2.carbon.identity.application.authentication.framework.config.model.AuthenticatorConfig; +import org.wso2.carbon.identity.application.authentication.framework.config.model.SequenceConfig; +import org.wso2.carbon.identity.application.authentication.framework.config.model.StepConfig; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.AuthenticationGraph; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.JsWrapperFactoryProvider; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.graaljs.JsGraalWrapperFactory; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.graaljs.JsGraalAuthenticationContext; +import org.wso2.carbon.identity.application.authentication.framework.context.AuthenticationContext; +import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedIdPData; +import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser; +import org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants; +import org.wso2.carbon.identity.application.common.model.ClaimMapping; + +import java.io.IOException; +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.Map; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; +import static org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants.JSAttributes.POLYGLOT_LANGUAGE; +import static org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants.JSAttributes.POLYGLOT_SOURCE; + +public class JsGraalAuthenticationContextTest { + + public static final String TEST_IDP = "testIdP"; + private static final String LAST_ATTEMPTED_USER_USERNAME = "lastAttemptedUsername"; + private static final String LAST_ATTEMPTED_USER_TENANT_DOMAIN = "lastAttemptedTenantDomain"; + private static final String LAST_ATTEMPTED_USER_USERSTORE_DOMAIN = "lastAttemptedUserstoreDomain"; + private static final String SERVICE_PROVIDER_NAME = "service_provider_js_test"; + private static final String BASIC_AUTHENTICATOR = "BasicAuthenticator"; + + private Context context; + + @BeforeClass + public void setUp() throws NoSuchFieldException, IllegalAccessException { + + context = Context.newBuilder(POLYGLOT_LANGUAGE) + .allowHostAccess(HostAccess.EXPLICIT) + .option("engine.WarnInterpreterOnly", "false") + .build(); + + Field wrapperFactory = JsWrapperFactoryProvider.class.getDeclaredField("jsWrapperBaseFactory"); + wrapperFactory.setAccessible(true); + wrapperFactory.set(JsWrapperFactoryProvider.getInstance(), new JsGraalWrapperFactory()); + } + + @Test + public void testClaimAssignment() throws IOException { + + ClaimMapping claimMapping1 = ClaimMapping.build("", "", "", false); + + ClaimMapping claimMapping2 = + ClaimMapping.build("Test.Remote.Claim.Url.2", "Test.Remote.Claim.Url.2", "", false); + + AuthenticatedUser authenticatedUser = new AuthenticatedUser(); + authenticatedUser.getUserAttributes().put(claimMapping1, "TestClaimVal1"); + authenticatedUser.getUserAttributes().put(claimMapping2, "TestClaimVal2"); + AuthenticationContext authenticationContext = new AuthenticationContext(); + setupAuthContextWithStepData(authenticationContext, authenticatedUser); + + JsGraalAuthenticationContext jsAuthenticationContext = new JsGraalAuthenticationContext(authenticationContext); + Value bindings = context.getBindings(POLYGLOT_LANGUAGE); + bindings.putMember("context", jsAuthenticationContext); + + Value result = context.eval( + Source.newBuilder(POLYGLOT_LANGUAGE, + "context.steps[1].subject.remoteClaims['Test.Remote.Claim.Url.1']", + POLYGLOT_SOURCE).build()); + assertTrue(result.isNull()); + + result = context.eval( + Source.newBuilder(POLYGLOT_LANGUAGE, + "context.steps[1].subject.remoteClaims['Test.Remote.Claim.Url.2']", + POLYGLOT_SOURCE).build()); + assertEquals(result.asString(), "TestClaimVal2"); + + context.eval( + Source.newBuilder(POLYGLOT_LANGUAGE, + "context.steps[1].subject.remoteClaims['Test.Remote.Claim.Url.2'] = 'Modified2'", + POLYGLOT_SOURCE).build()); + result = context.eval( + Source.newBuilder(POLYGLOT_LANGUAGE, + "context.steps[1].subject.remoteClaims['Test.Remote.Claim.Url.2']", + POLYGLOT_SOURCE).build()); + assertEquals(result.asString(), "Modified2"); + } + + private void setupAuthContextWithStepData(AuthenticationContext context, AuthenticatedUser authenticatedUser) { + + AuthenticatorConfig basicAuthenticatorConfig = new AuthenticatorConfig(); + basicAuthenticatorConfig.setName(BASIC_AUTHENTICATOR); + basicAuthenticatorConfig.setEnabled(true); + SequenceConfig sequenceConfig = new SequenceConfig(); + Map stepConfigMap = new HashMap<>(); + StepConfig stepConfig = new StepConfig(); + stepConfig.setOrder(1); + stepConfig.setAuthenticatedIdP(TEST_IDP); + stepConfig.setAuthenticatedAutenticator(basicAuthenticatorConfig); + stepConfigMap.put(1, stepConfig); + sequenceConfig.setStepMap(stepConfigMap); + AuthenticationGraph authenticationGraph = new AuthenticationGraph(); + authenticationGraph.setStepMap(stepConfigMap); + sequenceConfig.setAuthenticationGraph(authenticationGraph); + context.setSequenceConfig(sequenceConfig); + Map idPDataMap = new HashMap<>(); + AuthenticatedIdPData idPData = new AuthenticatedIdPData(); + idPData.setUser(authenticatedUser); + idPData.setIdpName(TEST_IDP); + idPDataMap.put(TEST_IDP, idPData); + context.setCurrentAuthenticatedIdPs(idPDataMap); + } + + @Test + public void testRemoteAddition() throws IOException { + + AuthenticatedUser authenticatedUser = new AuthenticatedUser(); + AuthenticationContext authenticationContext = new AuthenticationContext(); + setupAuthContextWithStepData(authenticationContext, authenticatedUser); + + JsGraalAuthenticationContext jsAuthenticationContext = new JsGraalAuthenticationContext(authenticationContext); + Value bindings = context.getBindings(POLYGLOT_LANGUAGE); + bindings.putMember("context", jsAuthenticationContext); + + context.eval( + Source.newBuilder(POLYGLOT_LANGUAGE, "context.steps[1].subject.remoteClaims['testClaim']='testValue'", + POLYGLOT_SOURCE).build()); + + ClaimMapping claimMapping = ClaimMapping.build("testClaim", "testClaim", "", false); + String claimCreatedByJs = authenticatedUser.getUserAttributes().get(claimMapping); + assertEquals(claimCreatedByJs, "testValue"); + } + + @Test + public void testGetServiceProviderFromWrappedContext() throws Exception { + + AuthenticationContext authenticationContext = new AuthenticationContext(); + authenticationContext.setServiceProviderName(SERVICE_PROVIDER_NAME); + + JsGraalAuthenticationContext jsAuthenticationContext = new JsGraalAuthenticationContext(authenticationContext); + Value bindings = context.getBindings(POLYGLOT_LANGUAGE); + bindings.putMember("context", jsAuthenticationContext); + + Value result = context.eval( + Source.newBuilder(POLYGLOT_LANGUAGE, "context.serviceProviderName", POLYGLOT_SOURCE).build()); + assertFalse(result.isNull()); + assertEquals(result.asString(), SERVICE_PROVIDER_NAME, "Service Provider name set in " + + "AuthenticationContext is not accessible from JSAuthenticationContext"); + } + + @Test + public void testGetLastLoginFailedUserFromWrappedContext() throws Exception { + + AuthenticatedUser lastAttemptedUser = new AuthenticatedUser(); + lastAttemptedUser.setUserName(LAST_ATTEMPTED_USER_USERNAME); + lastAttemptedUser.setTenantDomain(LAST_ATTEMPTED_USER_TENANT_DOMAIN); + lastAttemptedUser.setUserStoreDomain(LAST_ATTEMPTED_USER_USERSTORE_DOMAIN); + + AuthenticationContext authenticationContext = new AuthenticationContext(); + authenticationContext.setProperty(FrameworkConstants.JSAttributes.JS_LAST_LOGIN_FAILED_USER, lastAttemptedUser); + + JsGraalAuthenticationContext jsAuthenticationContext = new JsGraalAuthenticationContext(authenticationContext); + Value bindings = context.getBindings(POLYGLOT_LANGUAGE); + bindings.putMember("context", jsAuthenticationContext); + + Value result = context.eval( + Source.newBuilder(POLYGLOT_LANGUAGE, "context.lastLoginFailedUser", POLYGLOT_SOURCE).build()); + assertFalse(result.isNull()); + assertTrue(result.asProxyObject() instanceof JsAuthenticatedUser); + + Value username = context.eval( + Source.newBuilder(POLYGLOT_LANGUAGE, "context.lastLoginFailedUser.username", POLYGLOT_SOURCE).build()); + assertEquals(username.asString(), LAST_ATTEMPTED_USER_USERNAME); + + Value tenantDomain = context.eval( + Source.newBuilder(POLYGLOT_LANGUAGE, "context.lastLoginFailedUser.tenantDomain", POLYGLOT_SOURCE) + .build()); + assertEquals(tenantDomain.asString(), LAST_ATTEMPTED_USER_TENANT_DOMAIN); + + Value userStoreDomain = context.eval( + Source.newBuilder(POLYGLOT_LANGUAGE, "context.lastLoginFailedUser.userStoreDomain", POLYGLOT_SOURCE) + .build()); + assertEquals(userStoreDomain.asString(), LAST_ATTEMPTED_USER_USERSTORE_DOMAIN.toUpperCase()); + } + + @Test + public void testGetLastLoginFailedUserNullFromWrappedContext() throws Exception { + + AuthenticationContext authenticationContext = new AuthenticationContext(); + authenticationContext.setProperty(FrameworkConstants.JSAttributes.JS_LAST_LOGIN_FAILED_USER, null); + + JsGraalAuthenticationContext jsAuthenticationContext = new JsGraalAuthenticationContext(authenticationContext); + Value bindings = context.getBindings(POLYGLOT_LANGUAGE); + bindings.putMember("context", jsAuthenticationContext); + + Value result = context.eval( + Source.newBuilder(POLYGLOT_LANGUAGE, "context.lastLoginFailedUser", POLYGLOT_SOURCE).build()); + assertTrue(result.isNull()); + } + + @Test + public void testGetLastLoginAuthenticatorFromStep() throws Exception { + + AuthenticationContext authenticationContext = new AuthenticationContext(); + AuthenticatedUser authenticatedUser = new AuthenticatedUser(); + + setupAuthContextWithStepData(authenticationContext, authenticatedUser); + + JsGraalAuthenticationContext jsAuthenticationContext = new JsGraalAuthenticationContext(authenticationContext); + Value bindings = context.getBindings(POLYGLOT_LANGUAGE); + bindings.putMember("context", jsAuthenticationContext); + + Value result = context.eval( + Source.newBuilder(POLYGLOT_LANGUAGE, "context.steps[1].authenticator", POLYGLOT_SOURCE).build()); + + assertFalse(result.isNull()); + assertEquals(result.asString(), BASIC_AUTHENTICATOR, + "Authenticator of the step in AuthenticationContext is not accessible from JSAuthenticationContext"); + } +} diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/test/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/JsAuthenticationContextTest.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/test/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/JsNashornAuthenticationContextTest.java similarity index 94% rename from components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/test/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/JsAuthenticationContextTest.java rename to components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/test/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/JsNashornAuthenticationContextTest.java index 341159f10c98..0788d9051b7d 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/test/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/JsAuthenticationContextTest.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/test/java/org/wso2/carbon/identity/application/authentication/framework/config/model/graph/js/JsNashornAuthenticationContextTest.java @@ -24,6 +24,8 @@ import org.wso2.carbon.identity.application.authentication.framework.config.model.SequenceConfig; import org.wso2.carbon.identity.application.authentication.framework.config.model.StepConfig; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.AuthenticationGraph; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.JsWrapperFactory; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.JsWrapperFactoryProvider; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.nashorn.JsNashornAuthenticatedUser; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.nashorn.JsNashornAuthenticationContext; import org.wso2.carbon.identity.application.authentication.framework.context.AuthenticationContext; @@ -32,6 +34,7 @@ import org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants; import org.wso2.carbon.identity.application.common.model.ClaimMapping; +import java.lang.reflect.Field; import java.util.HashMap; import java.util.Map; @@ -47,7 +50,7 @@ import static org.testng.Assert.assertTrue; @Test -public class JsAuthenticationContextTest { +public class JsNashornAuthenticationContextTest { public static final String TEST_IDP = "testIdP"; private static final String LAST_ATTEMPTED_USER_USERNAME = "lastAttemptedUsername"; @@ -59,9 +62,12 @@ public class JsAuthenticationContextTest { private ScriptEngine scriptEngine; @BeforeClass - public void setUp() { + public void setUp() throws NoSuchFieldException, IllegalAccessException { scriptEngine = new ScriptEngineManager().getEngineByName("nashorn"); + Field wrapperFactory = JsWrapperFactoryProvider.class.getDeclaredField("jsWrapperBaseFactory"); + wrapperFactory.setAccessible(true); + wrapperFactory.set(JsWrapperFactoryProvider.getInstance(), new JsWrapperFactory()); } @Test diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/test/java/org/wso2/carbon/identity/application/authentication/framework/handler/sequence/impl/GraphBasedSequenceHandlerAbstractTest.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/test/java/org/wso2/carbon/identity/application/authentication/framework/handler/sequence/impl/GraphBasedSequenceHandlerAbstractTest.java index affc44a3532a..5bd267a411f4 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/test/java/org/wso2/carbon/identity/application/authentication/framework/handler/sequence/impl/GraphBasedSequenceHandlerAbstractTest.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/test/java/org/wso2/carbon/identity/application/authentication/framework/handler/sequence/impl/GraphBasedSequenceHandlerAbstractTest.java @@ -20,19 +20,30 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.testng.annotations.AfterTest; import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Parameters; +import org.wso2.carbon.CarbonConstants; import org.wso2.carbon.base.MultitenantConstants; import org.wso2.carbon.identity.application.authentication.framework.AbstractFrameworkTest; import org.wso2.carbon.identity.application.authentication.framework.MockAuthenticator; import org.wso2.carbon.identity.application.authentication.framework.config.builder.FileBasedConfigurationBuilder; import org.wso2.carbon.identity.application.authentication.framework.config.loader.UIBasedConfigurationLoader; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.JSExecutionSupervisor; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.JsFunctionRegistryImpl; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.JsGenericGraphBuilderFactory; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.JsGraphBuilderFactory; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.JsWrapperFactory; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.JsWrapperFactoryProvider; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.graaljs.JsGraalGraphBuilderFactory; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.graaljs.JsGraalWrapperFactory; import org.wso2.carbon.identity.application.authentication.framework.context.AuthenticationContext; import org.wso2.carbon.identity.application.authentication.framework.handler.SubjectCallback; import org.wso2.carbon.identity.application.authentication.framework.internal.FrameworkServiceDataHolder; import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser; +import org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants; import org.wso2.carbon.identity.application.common.model.ClaimMapping; import org.wso2.carbon.idp.mgt.IdentityProviderManager; import org.wso2.carbon.idp.mgt.dao.CacheBackedIdPMgtDAO; @@ -56,6 +67,7 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; +import static org.mockito.MockitoAnnotations.initMocks; public class GraphBasedSequenceHandlerAbstractTest extends AbstractFrameworkTest { @@ -68,19 +80,48 @@ public class GraphBasedSequenceHandlerAbstractTest extends AbstractFrameworkTest protected static final String TEST_USER_1_ID = "4b4414e1-916b-4475-aaee-6b0751c29ff6"; protected GraphBasedSequenceHandler graphBasedSequenceHandler = new GraphBasedSequenceHandler(); protected UIBasedConfigurationLoader configurationLoader; - protected JsGraphBuilderFactory graphBuilderFactory; + protected JsGenericGraphBuilderFactory graphBuilderFactory; + + @BeforeTest + public void setUpExecutionSupervisor() { + + initMocks(this); + JSExecutionSupervisor jsExecutionSupervisor = new JSExecutionSupervisor(1, 5000L); + FrameworkServiceDataHolder.getInstance().setJsExecutionSupervisor(jsExecutionSupervisor); + } + + @AfterTest + public void tearDownExecutionSupervisor() { + + FrameworkServiceDataHolder.getInstance().getJsExecutionSupervisor().shutdown(); + } @BeforeClass - protected void setupSuite() { + @Parameters({"scriptEngine"}) + protected void setupSuite(String scriptEngine) throws NoSuchFieldException, IllegalAccessException { configurationLoader = new UIBasedConfigurationLoader(); - graphBuilderFactory = new JsGraphBuilderFactory(); + CarbonConstants.ENABLE_LEGACY_AUTHZ_RUNTIME = false; + + if (scriptEngine.contentEquals(FrameworkConstants.JSAttributes.NASHORN)) { + graphBuilderFactory = new JsGraphBuilderFactory(); + } else if (scriptEngine.contentEquals(FrameworkConstants.JSAttributes.GRAALJS)) { + graphBuilderFactory = new JsGraalGraphBuilderFactory(); + } + + Field wrapperFactory = JsWrapperFactoryProvider.class.getDeclaredField("jsWrapperBaseFactory"); + wrapperFactory.setAccessible(true); + if (graphBuilderFactory instanceof JsGraphBuilderFactory) { + wrapperFactory.set(JsWrapperFactoryProvider.getInstance(), new JsWrapperFactory()); + } else if (graphBuilderFactory instanceof JsGraalGraphBuilderFactory) { + wrapperFactory.set(JsWrapperFactoryProvider.getInstance(), new JsGraalWrapperFactory()); + } JsFunctionRegistryImpl jsFunctionRegistry = new JsFunctionRegistryImpl(); FrameworkServiceDataHolder.getInstance().setJsFunctionRegistry(jsFunctionRegistry); graphBuilderFactory.init(); - FrameworkServiceDataHolder.getInstance().setJsGraphBuilderFactory(graphBuilderFactory); + FrameworkServiceDataHolder.getInstance().setJsGenericGraphBuilderFactory(graphBuilderFactory); AsyncSequenceExecutor asyncSequenceExecutor = new AsyncSequenceExecutor(); asyncSequenceExecutor.init(); diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/test/java/org/wso2/carbon/identity/application/authentication/framework/handler/sequence/impl/GraphBasedSequenceHandlerCustomFunctionsTest.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/test/java/org/wso2/carbon/identity/application/authentication/framework/handler/sequence/impl/GraphBasedSequenceHandlerCustomFunctionsTest.java index e90bdcfbd236..e0a172e8eef5 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/test/java/org/wso2/carbon/identity/application/authentication/framework/handler/sequence/impl/GraphBasedSequenceHandlerCustomFunctionsTest.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/test/java/org/wso2/carbon/identity/application/authentication/framework/handler/sequence/impl/GraphBasedSequenceHandlerCustomFunctionsTest.java @@ -19,6 +19,7 @@ package org.wso2.carbon.identity.application.authentication.framework.handler.sequence.impl; import org.apache.commons.lang3.SerializationUtils; +import org.graalvm.polyglot.HostAccess; import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; @@ -32,7 +33,7 @@ import org.wso2.carbon.identity.application.authentication.framework.MockAuthenticator; import org.wso2.carbon.identity.application.authentication.framework.config.model.SequenceConfig; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.JsFunctionRegistryImpl; -import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.nashorn.JsNashornAuthenticationContext; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.base.JsBaseAuthenticationContext; import org.wso2.carbon.identity.application.authentication.framework.context.AuthHistory; import org.wso2.carbon.identity.application.authentication.framework.context.AuthenticationContext; import org.wso2.carbon.identity.application.authentication.framework.exception.AuthenticationFailedException; @@ -91,17 +92,17 @@ public void tearDown() { CentralLogMgtServiceComponentHolder.getInstance().setIdentityEventService(null); } - public static String customFunction1(JsNashornAuthenticationContext context) { + public static String customFunction1(JsBaseAuthenticationContext context) { return "testResult1"; } - public static Boolean customBoolean(JsNashornAuthenticationContext context) { + public static Boolean customBoolean(JsBaseAuthenticationContext context) { return true; } - public static Boolean customBoolean2(JsNashornAuthenticationContext context, String value) { + public static Boolean customBoolean2(JsBaseAuthenticationContext context, String value) { return true; } @@ -113,7 +114,7 @@ public void testHandleDynamicJavascript1() throws Exception { JsFunctionRegistryImpl jsFunctionRegistrar = new JsFunctionRegistryImpl(); FrameworkServiceDataHolder.getInstance().setJsFunctionRegistry(jsFunctionRegistrar); jsFunctionRegistrar.register(JsFunctionRegistry.Subsystem.SEQUENCE_HANDLER, "fn1", - (Function) GraphBasedSequenceHandlerCustomFunctionsTest + (Function) GraphBasedSequenceHandlerCustomFunctionsTest ::customFunction1); jsFunctionRegistrar.register(JsFunctionRegistry.Subsystem.SEQUENCE_HANDLER, "fn2", new CustomFunctionImpl2()); @@ -136,16 +137,13 @@ public void testHandleDynamicBoolean() throws Exception { JsFunctionRegistry jsFunctionRegistrar = new JsFunctionRegistryImpl(); FrameworkServiceDataHolder.getInstance().setJsFunctionRegistry(jsFunctionRegistrar); jsFunctionRegistrar.register(JsFunctionRegistry.Subsystem.SEQUENCE_HANDLER, "fn1", - (Function) GraphBasedSequenceHandlerCustomFunctionsTest + (Function) GraphBasedSequenceHandlerCustomFunctionsTest ::customFunction1); jsFunctionRegistrar.register(JsFunctionRegistry.Subsystem.SEQUENCE_HANDLER, "getTrueFunction", - (Function) GraphBasedSequenceHandlerCustomFunctionsTest + (Function) GraphBasedSequenceHandlerCustomFunctionsTest ::customBoolean); - - jsFunctionRegistrar.register(JsFunctionRegistry.Subsystem.SEQUENCE_HANDLER, "getTrueFunction2", - (BiFunction) - GraphBasedSequenceHandlerCustomFunctionsTest - ::customBoolean2); + CustomBoolean2Impl customBoolean2 = new CustomBoolean2Impl(); + jsFunctionRegistrar.register(JsFunctionRegistry.Subsystem.SEQUENCE_HANDLER, "getTrueFunction2", customBoolean2); ServiceProvider sp1 = getTestServiceProvider("js-sp-dynamic-1.xml"); @@ -188,14 +186,14 @@ public void testHandleDynamicOnFail() throws Exception { JsFunctionRegistryImpl jsFunctionRegistrar = new JsFunctionRegistryImpl(); FrameworkServiceDataHolder.getInstance().setJsFunctionRegistry(jsFunctionRegistrar); jsFunctionRegistrar.register(JsFunctionRegistry.Subsystem.SEQUENCE_HANDLER, "fn1", - (Function) GraphBasedSequenceHandlerCustomFunctionsTest + (Function) GraphBasedSequenceHandlerCustomFunctionsTest ::customFunction1); jsFunctionRegistrar.register(JsFunctionRegistry.Subsystem.SEQUENCE_HANDLER, "getTrueFunction", - (Function) GraphBasedSequenceHandlerCustomFunctionsTest + (Function) GraphBasedSequenceHandlerCustomFunctionsTest ::customBoolean); jsFunctionRegistrar.register(JsFunctionRegistry.Subsystem.SEQUENCE_HANDLER, "getTrueFunction2", - (BiFunction) + (BiFunction) GraphBasedSequenceHandlerCustomFunctionsTest ::customBoolean2); @@ -222,14 +220,14 @@ public void testHandleDynamicOnFallback() throws Exception { JsFunctionRegistryImpl jsFunctionRegistrar = new JsFunctionRegistryImpl(); FrameworkServiceDataHolder.getInstance().setJsFunctionRegistry(jsFunctionRegistrar); jsFunctionRegistrar.register(JsFunctionRegistry.Subsystem.SEQUENCE_HANDLER, "fn1", - (Function) GraphBasedSequenceHandlerCustomFunctionsTest + (Function) GraphBasedSequenceHandlerCustomFunctionsTest ::customFunction1); jsFunctionRegistrar.register(JsFunctionRegistry.Subsystem.SEQUENCE_HANDLER, "getTrueFunction", - (Function) GraphBasedSequenceHandlerCustomFunctionsTest + (Function) GraphBasedSequenceHandlerCustomFunctionsTest ::customBoolean); jsFunctionRegistrar.register(JsFunctionRegistry.Subsystem.SEQUENCE_HANDLER, "getTrueFunction2", - (BiFunction) + (BiFunction) GraphBasedSequenceHandlerCustomFunctionsTest ::customBoolean2); @@ -254,7 +252,7 @@ public void testHandleDynamicJavascriptSerialization() throws Exception { JsFunctionRegistry jsFunctionRegistrar = new JsFunctionRegistryImpl(); FrameworkServiceDataHolder.getInstance().setJsFunctionRegistry(jsFunctionRegistrar); jsFunctionRegistrar.register(JsFunctionRegistry.Subsystem.SEQUENCE_HANDLER, "fn1", - (Function) GraphBasedSequenceHandlerCustomFunctionsTest + (Function) GraphBasedSequenceHandlerCustomFunctionsTest ::customFunction1); ServiceProvider sp1 = getTestServiceProvider("js-sp-dynamic-1.xml"); @@ -297,6 +295,7 @@ private AuthenticationContext processSequenceWithAcr(String[] acrArray) return processAndGetAuthenticationContext(acrArray, sp1); } + // private AuthenticationContext processAndGetAuthenticationContext(String[] acrArray, ServiceProvider sp1) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, FrameworkException { @@ -349,10 +348,16 @@ public Object answer(InvocationOnMock invocation) throws Throwable { }).when(request).getAttribute(Mockito.anyString()); } + @FunctionalInterface + public interface CustomBoolean2Interface extends Serializable { + + boolean getTrueFunction2(JsBaseAuthenticationContext context, String param1); + } + @FunctionalInterface public interface CustomFunctionInterface2 extends Serializable { - String customFunction2(JsNashornAuthenticationContext context, String param1, String param2); + String customFunction2(JsBaseAuthenticationContext context, String param1, String param2); } public static class MockFailingAuthenticator extends MockAuthenticator { @@ -389,9 +394,18 @@ public AuthenticatorFlowStatus process(HttpServletRequest request, HttpServletRe public class CustomFunctionImpl2 implements CustomFunctionInterface2 { - public String customFunction2(JsNashornAuthenticationContext context, String param1, String param2) { + public String customFunction2(JsBaseAuthenticationContext context, String param1, String param2) { return "testResult2"; } } + + public class CustomBoolean2Impl implements CustomBoolean2Interface { + + @HostAccess.Export + public boolean getTrueFunction2(JsBaseAuthenticationContext context, String param1) { + + return true; + } + } } diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/test/java/org/wso2/carbon/identity/application/authentication/framework/handler/sequence/impl/GraphBasedSequenceHandlerLongWaitTest.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/test/java/org/wso2/carbon/identity/application/authentication/framework/handler/sequence/impl/GraphBasedSequenceHandlerLongWaitTest.java index 3d2b135c0a28..5653aa44b25f 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/test/java/org/wso2/carbon/identity/application/authentication/framework/handler/sequence/impl/GraphBasedSequenceHandlerLongWaitTest.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/test/java/org/wso2/carbon/identity/application/authentication/framework/handler/sequence/impl/GraphBasedSequenceHandlerLongWaitTest.java @@ -18,6 +18,7 @@ package org.wso2.carbon.identity.application.authentication.framework.handler.sequence.impl; +import org.graalvm.polyglot.HostAccess; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; @@ -27,7 +28,7 @@ import org.wso2.carbon.identity.application.authentication.framework.JsFunctionRegistry; import org.wso2.carbon.identity.application.authentication.framework.config.model.SequenceConfig; import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.JsFunctionRegistryImpl; -import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.JsNashornGraphBuilder; +import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.JsGraphBuilder; import org.wso2.carbon.identity.application.authentication.framework.context.AuthenticationContext; import org.wso2.carbon.identity.application.authentication.framework.dao.impl.CacheBackedLongWaitStatusDAO; import org.wso2.carbon.identity.application.authentication.framework.dao.impl.LongWaitStatusDAOImpl; @@ -117,13 +118,14 @@ void publishEvent(String siddhiAppName, String inStreamName, String outStreamNam public static class AsyncAnalyticsCbFunctionImpl implements Fn1 { + @HostAccess.Export public void publishEvent(String siddhiAppName, String inStreamName, String outStreamName, Map payloadData, Map eventHandlers) { AsyncProcess asyncProcess = new AsyncProcess((ctx, r) -> { r.accept(ctx, Collections.emptyMap(), "onSuccess"); }); - JsNashornGraphBuilder.addLongWaitProcess(asyncProcess, eventHandlers); + JsGraphBuilder.addLongWaitProcess(asyncProcess, eventHandlers); } } } diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/test/resources/testng.xml b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/test/resources/testng.xml index 581212c1f10b..6a92baeb22da 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/test/resources/testng.xml +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/test/resources/testng.xml @@ -33,15 +33,6 @@ - - - - - - - - - @@ -56,8 +47,11 @@ - - + + + + + @@ -69,6 +63,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/features/authentication-framework/org.wso2.carbon.identity.application.authentication.framework.server.feature/pom.xml b/features/authentication-framework/org.wso2.carbon.identity.application.authentication.framework.server.feature/pom.xml index 90ae2fcdd83d..9898b333ae09 100644 --- a/features/authentication-framework/org.wso2.carbon.identity.application.authentication.framework.server.feature/pom.xml +++ b/features/authentication-framework/org.wso2.carbon.identity.application.authentication.framework.server.feature/pom.xml @@ -42,6 +42,27 @@ org.wso2.carbon.identity.framework org.wso2.carbon.identity.application.authentication.endpoint.util + + + org.graalvm.sdk + graal-sdk + + + org.graalvm.js + js + + + org.graalvm.truffle + truffle-api + + + com.ibm.icu + icu4j + + + org.graalvm.regex + regex + @@ -67,6 +88,46 @@ true ${basedir}/src/main/resources/runtimes/cxf3 + + org.graalvm.sdk + graal-sdk + ${graalvm.version} + jar + true + ${basedir}/src/main/resources/graal + + + org.graalvm.js + js + ${graalvm.version} + jar + true + ${basedir}/src/main/resources/graal + + + org.graalvm.truffle + truffle-api + ${graalvm.version} + jar + true + ${basedir}/src/main/resources/graal + + + com.ibm.icu + icu4j + ${icu.version} + jar + true + ${basedir}/src/main/resources/graal + + + org.graalvm.regex + regex + ${graalvm.version} + jar + true + ${basedir}/src/main/resources/graal + diff --git a/features/authentication-framework/org.wso2.carbon.identity.application.authentication.framework.server.feature/resources/p2.inf b/features/authentication-framework/org.wso2.carbon.identity.application.authentication.framework.server.feature/resources/p2.inf index 8635ecc75825..f24ac13d5940 100644 --- a/features/authentication-framework/org.wso2.carbon.identity.application.authentication.framework.server.feature/resources/p2.inf +++ b/features/authentication-framework/org.wso2.carbon.identity.application.authentication.framework.server.feature/resources/p2.inf @@ -21,4 +21,5 @@ org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../featur org.eclipse.equinox.p2.touchpoint.natives.mkdir(path:${installFolder}/../../../lib/); \ org.eclipse.equinox.p2.touchpoint.natives.mkdir(path:${installFolder}/../../../lib/runtimes/); \ org.eclipse.equinox.p2.touchpoint.natives.mkdir(path:${installFolder}/../../../lib/runtimes/cxf3/); \ -org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.identity.application.authentication.framework.server_${feature.version}/runtimes/cxf3/,target:${installFolder}/../../../lib/runtimes/cxf3/,overwrite:true);\ \ No newline at end of file +org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.identity.application.authentication.framework.server_${feature.version}/runtimes/cxf3/,target:${installFolder}/../../../lib/runtimes/cxf3/,overwrite:true);\ +org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.identity.application.authentication.framework.server_${feature.version}/graal/,target:${installFolder}/../../../lib/,overwrite:true);\ diff --git a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/identity.xml.j2 b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/identity.xml.j2 index 79d8ba00aaf0..2a07dfdd38f1 100644 --- a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/identity.xml.j2 +++ b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/identity.xml.j2 @@ -3621,6 +3621,11 @@ {{authentication.adaptive.authenticator_name_in_auth_config.enable}} + + + + {{authentication.adaptive.graaljs.script_statements_limit}} + diff --git a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/org.wso2.carbon.identity.core.server.feature.default.json b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/org.wso2.carbon.identity.core.server.feature.default.json index dc7c9f7ec688..30c6a1c1e480 100644 --- a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/org.wso2.carbon.identity.core.server.feature.default.json +++ b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/org.wso2.carbon.identity.core.server.feature.default.json @@ -769,6 +769,7 @@ "authentication.adaptive.execution_supervisor.thread_count": "1", "authentication.adaptive.execution_supervisor.timeout": "500ms", "authentication.adaptive.authenticator_name_in_auth_config.enable": true, + "authentication.adaptive.graaljs.script_statements_limit": "0", "federated.idp.role_claim_value_attribute_separator": ",", "configuration.store.query_length.max": "4194304", diff --git a/pom.xml b/pom.xml index b50267d77dd4..dd94ed5c5867 100644 --- a/pom.xml +++ b/pom.xml @@ -1782,6 +1782,32 @@ jackson-dataformat-cbor ${com.fasterxml.jackson.cbor.version} + + + org.graalvm.sdk + graal-sdk + ${graalvm.version} + + + org.graalvm.js + js + ${graalvm.version} + + + org.graalvm.truffle + truffle-api + ${graalvm.version} + + + org.graalvm.regex + regex + ${graalvm.version} + + + com.ibm.icu + icu4j + ${icu.version} + @@ -2086,6 +2112,11 @@ 2.2.224 + 22.3.4 + [22.3.0,23.0.0) + + 71.1 + 3.1.0 4.8.4.0