[java-idp-integration-tests] branch main updated: Work on WebAuthn Plugin test
Tom Zeller
tzeller at dragonacea.biz
Tue Sep 17 23:18:34 UTC 2024
This is an automated email from the git hooks/post-receive script.
tzeller pushed a commit to branch main
in repository java-idp-integration-tests.
View the commit online:
http://git.shibboleth.net/view/?p=java-idp-integration-tests.git;a=commit;h=98b8740d4303c9d705d428c11e036b205bd9c80f
The following commit(s) were added to refs/heads/main by this push:
new 98b8740 Work on WebAuthn Plugin test
98b8740 is described below
commit 98b8740d4303c9d705d428c11e036b205bd9c80f
Author: Tom Zeller <tzeller at dragonacea.biz>
AuthorDate: Tue Sep 17 18:18:25 2024 -0500
Work on WebAuthn Plugin test
---
.../shibboleth/idp/integration/tests/BaseTest.java | 124 ++++++
.../tests/plugins/WebAuthnPluginTest.java | 428 +++++++++++++++++++++
.../tests/util/testng/IgnoreTestIntercepter.java | 25 ++
.../tests/util/testng/annotation/ChromeOnly.java | 13 +
.../truststore.asc | 287 ++++++++++++++
src/test/webauthn/mfa-authn-config.xml | 41 ++
6 files changed, 918 insertions(+)
diff --git a/src/test/java/net/shibboleth/idp/integration/tests/BaseTest.java b/src/test/java/net/shibboleth/idp/integration/tests/BaseTest.java
index 76fbe94..83886a9 100644
--- a/src/test/java/net/shibboleth/idp/integration/tests/BaseTest.java
+++ b/src/test/java/net/shibboleth/idp/integration/tests/BaseTest.java
@@ -60,6 +60,7 @@ import java.util.stream.Stream;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
+import org.apache.commons.io.FileUtils;
import org.apache.hc.client5.http.classic.HttpClient;
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
@@ -428,6 +429,14 @@ public abstract class BaseTest {
@Nonnull
final public String truststoresResource = "/net/shibboleth/idp/integration/tests/truststores/";
+ /** Directory to store test distributions, defaults to "test-distributions/" */
+ @Nonnull
+ public final Path pathToTestDistributions = Paths.get(BaseTest.TEST_DISTRIBUTIONS_DIRECTORY);
+
+ /** Directory to store downloads, defaults to "test-distributions/downloads" */
+ @Nonnull
+ public final Path pathToDownloadsDir = pathToTestDistributions.resolve("downloads");
+
/** Class logger. */
@Nonnull private final Logger log = LoggerFactory.getLogger(BaseTest.class);
@@ -3237,4 +3246,119 @@ public abstract class BaseTest {
}
}
+ /**
+ * Wait for element to be clickable and click it.
+ *
+ * @param by
+ * locator
+ */
+ public void click(@Nonnull final By by) {
+ final WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
+ wait.until(ExpectedConditions.elementToBeClickable(by));
+ driver.findElement(by).click();
+ }
+
+ /**
+ * Download file at given URL to the downloads directory.
+ *
+ * See {@link pathToDownloadsDir} for path to downloads directory.
+ *
+ * Will not download file if it already exists in the downloads directory.
+ *
+ * @param baseUrl
+ * URL to download from, for example :
+ * "https://shibboleth.net/downloads/prerelease/"
+ * @param fileName
+ * name of file to download, for example :
+ * "idp-plugin-webauthn-0.0.3.tar.gz"
+ * @throws IOException
+ * if an error occurs
+ * @throws URISyntaxException
+ * if the URL is not valid
+ */
+ public void download(@Nonnull final String baseUrl, @Nonnull final String fileName)
+ throws IOException, URISyntaxException {
+
+ final Path pathToDownloadedFile = pathToDownloadsDir.resolve(fileName);
+
+ if (pathToDownloadedFile.toFile().exists()) {
+ return;
+ }
+
+ if (!pathToDownloadsDir.toFile().exists()) {
+ Files.createDirectory(pathToDownloadsDir);
+ }
+
+ final String urlToDownload = baseUrl + fileName;
+
+ log.debug("Downloading '{}' to '{}'", urlToDownload, pathToDownloadedFile);
+
+ FileUtils.copyURLToFile(new URI(urlToDownload).toURL(), pathToDownloadedFile.toFile());
+ }
+
+ /**
+ * Download prelease plugin or distribution and the detached signature.
+ *
+ * @param fileName
+ * name of prerelease plugin or distribution
+ * @throws IOException
+ * if an error occurs
+ * @throws URISyntaxException
+ * if the URL to download from is invalid
+ */
+ public void downloadPrerelease(@Nonnull final String fileName) throws IOException, URISyntaxException {
+ download("https://shibboleth.net/downloads/prerelease/", fileName);
+ download("https://shibboleth.net/downloads/prerelease/", fileName + ".asc");
+ }
+
+ /**
+ * Install local plugin prerelease.
+ *
+ * The truststore must exist in the {@link pathToTruststores} directory.
+ *
+ * @param plugin
+ * the plugin ID
+ * @param fileName
+ * the name of the downloaded plugin in the downloads directory
+ * @throws IOException
+ * if an error occurs
+ */
+ public void installLocalPluginPrerelease(@Nonnull final String plugin, @Nonnull final String fileName)
+ throws IOException {
+
+ log.debug("Install local plugin prerelease '{}'", fileName);
+
+ final Path pathToPlugin = pathToDownloadsDir.resolve(fileName);
+ assert pathToPlugin.toAbsolutePath().toFile().exists() : "Path to plugin not found " + pathToPlugin;
+
+ final Path pathToTruststore = pathToTruststores.resolve(plugin).resolve("truststore.asc");
+ assert pathToTruststore.toAbsolutePath().toFile().exists() : "Path to truststore not found " + pathToTruststore;
+
+ final String[] commands = new String[] { //
+ pluginCLI, //
+ "--noCheck", //
+ "--noRebuild", //
+ "--noPrompt", //
+ "--input ", pathToPlugin.toAbsolutePath().toString(), //
+ "--truststore ", pathToTruststore.toString() };
+
+ log.debug("Install local plugin prerelease with command '{}'", String.join("", commands));
+
+ final Process process = new ProcessBuilder() //
+ .command(commands)
+ .directory(idpHome)
+ .redirectErrorStream(true)
+ .start();
+
+ logProcess(process, "Install local plugin :");
+ }
+
+ /**
+ * Return true if SELENIUM_BROWSER system property is "chrome", ignoring case.
+ *
+ * @return true if SELENIUM_BROWSER system property is "chrome", ignoring case
+ */
+ public static boolean isChrome() {
+ return System.getProperty("SELENIUM_BROWSER").toLowerCase().equals("chrome");
+ }
}
diff --git a/src/test/java/net/shibboleth/idp/integration/tests/plugins/WebAuthnPluginTest.java b/src/test/java/net/shibboleth/idp/integration/tests/plugins/WebAuthnPluginTest.java
new file mode 100644
index 0000000..f931927
--- /dev/null
+++ b/src/test/java/net/shibboleth/idp/integration/tests/plugins/WebAuthnPluginTest.java
@@ -0,0 +1,428 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements. See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID 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 net.shibboleth.idp.integration.tests.plugins;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.time.Duration;
+
+import javax.annotation.Nonnull;
+
+import org.openqa.selenium.Alert;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.chrome.ChromeDriver;
+import org.openqa.selenium.support.ui.ExpectedConditions;
+import org.openqa.selenium.support.ui.WebDriverWait;
+import org.openqa.selenium.virtualauthenticator.HasVirtualAuthenticator;
+import org.openqa.selenium.virtualauthenticator.VirtualAuthenticator;
+import org.openqa.selenium.virtualauthenticator.VirtualAuthenticatorOptions;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import net.shibboleth.idp.integration.tests.saml2.AbstractSAML2IntegrationTest;
+import net.shibboleth.idp.integration.tests.util.testng.annotation.ChromeOnly;
+
+/**
+ * Some tests for the WebAuthn plugin for the IdP.
+ *
+ * https://shibboleth.atlassian.net/wiki/spaces/IDPPLUGINS/pages/3395321933/WebAuthnAuthnConfiguration
+ */
+public class WebAuthnPluginTest extends AbstractSAML2IntegrationTest {
+
+ /** Class logger. */
+ @Nonnull
+ private final Logger log = LoggerFactory.getLogger(WebAuthnPluginTest.class);
+
+ /**
+ * Download, cache, and install plugin dependencies to speed up tests during
+ * development.
+ *
+ * @throws IOException
+ * if an error occurs
+ */
+ protected void downloadAndInstallPluginDependencies() throws IOException {
+ try {
+ // "net.shibboleth.oidc.common"
+ download("https://shibboleth.net/downloads/identity-provider/plugins/oidc-common/3.1.1/",
+ "oidc-common-dist-3.1.1.tar.gz");
+ download("https://shibboleth.net/downloads/identity-provider/plugins/oidc-common/3.1.1/",
+ "oidc-common-dist-3.1.1.tar.gz.asc");
+ installLocalPlugin("net.shibboleth.oidc.common",
+ pathToDownloadsDir.resolve("oidc-common-dist-3.1.1.tar.gz").toAbsolutePath().toString());
+
+ // "net.shibboleth.idp.plugin.nashorn"
+ download("https://shibboleth.net/downloads/identity-provider/plugins/duo-oidc/2.1.0/",
+ "idp-plugin-duo-nimbus-dist-2.1.0.tar.gz");
+ download("https://shibboleth.net/downloads/identity-provider/plugins/duo-oidc/2.1.0/",
+ "idp-plugin-duo-nimbus-dist-2.1.0.tar.gz.asc");
+ installLocalPlugin("net.shibboleth.idp.plugin.authn.duo.nimbus",
+ pathToDownloadsDir.resolve("idp-plugin-duo-nimbus-dist-2.1.0.tar.gz").toAbsolutePath().toString());
+
+ // "net.shibboleth.idp.plugin.authn.duo.nimbus"
+ download("https://shibboleth.net/downloads/identity-provider/plugins/scripting/2.0.0/",
+ "idp-plugin-nashorn-jdk-dist-2.0.0.tar.gz");
+ download("https://shibboleth.net/downloads/identity-provider/plugins/scripting/2.0.0/",
+ "idp-plugin-nashorn-jdk-dist-2.0.0.tar.gz.asc");
+ installLocalPlugin("net.shibboleth.idp.plugin.nashorn",
+ pathToDownloadsDir.resolve("idp-plugin-nashorn-jdk-dist-2.0.0.tar.gz").toAbsolutePath().toString());
+ } catch (final URISyntaxException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ /**
+ * Install the WebAuthn Plugin and dependencies.
+ *
+ * @throws IOException
+ * if an error occurs
+ */
+ protected void installWebAuthnPlugin() throws IOException {
+
+ final String[] plugins = new String[] { //
+ "net.shibboleth.oidc.common", //
+ "net.shibboleth.idp.plugin.authn.duo.nimbus", //
+ "net.shibboleth.idp.plugin.nashorn" };
+
+ // TODO Include WebAuthn Plugin after 1.0 is released
+ // "net.shibboleth.idp.plugin.authn.webauthn"
+ // installPlugins(plugins);
+ downloadAndInstallPluginDependencies();
+ installWebAuthnPluginPrelease();
+
+ assertPluginsAreInstalled(plugins);
+ }
+
+ /**
+ * Install WebAuthn Plugin prerelease.
+ *
+ * @throws IOException
+ * if an error occurs
+ */
+ protected void installWebAuthnPluginPrelease() throws IOException {
+ try {
+
+ final String plugin = "net.shibboleth.idp.plugin.authn.webauthn";
+
+ final String fileName = "idp-plugin-webauthn-0.0.3.tar.gz";
+
+ downloadPrerelease(fileName);
+
+ installLocalPluginPrerelease(plugin, fileName);
+
+ assertPluginsAreInstalled(plugin);
+
+ } catch (final URISyntaxException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ /**
+ * Enter credential nickname.
+ *
+ * @param nickname
+ * credential nickname
+ */
+ protected void sendCredentialNickname(final @Nonnull String nickname) {
+
+ final WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
+
+ final Alert alert = wait.until(ExpectedConditions.alertIsPresent());
+
+ driver.switchTo().alert();
+
+ alert.sendKeys(nickname);
+
+ alert.accept();
+ }
+
+ /**
+ * Log in passwordless using "jdoe" as username.
+ */
+ protected void loginPasswordless() {
+ loginPasswordless("jdoe");
+ }
+
+ /**
+ * Log in passwordless.
+ *
+ * @param username
+ * user name
+ */
+ protected void loginPasswordless(final @Nonnull String username) {
+
+ final WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
+
+ wait.until(driver -> driver.findElement(By.name("j_username")));
+
+ final WebElement usernameElement = driver.findElement(By.name("j_username"));
+
+ usernameElement.sendKeys(username);
+
+ submitForm();
+ }
+
+ /**
+ * Set up access control policy to allow current user.
+ *
+ * @throws IOException
+ * if an error occurs
+ */
+ protected void setUpAccessControlPolicy() throws IOException {
+
+ final Path pathToAccessControlXML = Paths.get("conf", "access-control.xml");
+
+ final String oldText = " </util:map>";
+
+ final String newText = //
+ " <entry key=\"AccessByCurrentUser\">" + "\n" + //
+ " <bean parent=\"shibboleth.PredicateAccessControl\">" + "\n" + //
+ " <constructor-arg>" + "\n" + //
+ " <bean id=\"AccessByCurrentUserPredicate\"" + "\n" + //
+ " class=\"net.shibboleth.idp.plugin.authn.webauthn.admin.impl.AllowCurrentUserAccessPredicate\">\n"
+ + //
+ " </bean>" + "\n" + //
+ " </constructor-arg>" + "\n" + //
+ " </bean>" + "\n" + //
+ " </entry>" + "\n";
+
+ replaceIdPHomeFile(pathToAccessControlXML, oldText, newText + "\n" + oldText);
+ }
+
+ /**
+ * Do not enable attribute consent.
+ */
+ @BeforeClass
+ @Override
+ public void setUpAttributeConsent() throws IOException {
+ // do not enable attribute consent
+ }
+
+ /**
+ * Set up MFA authn flow.
+ *
+ * @throws IOException
+ * if an error occurs
+ */
+ protected void setUpMFAFlow() throws IOException {
+
+ final Path pathToWebAuthnProperties = Paths.get("conf", "authn", "authn.properties");
+
+ final String oldFlows = "#idp.authn.flows = Password";
+
+ final String newFlows = "idp.authn.flows = MFA";
+
+ replaceIdPHomeFile(pathToWebAuthnProperties, oldFlows, newFlows);
+ }
+
+ /**
+ * Set up MFA strategy to use WebAuthn as the sole factor but with password
+ * fallback for registration.
+ *
+ * @throws IOException
+ * if an error occurs
+ */
+ protected void setUpMFAStrategy() throws IOException {
+
+ final Path projectDir = pathToTestDistributions.toAbsolutePath().toFile().getParentFile().toPath();
+
+ assert projectDir.toAbsolutePath().toFile().exists() : "Path to project directory not found";
+
+ final Path pathToNewConfig = projectDir.resolve(Paths.get("src", "test", "webauthn", "mfa-authn-config.xml"));
+
+ assert pathToNewConfig.toAbsolutePath().toFile().exists() : "Path to new mfa-authn-config.xml not found";
+
+ final Path pathToMFAAuthnConfig = pathToIdPHome.resolve(Paths.get("conf", "authn", "mfa-authn-config.xml"));
+
+ Files.copy(pathToNewConfig, pathToMFAAuthnConfig, StandardCopyOption.REPLACE_EXISTING);
+ }
+
+ /**
+ * Set up SAML2 Redirect URLs for testing. -
+ */
+ protected void setUpSAML2URLs() {
+
+ startFlowURLPath = "/sp/SAML2/InitSSO/Redirect";
+
+ loginPageURLPath = "/idp/profile/SAML2/Redirect/SSO";
+
+ forceAuthnRequestURLPath = "/sp/SAML2/InitSSO/ForceAuthn";
+
+ responsePageURLPath = "/sp/SAML2/POST/ACS";
+ }
+
+ /**
+ * Set up virtual authenticator.
+ *
+ * @return the virtual authenticator
+ */
+ protected VirtualAuthenticator setUpVirtualAuthenticator() {
+
+ final VirtualAuthenticatorOptions options = new VirtualAuthenticatorOptions() //
+ .setIsUserVerified(true)
+ .setHasUserVerification(true)
+ .setTransport(VirtualAuthenticatorOptions.Transport.INTERNAL)
+ .setProtocol(VirtualAuthenticatorOptions.Protocol.CTAP2);
+
+ final VirtualAuthenticator authenticator = ((HasVirtualAuthenticator) driver).addVirtualAuthenticator(options);
+ log.debug("Set up virutal authenticator '{}'", authenticator);
+ return authenticator;
+ }
+
+ /**
+ * Download, install, and set up WebAuthn Plugin.
+ *
+ * @throws IOException
+ * if an error occurs
+ * @throws URISyntaxException
+ * if a download URL is incorrect
+ */
+ protected void setUpWebAuthnPlugin() throws IOException, URISyntaxException {
+
+ installWebAuthnPlugin();
+
+ enableModule("idp.authn.MFA");
+
+ buildWAR();
+
+ setUpWebAuthnRelyingParty();
+
+ setUpMFAFlow();
+
+ setUpMFAStrategy();
+
+ setUpAccessControlPolicy();
+ }
+
+ /**
+ * Set up WebAuthn relying party :
+ *
+ * idp.authn.webauthn.relyingPartyId = tests.shibboleth.net
+ *
+ * idp.authn.webauthn.allowOriginPort = true
+ *
+ * idp.authn.webauthn.allowOriginSubdomain = true
+ *
+ * @throws IOException
+ * if an error occurs
+ */
+ protected void setUpWebAuthnRelyingParty() throws IOException {
+
+ final Path pathToWebAuthnProperties = Paths.get("conf", "authn", "webauthn.properties");
+
+ final String oldRelyingPartyId = "idp.authn.webauthn.relyingPartyId = localhost";
+
+ final String newRelyingPartyId = "idp.authn.webauthn.relyingPartyId = tests.shibboleth.net";
+
+ replaceIdPHomeFile(pathToWebAuthnProperties, oldRelyingPartyId, newRelyingPartyId);
+
+ final String oldAllowOriginPort = "#idp.authn.webauthn.allowOriginPort = false";
+
+ final String newAllowOriginPort = "idp.authn.webauthn.allowOriginPort = true";
+
+ replaceIdPHomeFile(pathToWebAuthnProperties, oldAllowOriginPort, newAllowOriginPort);
+
+ final String oldAllowOriginSubdomain = "#idp.authn.webauthn.allowOriginSubdomain = false";
+
+ final String newAllowOriginSubdomain = "idp.authn.webauthn.allowOriginSubdomain = true";
+
+ replaceIdPHomeFile(pathToWebAuthnProperties, oldAllowOriginSubdomain, newAllowOriginSubdomain);
+ }
+
+ /**
+ * Test SAML 2 redirect SSO using WebAuthn plugin for authentication.
+ *
+ * A virtual devices is registered using password fallback.
+ *
+ * @throws Exception
+ * if an error occurs
+ */
+ @Test(enabled = false)
+ @ChromeOnly
+ public void testSAML2SSO() throws Exception {
+
+ setUpSAML2URLs();
+
+ startBrowser();
+
+ if (!(driver instanceof ChromeDriver)) {
+ log.info("WebAuthn plugin test is only supported on Chrome");
+ return;
+ }
+
+ setUpWebAuthnPlugin();
+
+ startServer();
+
+ setUpVirtualAuthenticator();
+
+ startFlow();
+
+ waitForLoginPage();
+
+ click(By.linkText("Enroll New Devices"));
+
+ waitForPageWithURL(getBaseURL() + "/idp/profile/admin/webauthn-registration");
+
+ loginPasswordless();
+
+ waitForPageWithURL(getBaseURL() + "/idp/profile/admin/webauthn-registration");
+
+ login();
+
+ click(By.id("registerButton"));
+
+ sendCredentialNickname("Test credential nickname");
+
+ click(By.id("finish_button"));
+
+ waitForPageBodyContains("Your registration session has ended");
+
+ // login
+
+ startFlow();
+
+ // already authenticated
+
+ waitForResponsePage();
+
+ validateResponse();
+
+ // force authn
+
+ driver.get(getBaseURL() + forceAuthnRequestURLPath);
+
+ waitForLoginPage();
+
+ loginPasswordless();
+
+ click(By.id("authenticate"));
+
+ waitForResponsePage();
+
+ validator.authnContextClassRef = "http://example.org/ac/classes/mfa";
+
+ validateResponse();
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/net/shibboleth/idp/integration/tests/util/testng/IgnoreTestIntercepter.java b/src/test/java/net/shibboleth/idp/integration/tests/util/testng/IgnoreTestIntercepter.java
index 1fac73f..8a49789 100644
--- a/src/test/java/net/shibboleth/idp/integration/tests/util/testng/IgnoreTestIntercepter.java
+++ b/src/test/java/net/shibboleth/idp/integration/tests/util/testng/IgnoreTestIntercepter.java
@@ -13,6 +13,7 @@ import org.testng.IMethodInterceptor;
import org.testng.ITestContext;
import net.shibboleth.idp.integration.tests.BaseTest;
+import net.shibboleth.idp.integration.tests.util.testng.annotation.ChromeOnly;
import net.shibboleth.idp.integration.tests.util.testng.annotation.IdPV5OrLater;
import net.shibboleth.idp.integration.tests.util.testng.annotation.LinuxOnly;
import net.shibboleth.idp.integration.tests.util.testng.annotation.OPConformance;
@@ -59,6 +60,30 @@ public class IgnoreTestIntercepter implements IMethodInterceptor {
return filteredTestMethods;
}
+ /**
+ * Return true if test is annotated as {@link ChromeOnly} and browser is Chrome.
+ *
+ * See {@link BaseTest#isChrome()}.
+ *
+ * @param method
+ * the test method
+ * @return return false if test is annotated as ChromeOnly and browser is
+ * Chrome, true otherwise.
+ */
+ public boolean chromeOnly(final IMethodInstance method) {
+
+ final ChromeOnly chromeOnly = method.getMethod()
+ .getConstructorOrMethod()
+ .getMethod()
+ .getAnnotation(ChromeOnly.class);
+
+ if (chromeOnly != null && BaseTest.isChrome()) {
+ return true;
+ }
+
+ return false;
+ }
+
/**
* Return false if test is annotated as {@link IdPV5OrLater} and IdP major
* version is less than 5.
diff --git a/src/test/java/net/shibboleth/idp/integration/tests/util/testng/annotation/ChromeOnly.java b/src/test/java/net/shibboleth/idp/integration/tests/util/testng/annotation/ChromeOnly.java
new file mode 100644
index 0000000..1e23376
--- /dev/null
+++ b/src/test/java/net/shibboleth/idp/integration/tests/util/testng/annotation/ChromeOnly.java
@@ -0,0 +1,13 @@
+
+package net.shibboleth.idp.integration.tests.util.testng.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(ElementType.METHOD)
+public @interface ChromeOnly {
+
+}
diff --git a/src/test/resources/net/shibboleth/idp/integration/tests/truststores/net.shibboleth.idp.plugin.authn.webauthn/truststore.asc b/src/test/resources/net/shibboleth/idp/integration/tests/truststores/net.shibboleth.idp.plugin.authn.webauthn/truststore.asc
new file mode 100644
index 0000000..30ac76c
--- /dev/null
+++ b/src/test/resources/net/shibboleth/idp/integration/tests/truststores/net.shibboleth.idp.plugin.authn.webauthn/truststore.asc
@@ -0,0 +1,287 @@
+
+
Philip David Smart <philip.smart at jisc.ac.uk> id B8A3DC52
+
-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: BCPG v1.72.2
+
+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
+AAEAEAAAkgoABQAAAAEAAAKqkhQAAwAAAAQAAAKyknwABwAAA+IAAAK6kpEAAgAA
+AAQ3MTAAkpIAAgAAAAQ3MTAAoAAABwAAAAQwMTAwoAIABAAAAAEAAACWoAMABAAA
+AAEAAADqohcAAwAAAAEAAgAAowEABwAAAAEBAAAApAIAAwAAAAEAAAAApAMAAwAA
+AAEAAAAApAUAAwAAAAEAHAAApAYAAwAAAAEAAAAApDIABQAAAAQAAAacpDMAAgAA
+AAYAAAa8pDQAAgAAACIAAAbCAAAAAAAAAAEAAAARAAAACQAAAAUyMDE4OjA3OjE2
+IDEwOjA3OjM4ADIwMTg6MDc6MTYgMTA6MDc6MzgAAAAKdAAAApMAAAhvAAAE+QAA
+KOIAAA/BAAAAAAAAAAEAAAGPAAAAZAXXBg4DvwPBQXBwbGUgaU9TAAABTU0AEwAB
+AAkAAAABAAAACQACAAcAAAIuAAAA+AADAAcAAABoAAADJgAEAAkAAAABAAAAAQAF
+AAkAAAABAAAA6AAGAAkAAAABAAAA8QAHAAkAAAABAAAAAQAIAAoAAAADAAADjgAM
+AAoAAAACAAADpgANAAkAAAABAAAAKAAOAAkAAAABAAAABAAPAAkAAAABAAAAAgAQ
+AAkAAAABAAAAAQARAAIAAAAlAAADtgAUAAkAAAABAAAABQAXAAkAAAABAAAAAAAZ
+AAkAAAABAAAAAAAaAAIAAAAGAAAD3AAfAAkAAAABAAAAAAAAAABicGxpc3QwME8R
+AgBHAkwCUQJVAlICSwJDAjsCLAIaAnMBzgCQAIgAiAB/AEsCUgJWAlYCTgJGAj4C
+MQIgAsoBjwB/AHkAgACBAIAATgJXAloCVgJOAkQCOAIlAg8CMwFwAHcAjACKAJYA
+jwBMAlYCVwJQAkYCOgIrAhIC4wGiAGUAkgCJAI4AjQCSAEoCUgJQAkcCOgIqAhYC
+9AFqAXgAdACRAIUAhQCMAIsASQJKAvYB+gC8AOIAjgGJAbEAcQBrAIMAeQB6AH4A
+gQBFAoEBTQDQALEA4QDiAL0ArgCZAIkAYwBoAHkAdQBuAEECBAF5ABgBtADHANUA
+uQCkAN0AyQCQAIMAfABqAGUAOwL2AHIAIgEEAb0ArACYAIYAuwDQAK8AXgBmAG8A
+eAAxAgkBUADvAIsAqwC/AJ0AbQB5AF8AZABvAHwAdAB1ACYC5gG3AHUAdgCuANwA
+DAHGAGIAYQBqAHEAdwBvAHAAHQIaAv8BtAGjAZ0BhwF4ATEBcwBtAHYAdQB0AGsA
+ZwAYAhkCDQL5AegB2gHFAagBcgGtAF8AdgBzAHIAagBoABUCGQIUAgkC/AHoAc0B
+sAGPARYBZgBmAHkAagBzAGYAEwITAg8CBAL4AeUBygGzAZsBfgHNAGEAcgBpAGsA
+YQAQAg0CCQL/AfQB4gHJAbMBoAGMAXwBUwEDAeAAcAByAAAIAAAAAAAAAgEAAAAA
+AAAAAQAAAAAAAAAAAAAAAAAAAgxicGxpc3QwMNQBAgMEBQYHCFVmbGFnc1V2YWx1
+ZVl0aW1lc2NhbGVVZXBvY2gQARMAAAU6zneKsRI7msoAEAAIERcdJy0vOD0AAAAA
+AAABAQAAAAAAAAAJAAAAAAAAAAAAAAAAAAAAP///xsEAChy8//+2DAAAShH///v9
+AArWDwAAADsAAAEAAAAAZQAAAQA0MkRDQUE3RC0wMTU0LTRBNTktQjY2NS03QTZB
+QjZBRkM4OEQAAHE4MjVzAAA/1d8AD/+1AD/V3wAP/7UAAAAJAAAABQAAAAkAAAAF
+QXBwbGUAaVBob25lIDcgYmFjayBjYW1lcmEgMy45OW1tIGYvMS44AAANAAEAAgAA
+AAJOAAAAAAIABQAAAAMAAAeGAAMAAgAAAAJXAAAAAAQABQAAAAMAAAeeAAUAAQAA
+AAEAAAAAAAYABQAAAAEAAAe2AAwAAgAAAAJLAAAAAA0ABQAAAAEAAAe+ABAAAgAA
+AAJUAAAAABEABQAAAAEAAAfGABcAAgAAAAJUAAAAABgABQAAAAEAAAfOAB8ABQAA
+AAEAAAfWAAAAAAAAADMAAAABAAAAIAAAAAEAAA5QAAAAZAAAAAMAAAABAAAACwAA
+AAEAABWDAAAAZAAAWb0AAADZAAAArQAABJ4AAN2FAAABWAAA3YUAAAFYAAAACAAA
+AAH/4Q1XaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVn
+aW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/PiA8eDp4bXBt
+ZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJYTVAgQ29yZSA2
+LjAuMCI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5
+OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFi
+b3V0PSIiIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIg
+eG1sbnM6bXdnLXJzPSJodHRwOi8vd3d3Lm1ldGFkYXRhd29ya2luZ2dyb3VwLmNv
+bS9zY2hlbWFzL3JlZ2lvbnMvIiB4bWxuczpzdEFyZWE9Imh0dHA6Ly9ucy5hZG9i
+ZS5jb20veG1wL3NUeXBlL0FyZWEjIiB4bWxuczphcHBsZS1maT0iaHR0cDovL25z
+LmFwcGxlLmNvbS9mYWNlaW5mby8xLjAvIiB4bWxuczpzdERpbT0iaHR0cDovL25z
+LmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL0RpbWVuc2lvbnMjIiB4bWxuczpwaG90
+b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iIHhtcDpD
+cmVhdGVEYXRlPSIyMDE4LTA3LTE2VDEwOjA3OjM4LjcxMCIgeG1wOkNyZWF0b3JU
+b29sPSIxMS40IiB4bXA6TW9kaWZ5RGF0ZT0iMjAxOC0wNy0xNlQxMDowNzozOCIg
+cGhvdG9zaG9wOkRhdGVDcmVhdGVkPSIyMDE4LTA3LTE2VDEwOjA3OjM4LjcxMCI+
+IDxtd2ctcnM6UmVnaW9ucyByZGY6cGFyc2VUeXBlPSJSZXNvdXJjZSI+IDxtd2ct
+cnM6UmVnaW9uTGlzdD4gPHJkZjpTZXE+IDxyZGY6bGk+IDxyZGY6RGVzY3JpcHRp
+b24gbXdnLXJzOlR5cGU9IkZhY2UiPiA8bXdnLXJzOkFyZWEgc3RBcmVhOnk9IjAu
+NTEyOTk5OTk5OTk5OTk5OSIgc3RBcmVhOnc9IjAuMjM3OTk5OTk5OTk5OTk5OTki
+IHN0QXJlYTp4PSIwLjM3MSIgc3RBcmVhOmg9IjAuMzE3OTk5OTk5OTk5OTk5OTUi
+IHN0QXJlYTp1bml0PSJub3JtYWxpemVkIi8+IDxtd2ctcnM6RXh0ZW5zaW9ucyBh
+cHBsZS1maTpBbmdsZUluZm9ZYXc9IjAiIGFwcGxlLWZpOkFuZ2xlSW5mb1JvbGw9
+IjI3MCIgYXBwbGUtZmk6Q29uZmlkZW5jZUxldmVsPSIxMDAwIiBhcHBsZS1maTpU
+aW1lc3RhbXA9IjEzNzk5OTkyMTQ1MCIgYXBwbGUtZmk6RmFjZUlEPSI5Ii8+IDwv
+cmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpsaT4gPC9yZGY6U2VxPiA8L213Zy1yczpS
+ZWdpb25MaXN0PiA8bXdnLXJzOkFwcGxpZWRUb0RpbWVuc2lvbnMgc3REaW06aD0i
+MzAyNCIgc3REaW06dz0iNDAzMiIgc3REaW06dW5pdD0icGl4ZWwiLz4gPC9td2ct
+cnM6UmVnaW9ucz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94Onht
+cG1ldGE+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPD94cGFja2V0
+IGVuZD0idyI/PgD/7QB4UGhvdG9zaG9wIDMuMAA4QklNBAQAAAAAAD8cAVoAAxsl
+RxwCAAACAAIcAj8ABjEwMDczOBwCPgAIMjAxODA3MTYcAjcACDIwMTgwNzE2HAI8
+AAYxMDA3MzgAOEJJTQQlAAAAAAAQOGix0QQ3mMOAExf+M5Hq4v/iAkBJQ0NfUFJP
+RklMRQABAQAAAjBBREJFAhAAAG1udHJSR0IgWFlaIAfQAAgACwATADMAO2Fjc3BB
+UFBMAAAAAG5vbmUAAAAAAAAAAAAAAAAAAAAAAAD21gABAAAAANMtQURCRQAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACmNwcnQA
+AAD8AAAAMmRlc2MAAAEwAAAAa3d0cHQAAAGcAAAAFGJrcHQAAAGwAAAAFHJUUkMA
+AAHEAAAADmdUUkMAAAHUAAAADmJUUkMAAAHkAAAADnJYWVoAAAH0AAAAFGdYWVoA
+AAIIAAAAFGJYWVoAAAIcAAAAFHRleHQAAAAAQ29weXJpZ2h0IDIwMDAgQWRvYmUg
+U3lzdGVtcyBJbmNvcnBvcmF0ZWQAAABkZXNjAAAAAAAAABFBZG9iZSBSR0IgKDE5
+OTgpAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABYWVogAAAAAAAA81EA
+AQAAAAEWzFhZWiAAAAAAAAAAAAAAAAAAAAAAY3VydgAAAAAAAAABAjMAAGN1cnYA
+AAAAAAAAAQIzAABjdXJ2AAAAAAAAAAECMwAAWFlaIAAAAAAAAJwYAABPpQAABPxY
+WVogAAAAAAAANI0AAKAsAAAPlVhZWiAAAAAAAAAmMQAAEC8AAL6c/8AAEQgA6gCW
+AwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQ
+AAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHw
+JDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hp
+anN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TF
+xsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEB
+AQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUh
+MQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4
+OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWW
+l5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp
+6vLz9PX29/j5+v/bAEMAGBgYGBgYKRgYKTopKSk6Tjo6OjpOY05OTk5OY3djY2Nj
+Y2N3d3d3d3d3d4+Pj4+Pj6enp6enu7u7u7u7u7u7u//bAEMBHR8fMCwwUiwsUsSF
+bYXExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTE
+xMTExP/dAAQACv/aAAwDAQACEQMRAD8A6KiiisiwpaSkJ4oAZK2xd3uB+fFP3CqL
+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
+=+Re0
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/src/test/webauthn/mfa-authn-config.xml b/src/test/webauthn/mfa-authn-config.xml
new file mode 100644
index 0000000..5ba7ffe
--- /dev/null
+++ b/src/test/webauthn/mfa-authn-config.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:context="http://www.springframework.org/schema/context"
+ xmlns:util="http://www.springframework.org/schema/util"
+ xmlns:p="http://www.springframework.org/schema/p"
+ xmlns:c="http://www.springframework.org/schema/c"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
+ http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
+
+ default-init-method="initialize"
+ default-destroy-method="destroy">
+
+ <util:map id="shibboleth.authn.MFA.TransitionMap">
+ <entry key="">
+ <bean parent="shibboleth.authn.MFA.Transition" p:nextFlowStrategy-ref="checkPasswordOrWebAuthn" />
+ </entry>
+ </util:map>
+
+ <!-- Use the webauthn flow unless a registration context exists and the user does not have any FIDO2 credentials registered,
+ then use a password flow.-->
+ <bean id="checkPasswordOrWebAuthn" parent="shibboleth.ContextFunctions.Scripted" factory-method="inlineScript">
+ <constructor-arg>
+ <value>
+ <![CDATA[
+ nextFlow = "authn/WebAuthn"
+ // Check if we can use a WebAuthn flow, or if the user has no credentials available to them use a password flow
+ webauthnRegCtx = input.getSubcontext("net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnRegistrationContext");
+ if (webauthnRegCtx != null){
+ if (!webauthnRegCtx.isWebAuthnAvailable()){
+ nextFlow = "authn/Password"
+ }
+ }
+ nextFlow; // pass control to WebAuthnFlow
+ ]]>
+ </value>
+ </constructor-arg>
+ </bean>
+
+</beans>
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list