diff --git a/agama/engine/src/main/java/io/jans/agama/engine/service/ActionService.java b/agama/engine/src/main/java/io/jans/agama/engine/service/ActionService.java index c1886a4069a..dbcba4926a2 100644 --- a/agama/engine/src/main/java/io/jans/agama/engine/service/ActionService.java +++ b/agama/engine/src/main/java/io/jans/agama/engine/service/ActionService.java @@ -174,7 +174,7 @@ private Object[] getArgsForCall(Executable javaExec, int arity, Object[] argumen logger.trace("Parameter is a primitive (or wrapped) {}", typeName); javaArgs[i] = arg; - } else if (argClass.isAssignableFrom(Number.class)) { + } else if (Number.class.isAssignableFrom(argClass)) { Object number = PrimitiveUtils.primitiveNumberFrom((Number) arg, paramType); if (number != null) { diff --git a/agama/engine/src/test/java/io/jans/agama/test/BaseTest.java b/agama/engine/src/test/java/io/jans/agama/test/BaseTest.java new file mode 100644 index 00000000000..e3fb8e1cb1c --- /dev/null +++ b/agama/engine/src/test/java/io/jans/agama/test/BaseTest.java @@ -0,0 +1,183 @@ +package io.jans.agama.test; + +import com.gargoylesoftware.htmlunit.BrowserVersion; +import com.gargoylesoftware.htmlunit.Page; +import com.gargoylesoftware.htmlunit.WebClient; +import com.gargoylesoftware.htmlunit.WebClientOptions; +import com.gargoylesoftware.htmlunit.WebResponse; +import com.gargoylesoftware.htmlunit.html.DomElement; +import com.gargoylesoftware.htmlunit.html.HtmlForm; +import com.gargoylesoftware.htmlunit.html.HtmlPage; + +import io.jans.inbound.oauth2.CodeGrantUtil; +import io.jans.inbound.oauth2.OAuthParams; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Collections; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.net.URISyntaxException; +import java.net.URLEncoder; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.json.JSONObject; +import org.testng.ITestContext; +import org.testng.annotations.BeforeSuite; + +import static java.nio.charset.StandardCharsets.UTF_8; + +//import org.testng.annotations.Test; +import static org.testng.Assert.*; + +public class BaseTest { + + private static String AGAMA_ACR = "agama"; + private Map map = null; + + Logger logger = LogManager.getLogger(getClass()); + WebClient client = null; + + BaseTest() { + + client = new WebClient(BrowserVersion.CHROME); + WebClientOptions options = client.getOptions(); + + options.setThrowExceptionOnFailingStatusCode(false); + //prevents the finish page to autosubmit the POST to AS's postlogin endpoint + options.setJavaScriptEnabled(false); + + } + + @BeforeSuite + public void init(ITestContext context) throws IOException { + + String propertiesFile = context.getCurrentXmlTest().getParameter("propertiesFile"); + Properties prop = new Properties(); + prop.load(Files.newBufferedReader(Paths.get(propertiesFile), UTF_8)); + + map = new Hashtable<>(); + //do not bother about empty keys... but + //If a value is found null, this will throw a NPE since we are using a Hashtable + prop.forEach((key, value) -> map.put(key.toString(), value.toString())); + context.getSuite().getXmlSuite().setParameters(map); + + } + + String authzRequestUrl(String flowQName, Map inputs) { + + OAuthParams p = new OAuthParams(); + p.setAuthzEndpoint(map.get("authzEndpoint")); + p.setClientId(map.get("clientId")); + p.setRedirectUri(map.get("redirectUri")); + p.setScopes(Collections.singletonList("openid")); + + String queryParam = URLEncoder.encode(map.get("custParamName"), UTF_8); + + StringBuilder builder = new StringBuilder(flowQName); + if (inputs != null) { + JSONObject jo = new JSONObject(inputs); + builder.append("-").append(jo.toString()); + } + + Map custParams = new HashMap<>(); + custParams.put("acr_values", AGAMA_ACR); + custParams.put(queryParam, builder.toString()); + p.setCustParamsAuthReq(custParams); + + String url = null; + CodeGrantUtil grant = new CodeGrantUtil(p); + + try { + url = grant.makeAuthzRequest().getFirst(); + logger.debug("Authentication request built is: {}", url); + } catch (URISyntaxException e) { + fail(e.getMessage(), e); + } + return url; + + } + + HtmlPage launch(String flowQName, Map parameters) { + + //Generate an authn request and launch it in the htmlUnit browser + String url = authzRequestUrl(flowQName, parameters); + logger.info("Starting flow {}", flowQName); + try { + Page p = client.getPage(url); + + //Check it is an ok web page + assertTrue(p.isHtmlPage(), "Not an html page"); + assertOK(p); + return (HtmlPage) p; + + } catch (IOException e) { + fail(e.getMessage(), e); + return null; + } + + } + + void validateFinishPage(HtmlPage page, boolean success) { + + assertOK(page); + //check we are effectively at the finish page + String title = page.getTitleText().toLowerCase(); + + if (success) { + assertTrue(title.contains("redirect"), "'redirect' word not found in page title"); + + List forms = page.getForms(); + assertEquals(forms.size(), 1, "Page should have one and only one form"); + + HtmlForm form = forms.get(0); + assertTrue(form.getActionAttribute().contains("postlogin"), "Form does not have the expected action attribute"); + assertEquals(form.getMethodAttribute().toLowerCase(), "post", "Form does not use POST"); + + } else { + + assertTrue(title.contains("error"), "'error' word not found in page title"); + + String text = page.getVisibleText().toLowerCase(); + assertTrue(text.contains("authentication"), "'authentication' word not found in page text"); + assertTrue(text.contains("failed"), "'failed' word not found in page text"); + } + + } + + void assertOK(Page page) { + assertEquals(page.getWebResponse().getStatusCode(), WebResponse.OK); + } + + void assertServerError(Page page) { + assertEquals(page.getWebResponse().getStatusCode(), WebResponse.INTERNAL_SERVER_ERROR); + } + +

P doClick(DomElement el) { + + try { + return el.click(); + } catch (IOException e) { + fail(e.getMessage(), e); + return null; + } + + } + + void typeInInputWithName(HtmlForm form, String name, String text) { + + try { + //See f1/index.ftl + form.getInputByName("something").type(text); + } catch (IOException e) { + fail(e.getMessage(), e); + } + + } + +} \ No newline at end of file diff --git a/agama/engine/src/test/java/io/jans/agama/test/CustomConfigsFlowTest.java b/agama/engine/src/test/java/io/jans/agama/test/CustomConfigsFlowTest.java new file mode 100644 index 00000000000..6e511e5df9b --- /dev/null +++ b/agama/engine/src/test/java/io/jans/agama/test/CustomConfigsFlowTest.java @@ -0,0 +1,64 @@ +package io.jans.agama.test; + +import com.gargoylesoftware.htmlunit.html.HtmlPage; +import com.gargoylesoftware.htmlunit.html.HtmlSubmitInput; +import com.gargoylesoftware.htmlunit.WebResponse; + +import org.testng.annotations.Parameters; +import org.testng.annotations.Test; + +import static org.testng.Assert.*; + +@org.testng.annotations.Ignore +public class CustomConfigsFlowTest extends BaseTest { + + private static final String QNAME = "io.jans.agama.test.showConfig"; + + @Test + public void withTimeout() { + + HtmlPage page = launchAndWait(10); + //Flow should have timed out now - see flow impl + //The page currently shown may correspond to the Agama timeout template or to + //the mismatch (not found) page in case the cleaner job already wiped the flow execution + + int status = page.getWebResponse().getStatusCode(); + String text = page.getVisibleText().toLowerCase(); + + if (status == WebResponse.OK) { + //See timeout.ftlh + assertTrue(text.contains("took")); + assertTrue(text.contains("more")); + assertTrue(text.contains("expected")); + } else if (status == WebResponse.NOT_FOUND) { + //See mismatch.ftlh + assertTrue(text.contains("not")); + assertTrue(text.contains("found")); + } else { + fail("Unexpected status code " + status); + } + + } + + @Test + public void noTimeout() { + HtmlPage page = launchAndWait(2); + validateFinishPage(page, false); + } + + private HtmlPage launchAndWait(int wait) { + + HtmlPage page = launch(QNAME, null); + try { + Thread.sleep(1000L * wait); + } catch (InterruptedException e) { + fail(e.getMessage(), e); + } + + //click on the "Continue" button + HtmlSubmitInput button = page.getForms().get(0).getInputByValue("Continue"); + return doClick(button); + + } + +} \ No newline at end of file diff --git a/agama/engine/src/test/java/io/jans/agama/test/MathFlowTest.java b/agama/engine/src/test/java/io/jans/agama/test/MathFlowTest.java new file mode 100644 index 00000000000..1d5ebeead10 --- /dev/null +++ b/agama/engine/src/test/java/io/jans/agama/test/MathFlowTest.java @@ -0,0 +1,62 @@ +package io.jans.agama.test; + +import com.gargoylesoftware.htmlunit.html.HtmlPage; + +import java.util.Arrays; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.testng.annotations.Test; + +import static org.testng.Assert.*; + +@org.testng.annotations.Ignore +public class MathFlowTest extends BaseTest { + + private static final String QNAME = "io.jans.agama.test.math"; + + @Test + public void runEmpty() { + HtmlPage page = launch(QNAME, Collections.singletonMap("numbers",Collections.emptyList())); + logger.info("Landed at {}", page.getUrl()); + assertServerError(page); + } + + @Test + public void runRandom() { + + int len = (int) (Math.random() * 10); + List list = new ArrayList<>(); + + for (int i = 0; i < len; i++) { + list.add(1 + (int) (Math.random() * 100)); + } + run(list); + + } + + @Test + public void runFixed1() { + run(Arrays.asList(30, 42, 70, 105)); + } + + @Test + public void runFixed2() { + run(Arrays.asList(6, 12, 22, 27)); + } + + @Test + public void runFixed3() { + run(Arrays.asList(1, 1, 1)); + } + + private void run(List list) { + + HtmlPage page = launch(QNAME, Collections.singletonMap("numbers", list)); + logger.info("Landed at {}", page.getUrl()); + validateFinishPage(page, true); + + } + +} diff --git a/agama/engine/src/test/java/io/jans/agama/test/SaySomething2FlowTest.java b/agama/engine/src/test/java/io/jans/agama/test/SaySomething2FlowTest.java new file mode 100644 index 00000000000..4a8ee169cfa --- /dev/null +++ b/agama/engine/src/test/java/io/jans/agama/test/SaySomething2FlowTest.java @@ -0,0 +1,57 @@ +package io.jans.agama.test; + +import com.gargoylesoftware.htmlunit.html.HtmlForm; +import com.gargoylesoftware.htmlunit.html.HtmlPage; +import com.gargoylesoftware.htmlunit.html.HtmlSubmitInput; + +import java.util.Arrays; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.testng.annotations.Parameters; +import org.testng.annotations.Test; + +import static org.testng.Assert.*; + +@org.testng.annotations.Ignore +public class SaySomething2FlowTest extends BaseTest { + + private static final String QNAME = "org.gluu.flow1"; + + @Test + public void something() { + HtmlPage page = run("Rosamaria Montibeller"); + validateFinishPage(page, true); + } + + @Test + public void nothing() { + HtmlPage page = run(null); + validateFinishPage(page, false); + } + + private HtmlPage run(String text) { + + HtmlPage page = launch(QNAME, null); + + //click on the "Continue" button + HtmlSubmitInput button = page.getForms().get(0).getInputByValue("Continue"); + page = doClick(button); + + assertOK(page); + assertTrue(page.getVisibleText().contains("Gluu")); //see f1/*.ftl + + HtmlForm form = page.getForms().get(0); + if (text != null) { + //See f1/index.ftl + typeInInputWithName(form, "something", text); + } + + //click on the "Continue" button + button = form.getInputByValue("Continue"); + return doClick(button); + + } + +} diff --git a/agama/engine/src/test/java/io/jans/agama/test/SaySomething3FlowTest.java b/agama/engine/src/test/java/io/jans/agama/test/SaySomething3FlowTest.java new file mode 100644 index 00000000000..1edc5ec8723 --- /dev/null +++ b/agama/engine/src/test/java/io/jans/agama/test/SaySomething3FlowTest.java @@ -0,0 +1,40 @@ +package io.jans.agama.test; + +import com.gargoylesoftware.htmlunit.html.HtmlForm; +import com.gargoylesoftware.htmlunit.html.HtmlPage; +import com.gargoylesoftware.htmlunit.html.HtmlSubmitInput; + +import java.util.Arrays; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.testng.annotations.Parameters; +import org.testng.annotations.Test; + +import static org.testng.Assert.*; + +@org.testng.annotations.Ignore +public class SaySomething3FlowTest extends BaseTest { + + private static final String QNAME = "org.gluu.flow3"; + + @Test + public void test() { + HtmlPage page = launch(QNAME, null); + + //click on the "Go" button + HtmlSubmitInput button = page.getForms().get(0).getInputByValue("Go"); + page = doClick(button); + + assertOK(page); + assertTrue(page.getVisibleText().contains("Agama")); //see me/myindex.ftl and f1/index2.ftl + + button = page.getForms().get(0).getInputByValue("Continue"); + page = doClick(button); + + validateFinishPage(page, false); + + } + +} \ No newline at end of file diff --git a/agama/engine/src/test/java/io/jans/agama/test/SaySomethingFlowTest.java b/agama/engine/src/test/java/io/jans/agama/test/SaySomethingFlowTest.java new file mode 100644 index 00000000000..509a7f9f17a --- /dev/null +++ b/agama/engine/src/test/java/io/jans/agama/test/SaySomethingFlowTest.java @@ -0,0 +1,59 @@ +package io.jans.agama.test; + +import com.gargoylesoftware.htmlunit.html.HtmlForm; +import com.gargoylesoftware.htmlunit.html.HtmlPage; +import com.gargoylesoftware.htmlunit.html.HtmlSubmitInput; + +import java.util.Arrays; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.testng.annotations.Parameters; +import org.testng.annotations.Test; + +import static org.testng.Assert.*; + +@org.testng.annotations.Ignore +public class SaySomethingFlowTest extends BaseTest { + + private static final String QNAME = "org.gluu.flow2"; + + @Test + public void nothing() { + HtmlPage page = run(null, null); + validateFinishPage(page, false); + } + + @Test + public void NoOnesomething() { + HtmlPage page = run(null, "Jans over Gluu"); + validateFinishPage(page, true); + } + + @Test + public void someOnesomething() { + HtmlPage page = run("jgomer2001", "I like CE"); + validateFinishPage(page, true); + } + + private HtmlPage run(String name, String text) { + + boolean nameEmpty = name == null; + HtmlPage page = launch(QNAME, nameEmpty ? null : Collections.singletonMap("val", name)); + + if (!nameEmpty) assertTrue(page.getVisibleText().contains(name)); + + HtmlForm form = page.getForms().get(0); + + if (text != null) { + //See f1/index.ftl + typeInInputWithName(form, "something", text); + } + //click on the "Continue" button + HtmlSubmitInput button = form.getInputByValue("Continue"); + return doClick(button); + + } + +} \ No newline at end of file diff --git a/agama/engine/src/test/java/io/jans/agama/test/UidOnlyAuthTest.java b/agama/engine/src/test/java/io/jans/agama/test/UidOnlyAuthTest.java new file mode 100644 index 00000000000..0c7720f5afa --- /dev/null +++ b/agama/engine/src/test/java/io/jans/agama/test/UidOnlyAuthTest.java @@ -0,0 +1,42 @@ +package io.jans.agama.test; + +import com.gargoylesoftware.htmlunit.html.HtmlPage; +import com.gargoylesoftware.htmlunit.html.HtmlSubmitInput; + +import java.util.Arrays; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Parameters; +import org.testng.annotations.Test; + +import static org.testng.Assert.*; + +@org.testng.annotations.Ignore +public class UidOnlyAuthTest extends BaseTest { + + private static final String QNAME = "io.jans.agama.test.auth.uidOnly"; + + @BeforeClass + public void enableJS() { + client.getOptions().setJavaScriptEnabled(true); + } + + @Test + public void randUid() { + HtmlPage page = run("" + Math.random()); + validateFinishPage(page, false); + } + + private HtmlPage run(String uid) { + + HtmlPage page = launch(QNAME, Collections.singletonMap("uid", uid)); + //click on the "Continue" button + HtmlSubmitInput button = page.getForms().get(0).getInputByValue("Continue"); + return doClick(button); + + } + +} \ No newline at end of file