[java-idp-integration-tests] branch main updated: Work on OIDC test

Tom Zeller tzeller at dragonacea.biz
Thu Feb 22 23:58:22 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=7fa600e59ebec05428da9ca0c7e6b79f0d6c0bb0

The following commit(s) were added to refs/heads/main by this push:
     new 7fa600e  Work on OIDC test
7fa600e is described below

commit 7fa600e59ebec05428da9ca0c7e6b79f0d6c0bb0
Author: Tom Zeller <tzeller at dragonacea.biz>
AuthorDate: Thu Feb 22 17:58:06 2024 -0600

    Work on OIDC test
    
    Still disabled, but should work
---
 pom.xml                                            |   2 +-
 src/test/docker/shib-tests-rp/Dockerfile           |   3 +-
 src/test/docker/shib-tests-rp/docker-compose.yml   |   1 +
 .../etc/httpd/conf.modules.d/10-auth_openidc.conf  |   8 +-
 .../idp/integration/tests/BaseIntegrationTest.java |  97 +++++
 .../idp/integration/tests/oidc/OIDCTest.java       | 481 ++++++++++++++++++++-
 .../idp/integration/tests/oidc/RPContainer.java    |  36 ++
 src/test/oidc/jetty-base/etc/rewrite-rules.xml     |  16 +
 .../oidc/shibboleth-idp/metadata/oidc-client.json  |  13 +
 src/test/plugins/.gitignore                        |   2 +
 10 files changed, 655 insertions(+), 4 deletions(-)

diff --git a/pom.xml b/pom.xml
index 2c32b68..c557918 100644
--- a/pom.xml
+++ b/pom.xml
@@ -449,7 +449,7 @@
                                 </goals>
                                 <configuration>
                                     <!-- Run only if DNS system property is true -->
-                                    <target if="DNS">
+                                    <target if="tests_cert">
                                         <!-- Copy pfx file -->
                                         <copy
                                             file="${env.tests_pfx_filepath}"
diff --git a/src/test/docker/shib-tests-rp/Dockerfile b/src/test/docker/shib-tests-rp/Dockerfile
index ae73c08..ceff775 100644
--- a/src/test/docker/shib-tests-rp/Dockerfile
+++ b/src/test/docker/shib-tests-rp/Dockerfile
@@ -32,8 +32,9 @@ RUN sed -i -e 's/#ServerName www.example.com:443/ServerName ${ServerName}:443/'
 
 # Copy demo web pages, non-secure displays "Hello world" while secure displays "Secure".
 COPY var/www/html/index.html        /var/www/html/index.html
-COPY var/www/html/secure/index.html /var/www/html/secureindex.html
+COPY var/www/html/secure/index.html /var/www/html/secure/index.html
 COPY var/www/cgi-bin/printenv       /var/www/cgi-bin/printenv
+RUN chmod ugo+x /var/www/cgi-bin/printenv
 
 # Expose http and https ports.
 EXPOSE 80 443
diff --git a/src/test/docker/shib-tests-rp/docker-compose.yml b/src/test/docker/shib-tests-rp/docker-compose.yml
index 2d50f1b..f593657 100644
--- a/src/test/docker/shib-tests-rp/docker-compose.yml
+++ b/src/test/docker/shib-tests-rp/docker-compose.yml
@@ -12,6 +12,7 @@ services:
     build: .
     environment:
       - ServerName=${ServerName:-rp.tests.shibboleth.net}
+      - OIDCProviderMetadataURL=${OIDCProviderMetadataURL:-https://idp.tests.shibboleth.net:8443/.well-known/openid-configuration}
     extra_hosts:
       - "idp.tests.shibboleth.net:host-gateway"
     ports:
diff --git a/src/test/docker/shib-tests-rp/etc/httpd/conf.modules.d/10-auth_openidc.conf b/src/test/docker/shib-tests-rp/etc/httpd/conf.modules.d/10-auth_openidc.conf
index 8c71e1b..2365614 100644
--- a/src/test/docker/shib-tests-rp/etc/httpd/conf.modules.d/10-auth_openidc.conf
+++ b/src/test/docker/shib-tests-rp/etc/httpd/conf.modules.d/10-auth_openidc.conf
@@ -1,7 +1,8 @@
 # Test configuration for mod_auth_openidc RP
 LoadModule auth_openidc_module modules/mod_auth_openidc.so
 
-OIDCProviderMetadataURL https://idp.tests.shibboleth.net:8443/.well-known/openid-configuration
+# OIDCProviderMetadataURL https://idp.tests.shibboleth.net:8443/.well-known/openid-configuration
+OIDCProviderMetadataURL ${OIDCProviderMetadataURL}
 OIDCClientID            test_oidc_rp
 OIDCClientSecret        topsecret
 
@@ -21,3 +22,8 @@ OIDCResponseType        id_token
    AuthType openid-connect
    Require valid-user
 </Location>
+
+<Location /cgi-bin/printenv>
+   AuthType openid-connect
+   Require valid-user
+</Location>
diff --git a/src/test/java/net/shibboleth/idp/integration/tests/BaseIntegrationTest.java b/src/test/java/net/shibboleth/idp/integration/tests/BaseIntegrationTest.java
index 95ac6ad..0702a20 100644
--- a/src/test/java/net/shibboleth/idp/integration/tests/BaseIntegrationTest.java
+++ b/src/test/java/net/shibboleth/idp/integration/tests/BaseIntegrationTest.java
@@ -3070,4 +3070,101 @@ public abstract class BaseIntegrationTest {
         logProcess(process, "Remove plugin :");
     }
 
+    /**
+     * Install plugin already downloaded to src/test/plugins.
+     * 
+     * @param plugin
+     *            the plugin id
+     * @param file
+     *            the plugin distribution
+     * @throws IOException
+     *             if an error occurs
+     */
+    public void installLocalPlugin(@Nonnull final String plugin, @Nonnull final String file) throws IOException {
+
+        // Path to the project build directory.
+        final Path buildPath = Paths.get(TEST_DISTRIBUTIONS_DIRECTORY);
+        assert buildPath.toAbsolutePath().toFile().exists() : "Path to build directory not found";
+
+        // Path to project directory
+        final Path project = buildPath.toAbsolutePath().toFile().getParentFile().toPath();
+        assert project.toAbsolutePath().toFile().exists() : "Path to project not found";
+
+        // Path to src/test/plugins
+        final Path pathToPlugins = project.resolve(Paths.get("src", "test", "plugins"));
+        assert pathToPlugins.toAbsolutePath().toFile().exists() : "Path to test plugins not found";
+
+        // Path to plugin
+        final Path pathToPlugin = pathToPlugins.resolve(file);
+        log.debug("Path to plugin '{}'", pathToPlugin.toAbsolutePath());
+        assert pathToPlugins.toAbsolutePath().toFile().exists() : "Path to plugin not found";
+
+        // Path to plugin truststore
+        final Path pathToTruststore = pathToTruststores.resolve(plugin).resolve("truststore.asc");
+        log.debug("Path to plugin truststore '{}'", pathToTruststore);
+        assert pathToTruststore.toAbsolutePath().toFile().exists() : "Path to plugin truststore not found";
+
+        final String truststore = pathToTruststore.toString();
+
+        final String[] commands = new String[] { //
+                pluginCLI, //
+                "--noRebuild", //
+                "--noPrompt", //
+                "--input", pathToPlugin.toString(), //
+                "--truststore ", truststore };
+
+        final Process process = new ProcessBuilder() //
+                .command(commands)
+                .directory(idpHome)
+                .start();
+
+        logProcess(process, "Install local plugin :");
+    }
+
+    /**
+     * Add module to Jetty.
+     * 
+     * @param module
+     *            the module to add
+     * @throws IOException
+     *             if an error occurs
+     */
+    protected void enableJettyModule(@Nonnull final String module) throws IOException {
+
+        // Path to Java, either idp.java.home or java.home system property
+        final String javaHome = System.getProperty("idp.java.home", System.getProperty("java.home"));
+
+        final Path pathToJava = Paths.get(javaHome, "bin", "java");
+
+        assert pathToJava.toFile().exists() : "Path to java not found";
+
+        final String javaCLI = pathToJava.toAbsolutePath().toString();
+
+        log.debug("Path to Java '{}'", javaCLI);
+
+        // Path to Jetty start.jar
+        final Path pathToStartJAR = pathToJettyHome.toAbsolutePath().resolve("start.jar");
+
+        assert pathToStartJAR.toFile().exists() : "Path to start.jar not found";
+
+        final String startCLI = pathToStartJAR.toAbsolutePath().toString();
+
+        log.debug("Path to start.jar '{}'", startCLI);
+
+        // Add module to Jetty
+        final String[] commands = new String[] { //
+                javaCLI, //
+                "-jar", //
+                startCLI, //
+                "--add-module=" + module };
+
+        final Process process = new ProcessBuilder() //
+                .command(commands)
+                .directory(pathToJettyBase.toAbsolutePath().toFile())
+                .redirectErrorStream(true)
+                .start();
+
+        logProcess(process, "Install Jetty '" + module + "' module :");
+    }
+
 }
diff --git a/src/test/java/net/shibboleth/idp/integration/tests/oidc/OIDCTest.java b/src/test/java/net/shibboleth/idp/integration/tests/oidc/OIDCTest.java
index 4635372..6ed13cf 100644
--- a/src/test/java/net/shibboleth/idp/integration/tests/oidc/OIDCTest.java
+++ b/src/test/java/net/shibboleth/idp/integration/tests/oidc/OIDCTest.java
@@ -17,6 +17,12 @@
 
 package net.shibboleth.idp.integration.tests.oidc;
 
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
@@ -24,11 +30,13 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testcontainers.containers.Container.ExecResult;
 import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
 import net.shibboleth.idp.integration.tests.BaseIntegrationTest;
 import net.shibboleth.idp.integration.tests.BrowserData;
+import net.shibboleth.shared.component.ComponentInitializationException;
 
 // WIP
 public class OIDCTest extends BaseIntegrationTest {
@@ -37,6 +45,14 @@ public class OIDCTest extends BaseIntegrationTest {
     @Nonnull
     private final Logger log = LoggerFactory.getLogger(OIDCTest.class);
 
+    /** CLI to generate keys. */
+    @Nullable
+    protected String jwtgenCLI;
+
+    /** RP Docker container. */
+    @Nullable
+    protected RPContainer rp = null;
+
     @BeforeClass
     public void setUpURLs() throws Exception {
 
@@ -44,6 +60,387 @@ public class OIDCTest extends BaseIntegrationTest {
 
     }
 
+    /**
+     * Enable OIDC credentials.
+     * 
+     * @throws IOException
+     *             if an error occurs
+     */
+    protected void enableOIDCCredentials() throws IOException {
+
+        final Path pathToCredentialsXML = pathToIdPHome.resolve(Paths.get("conf", "credentials.xml"));
+
+        assert pathToCredentialsXML.toAbsolutePath().toFile().exists() : "Path to credentials.xml not found";
+
+        final String oldText = "</beans>";
+
+        final StringBuilder newText = new StringBuilder();
+
+        newText.append("<!-- OIDC extension default credential definitions -->\n");
+
+        newText.append("<import resource=\"oidc-credentials.xml\" />\n");
+
+        newText.append(oldText + "\n");
+
+        replaceFile(pathToCredentialsXML, oldText, newText.toString());
+    }
+
+    /**
+     * Enable OIDC attributes / claim rules.
+     * 
+     * @throws IOException
+     *             if an error occurs
+     */
+    protected void enableOIDCAttributes() throws IOException {
+
+        final Path pathToDefaultRulesXML = pathToIdPHome.resolve(Paths.get("conf", "attributes", "default-rules.xml"));
+
+        assert pathToDefaultRulesXML.toAbsolutePath().toFile().exists() : "Path to default-rules.xml not found";
+
+        final String oldText = "</beans>";
+
+        final StringBuilder newText = new StringBuilder();
+
+        newText.append(" <import resource=\"oidc-claim-rules.xml\" />\n");
+
+        newText.append(oldText + "\n");
+
+        replaceFile(pathToDefaultRulesXML, oldText, newText.toString());
+    }
+
+    /**
+     * Generate OIDC credentials.
+     * 
+     * See : {@link #createOIDCCredentials1()} and {@link #createOIDCCredentials2()}
+     * and {@link #createOIDCCredentials3()}.
+     * 
+     * @throws IOException
+     *             if an error occurs
+     */
+    protected void createOIDCCredentials() throws IOException {
+
+        final Path pathToJWTGen = pathToBin.resolve(isWindows() ? "jwtgen.bat" : "jwtgen.sh");
+
+        assert pathToJWTGen.toFile().exists() : "Path to jwtgen not found";
+
+        jwtgenCLI = pathToJWTGen.toAbsolutePath().toString();
+
+        createOIDCCredentials1();
+
+        createOIDCCredentials2();
+
+        createOIDCCredentials3();
+    }
+
+    /**
+     * Generate idp-signing-rs.jwk
+     * 
+     * @throws IOException
+     *             if an error occurs
+     */
+    protected void createOIDCCredentials1() throws IOException {
+
+        final String[] commands = new String[] { //
+                jwtgenCLI, //
+                "--type", "RSA", //
+                "--size", "2048", //
+                "--usage", "sig", //
+                "--id", "defaultRSASign", //
+                "--output", "credentials/idp-signing-rs.jwk" };
+
+        final Process process = new ProcessBuilder() //
+                .command(commands)
+                .directory(idpHome)
+                .start();
+
+        logProcess(process, "Generate idp-signing-rs.jwk :");
+    }
+
+    /**
+     * Generate idp-signing-es.jwk
+     * 
+     * @throws IOException
+     *             if an error occurs
+     */
+    protected void createOIDCCredentials2() throws IOException {
+
+        final String[] commands = new String[] { //
+                jwtgenCLI, //
+                "--type", "EC", //
+                "--curve", "P-256", //
+                "--usage", "sig", //
+                "--id", "defaultECSign", //
+                "--output", "credentials/idp-signing-es.jwk" };
+
+        final Process process = new ProcessBuilder() //
+                .command(commands)
+                .directory(idpHome)
+                .start();
+
+        logProcess(process, "Generate idp-signing-es.jwk :");
+    }
+
+    /**
+     * Generate idp-encryption-rsa.jwk
+     * 
+     * @throws IOException
+     *             if an error occurs
+     */
+    protected void createOIDCCredentials3() throws IOException {
+
+        final String[] commands = new String[] { //
+                jwtgenCLI, //
+                "--type", "RSA", //
+                "--size", "2048", //
+                "--usage", "enc", //
+                "--id", "defaultRSAEnc", //
+                "--output", "credentials/idp-encryption-rsa.jwk" };
+
+        final Process process = new ProcessBuilder() //
+                .command(commands)
+                .directory(idpHome)
+                .start();
+
+        logProcess(process, "Generate idp-encryption-rsa.jwk :");
+    }
+
+    /**
+     * Add OIDC.Keyset and OIDC.Configuration to unverified relying party.
+     * 
+     * Add default OIDC profiles to default relying party.
+     * 
+     * @throws IOException
+     *             if an error occurs
+     */
+    protected void enableOIDCProfiles() throws IOException {
+
+        final Path pathToRelyingPartyXML = pathToIdPHome.resolve(Paths.get("conf", "relying-party.xml"));
+
+        assert pathToRelyingPartyXML.toAbsolutePath().toFile().exists() : "Path to relying-party.xml not found";
+
+        // Add <ref bean="OIDC.Keyset" /> to shibboleth.UnverifiedRelyingParty
+        // Add <ref bean="OIDC.Configuration" /> to shibboleth.UnverifiedRelyingParty
+
+        final String unverifiedRPText = "<!-- <bean parent=\"SAML2.SSO\" p:encryptAssertions=\"false\" /> -->";
+
+        final StringBuilder addToUnverified = new StringBuilder();
+        addToUnverified.append("<ref bean=\"OIDC.Keyset\" />\n");
+        addToUnverified.append("<ref bean=\"OIDC.Configuration\" />\n");
+
+        replaceFile(pathToRelyingPartyXML, unverifiedRPText, unverifiedRPText + "\n" + addToUnverified.toString());
+
+        // Add default OIDC profiles to default relying party
+
+        final String defaultRPText = "<ref bean=\"SAML2.ArtifactResolution\" />";
+
+        final StringBuilder addToDefault = new StringBuilder();
+        addToDefault.append("<ref bean=\"OIDC.SSO\" />\n");
+        addToDefault.append("<ref bean=\"OIDC.UserInfo\" />\n");
+        addToDefault.append("<ref bean=\"OAUTH2.Token\" />\n");
+        addToDefault.append("<ref bean=\"OAUTH2.Revocation\" />\n");
+        addToDefault.append("<ref bean=\"OAUTH2.Introspection\" />\n");
+
+        replaceFile(pathToRelyingPartyXML, defaultRPText, defaultRPText + "\n" + addToDefault.toString());
+    }
+
+    /**
+     * Enable client registration.
+     * 
+     * @throws IOException
+     *             if an error occurs
+     */
+    protected void registerRP1() throws IOException {
+
+        final Path pathToOIDCClientInfoResolversXML = pathToIdPHome
+                .resolve(Paths.get("conf", "oidc-clientinfo-resolvers.xml"));
+
+        assert pathToOIDCClientInfoResolversXML.toFile().exists() : "Path to oidc-clientinfo-resolvers.xml not found";
+
+        // Uncomment ExampleFileResolver
+        final String toUncomment = "<bean id=\"ExampleFileResolver\" parent=\"shibboleth.oidc.FilesystemClientInformationResolver\"\\\n"
+                + "        c:metadata=\"%\\{idp.home\\}/metadata/oidc-client.json\" />";
+
+        uncommentFile(pathToOIDCClientInfoResolversXML, toUncomment);
+
+        // Uncomment <ref bean="ExampleFileResolver" />
+        uncommentFile(pathToOIDCClientInfoResolversXML, "<ref bean=\"ExampleFileResolver\" />");
+    }
+
+    /**
+     * Register client by copying metadata/oidc-client.json.
+     * 
+     * @throws IOException
+     *             if an error occurs
+     */
+    protected void registerRP2() throws IOException {
+
+        final Path pathToOIDCClientJSON = Paths.get("src", "test", "oidc", "shibboleth-idp", "metadata",
+                "oidc-client.json");
+
+        assert pathToOIDCClientJSON.toFile().exists() : "Path to oidc-client.json not found";
+
+        final Path destPath = pathToIdPHome.resolve(Paths.get("metadata", "oidc-client.json"));
+
+        Files.copy(pathToOIDCClientJSON, destPath, StandardCopyOption.REPLACE_EXISTING);
+    }
+
+    /**
+     * Set up static/openid-configuration.json.
+     * 
+     * Set up dynamic publication by enabling the Jetty 'rewrite' module.
+     * 
+     * @throws IOException
+     *             if an error occurs
+     */
+    // TODO Jetty only
+    protected void enableOIDCDiscovery() throws IOException {
+
+        // static/openid-configuration.json
+        final Path pathToOpenIDConfiguration = pathToIdPHome.resolve(Paths.get("static", "openid-configuration.json"));
+
+        assert pathToOpenIDConfiguration.toFile().exists() : "Path to openid-configuration.json not found";
+
+        // Replace service_name with IdP's FQDN and port
+        final String serviceName = "idp.tests.shibboleth.net:" + securePort;
+
+        replaceFile(pathToOpenIDConfiguration, "\\{\\{ service_name \\}\\}", serviceName);
+
+        // Remove port from issuer URL
+        replaceFile(pathToOpenIDConfiguration, //
+                "\"issuer\":\"https://idp.tests.shibboleth.net:" + securePort + "\"", //
+                "\"issuer\":\"https://idp.tests.shibboleth.net\"");
+
+        enableJettyModule("rewrite");
+
+        final Path pathToSourceRewriteRulesXML = Paths.get("src", "test", "oidc", "jetty-base", "etc",
+                "rewrite-rules.xml");
+
+        assert pathToSourceRewriteRulesXML.toFile().exists() : "Path to rewrite-rules.xml not found";
+
+        final Path pathToTargetRewriteRulesXML = pathToJettyBase.resolve(Paths.get("etc", "rewrite-rules.xml"));
+
+        Files.copy(pathToSourceRewriteRulesXML, pathToTargetRewriteRulesXML, StandardCopyOption.REPLACE_EXISTING);
+    }
+
+    /**
+     * Use example OIDC attribute resolver and filter.
+     * 
+     * @throws IOException
+     */
+    protected void setUpIdPAttributes() throws IOException {
+
+        Files.move( //
+                pathToIdPHome.resolve(Paths.get("conf", "examples", "oidc-attribute-resolver.xml")), //
+                pathToIdPHome.resolve(Paths.get("conf", "attribute-resolver.xml")), //
+                StandardCopyOption.REPLACE_EXISTING);
+
+        Files.move(//
+                pathToIdPHome.resolve(Paths.get("conf", "examples", "oidc-attribute-filter.xml")), //
+                pathToIdPHome.resolve(Paths.get("conf", "attribute-filter.xml")), //
+                StandardCopyOption.REPLACE_EXISTING);
+    }
+
+    /**
+     * Set the Open ID Connect issuer property.
+     * 
+     * @throws IOException
+     *             if an error occurs
+     */
+    protected void setUpOPIssuer() throws IOException {
+
+        final Path pathToOIDCProperties = pathToIdPHome.resolve(Paths.get("conf", "oidc.properties"));
+
+        assert pathToOIDCProperties.toFile().exists() : "Path to oidc.properties not found";
+
+        replaceFile(pathToOIDCProperties, "https://your.issuer.example.org", "https://idp.tests.shibboleth.net");
+    }
+
+    /**
+     * Start RP.
+     * 
+     * @throws ComponentInitializationException if an error occurs
+     */
+    protected void startRP() throws ComponentInitializationException {
+
+        rp = new RPContainer();
+
+        rp.setId("rp.tests.shibboleth.net");
+
+        rp.setOPPort(securePort.toString());
+
+        rp.initialize();
+
+        rp.start();
+    }
+
+    /**
+     * Update client port in their metadata.
+     * 
+     * @param port
+     *            the client port
+     * @throws IOException
+     *             if an error occurs
+     */
+    protected void updateClientPort(@Nonnull final String port) throws IOException {
+        final Path pathToOIDCClientJSON = pathToIdPHome.resolve(Paths.get("metadata", "oidc-client.json"));
+
+        assert pathToOIDCClientJSON.toFile().exists() : "Path to oidc-client.json not found";
+
+        replaceFile(pathToOIDCClientJSON, "40443", port);
+    }
+
+    /**
+     * Get /.well-known/openid-configuration and verify that the issuer is correct.
+     */
+    protected void getOpenIDConfigurationFromBrowser() {
+
+        final String baseURL = "https://idp.tests.shibboleth.net:" + securePort;
+
+        final String openidConfigurationURL = baseURL + "/.well-known/openid-configuration";
+
+        log.debug("Attempting to get openid-configuration URL '{}'", openidConfigurationURL);
+
+        driver.get(openidConfigurationURL);
+
+        final String pageSource = getPageSource();
+
+        // TODO why quoted / ?
+        final String expectedIssuer = "\"issuer\":\"https:\\/\\/idp.tests.shibboleth.net\"";
+
+        Assert.assertTrue(pageSource.contains(expectedIssuer), "Expected issuer not found");
+    }
+
+    /**
+     * Get /.well-known/openid-configuration and verify that the issuer is correct.
+     * 
+     * @throws InterruptedException
+     * @throws IOException
+     * @throws UnsupportedOperationException
+     */
+    protected void getOpenIDConfigurationFromContainer()
+            throws UnsupportedOperationException, IOException, InterruptedException {
+
+        final String baseURL = "https://idp.tests.shibboleth.net:" + securePort;
+
+        final String openidConfigurationURL = baseURL + "/.well-known/openid-configuration";
+
+        log.debug("Attempting to get openid-configuration URL '{}'", openidConfigurationURL);
+
+        final ExecResult result = rp.container.execInContainer( //
+                "curl", //
+                "-vvv", //
+                openidConfigurationURL);
+
+        final String response = result.getStdout();
+
+        log.debug("Response openid-configuration from container\n{}\n", response);
+
+        // TODO why quoted / ?
+        final String expectedIssuer = "\"issuer\":\"https:\\/\\/idp.tests.shibboleth.net\"";
+
+        Assert.assertTrue(response.contains(expectedIssuer), "Expected issuer not found");
+    }
+
     @Test(dataProvider = "sauceOnDemandBrowserDataProvider", enabled = false)
     public void testRP(@Nullable final BrowserData browserData) throws Exception {
 
@@ -83,10 +480,92 @@ public class OIDCTest extends BaseIntegrationTest {
         final String pageSource = getPageSource();
 
         log.debug("Print env from browser\n{}\n", pageSource);
-        
+
         Assert.assertTrue(pageSource.contains(expectedServerNameFromEnv), "Expected ServerName not found");
 
         rp.stop();
     }
 
+    @Test(dataProvider = "sauceOnDemandBrowserDataProvider", enabled = false)
+    public void testAuthn(@Nullable final BrowserData browserData) throws Exception {
+
+        // Install OIDC OP plugin
+
+        // TODO local install
+        installLocalPlugin("net.shibboleth.oidc.common", "oidc-common-dist-3.0.1.tar.gz");
+        installLocalPlugin("net.shibboleth.idp.plugin.oidc.config", "idp-plugin-oidc-config-dist-2.0.0.tar.gz");
+        installLocalPlugin("net.shibboleth.idp.plugin.oidc.op", "idp-plugin-oidc-op-distribution-4.0.0.tar.gz");
+
+        // TODO remote install
+        final String[] plugins = new String[] { //
+                "net.shibboleth.oidc.common", //
+                "net.shibboleth.idp.plugin.oidc.config", //
+                "net.shibboleth.idp.plugin.oidc.op" };
+        // installPlugins(plugins);
+
+        assertPluginsAreInstalled(plugins);
+
+        buildWAR();
+
+        enableOIDCCredentials();
+
+        enableOIDCAttributes();
+
+        createOIDCCredentials();
+
+        enableOIDCProfiles();
+
+        registerRP1();
+
+        registerRP2();
+
+        enableOIDCDiscovery();
+
+        setUpIdPAttributes();
+
+        setUpOPIssuer();
+
+        startRP();
+
+        updateClientPort(rp.httpsPort.toString());
+
+        startServer();
+
+        assertPluginsAreOnStatusPage(plugins);
+
+        secureBaseURL = "https://idp.tests.shibboleth.net:" + securePort;
+
+        startSeleniumClient(browserData);
+
+        getOpenIDConfigurationFromBrowser();
+
+        getOpenIDConfigurationFromContainer();
+
+        driver.get(rp.getBaseURL() + "/secure");
+
+        waitForLoginPage();
+
+        login();
+
+        waitForPageURLContains("/secure");
+        
+        Assert.assertTrue(getPageSource().contains("Secure"));
+
+        driver.get(rp.getBaseURL() + "/cgi-bin/printenv");
+
+        Assert.assertTrue(getPageSource().contains("OIDC_CLAIM_iss=\"https://idp.tests.shibboleth.net\""));
+
+        // TODO more asserts
+    }
+
+    /**
+     * Stop the server.
+     */
+    @AfterMethod(dependsOnMethods = { "failTestClass" })
+    public void stopRP() {
+        if (rp != null && rp.isRunning()) {
+            rp.stop();
+        }
+    }
+
 }
diff --git a/src/test/java/net/shibboleth/idp/integration/tests/oidc/RPContainer.java b/src/test/java/net/shibboleth/idp/integration/tests/oidc/RPContainer.java
index bf3030e..3baaaea 100644
--- a/src/test/java/net/shibboleth/idp/integration/tests/oidc/RPContainer.java
+++ b/src/test/java/net/shibboleth/idp/integration/tests/oidc/RPContainer.java
@@ -35,6 +35,7 @@ import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
 import net.shibboleth.shared.annotation.constraint.NotEmpty;
 import net.shibboleth.shared.component.AbstractIdentifiableInitializableComponent;
 import net.shibboleth.shared.component.ComponentInitializationException;
+import net.shibboleth.shared.logic.Constraint;
 
 /**
  * Start and stop an OpenID Connect Relying Party (RP) Docker container.
@@ -78,6 +79,10 @@ public class RPContainer extends AbstractIdentifiableInitializableComponent impl
     @NonnullAfterInit
     private Path pathToRP;
 
+    /** Port used by the OP. */
+    @NonnullAfterInit
+    private String opPort;
+
     /** System property to set path to TLS cert. */
     final static String pathToTLSCertSystemProperty = "tlsCert";
 
@@ -191,6 +196,8 @@ public class RPContainer extends AbstractIdentifiableInitializableComponent impl
      * 
      * Set container name and hostname to id.
      * 
+     * Set OIDCProviderMetadataURL using OP port.
+     * 
      * Expose ports 80 and 443.
      * 
      * {@inheritDoc}
@@ -200,6 +207,10 @@ public class RPContainer extends AbstractIdentifiableInitializableComponent impl
 
         super.doInitialize();
 
+        if (opPort == null) {
+            throw new ComponentInitializationException("OP port cannot be null");
+        }
+
         log.debug("{} Initializing", getLogPrefix());
 
         // Do not delete image on exit
@@ -236,6 +247,11 @@ public class RPContainer extends AbstractIdentifiableInitializableComponent impl
         // Set 'ServerName' environment variable to the id
         container.withEnv("ServerName", getId());
 
+        // set OIDCProviderMetadataURL
+        final String OIDCProviderMetadataURL = //
+                "https://idp.tests.shibboleth.net:" + opPort + "/.well-known/openid-configuration";
+        container.withEnv("OIDCProviderMetadataURL", OIDCProviderMetadataURL);
+
         // Expose ports 80 and 443
         container.addExposedPort(80);
         container.addExposedPort(443);
@@ -243,6 +259,15 @@ public class RPContainer extends AbstractIdentifiableInitializableComponent impl
         log.debug("{} Initialized", getLogPrefix());
     }
 
+    /**
+     * Get base URL of the form "https://rp.tests.shibboleth.net:<port>".
+     * 
+     * @return base URL
+     */
+    public String getBaseURL() {
+        return "https://rp.tests.shibboleth.net:" + httpsPort.toString();
+    }
+
     /**
      * Return a prefix for logging messages for this component.
      * 
@@ -258,6 +283,17 @@ public class RPContainer extends AbstractIdentifiableInitializableComponent impl
         return logPrefix;
     }
 
+    /**
+     * Set the OP port used to construct the OIDCProviderMetadataURL.
+     * 
+     * @param port
+     *            OP port
+     */
+    public void setOPPort(@Nonnull final String port) {
+        Constraint.isNotNull(port, "OP port cannot be null");
+        opPort = port;
+    }
+
     /**
      * Start container and wait for web server to be available.
      * 
diff --git a/src/test/oidc/jetty-base/etc/rewrite-rules.xml b/src/test/oidc/jetty-base/etc/rewrite-rules.xml
new file mode 100644
index 0000000..bcaf376
--- /dev/null
+++ b/src/test/oidc/jetty-base/etc/rewrite-rules.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_10_0.dtd">
+<Configure id="Rewrite" class="org.eclipse.jetty.rewrite.handler.RuleContainer">
+
+    <!-- OIDC Discovery -->
+    <Call name="addRule">
+        <Arg>
+            <New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
+                <Set name="pattern">/.well-known/openid-configuration</Set>
+                <Set name="replacement">/idp/profile/oidc/configuration</Set>
+            </New>
+        </Arg>
+    </Call>
+
+</Configure>
+
diff --git a/src/test/oidc/shibboleth-idp/metadata/oidc-client.json b/src/test/oidc/shibboleth-idp/metadata/oidc-client.json
new file mode 100644
index 0000000..996fd9c
--- /dev/null
+++ b/src/test/oidc/shibboleth-idp/metadata/oidc-client.json
@@ -0,0 +1,13 @@
+[
+  {
+    "scope": "openid email",
+    "redirect_uris": [ "https://rp.tests.shibboleth.net:40443/redirect_uri" ],
+    "client_id": "test_oidc_rp",
+    "client_secret": "topsecret",
+    "response_types": [
+      "code",
+      "id_token",
+      "id_token token"
+    ]
+  }
+]
diff --git a/src/test/plugins/.gitignore b/src/test/plugins/.gitignore
new file mode 100644
index 0000000..d6b7ef3
--- /dev/null
+++ b/src/test/plugins/.gitignore
@@ -0,0 +1,2 @@
+*
+!.gitignore

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the commits mailing list