[java-idp-integration-tests] 01/02: Add invalid record and pruning consent tests for Postgres.

Tom Zeller tzeller at dragonacea.biz
Tue Jun 3 23:22:27 UTC 2025


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=3d8ea0c04d1e27fe06c73c3ead043667b4a83dce

commit 3d8ea0c04d1e27fe06c73c3ead043667b4a83dce
Author: Tom Zeller <tzeller at dragonacea.biz>
AuthorDate: Tue Jun 3 18:01:48 2025 -0500

    Add invalid record and pruning consent tests for Postgres.
    
    https://shibboleth.atlassian.net/browse/IDP-2323
---
 .../shibboleth/idp/integration/tests/BaseTest.java |  72 +++++
 .../tests/consent/BasePostgresConsentTest.java     | 334 +++++++++++++++++++++
 .../tests/consent/PostgresConsentTest.java         | 209 ++++---------
 .../consent/PruneRecordsPostgresConsentTest.java   | 124 ++++++++
 .../tests/util/TestbedStorageServiceClient.java    | 193 ++++++++++++
 5 files changed, 777 insertions(+), 155 deletions(-)

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 5e4e0ca..82ee0fa 100644
--- a/src/test/java/net/shibboleth/idp/integration/tests/BaseTest.java
+++ b/src/test/java/net/shibboleth/idp/integration/tests/BaseTest.java
@@ -54,8 +54,10 @@ import java.util.Random;
 import java.util.Set;
 import java.util.SortedSet;
 import java.util.TreeSet;
+import java.util.function.IntConsumer;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
+import java.util.stream.IntStream;
 import java.util.stream.Stream;
 
 import javax.annotation.Nonnull;
@@ -91,8 +93,10 @@ import org.openqa.selenium.support.ui.ExpectedConditions;
 import org.openqa.selenium.support.ui.WebDriverWait;
 import org.opensaml.core.config.InitializationException;
 import org.opensaml.core.config.InitializationService;
+import org.opensaml.core.criterion.EntityIdCriterion;
 import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
 import org.opensaml.core.xml.io.UnmarshallerFactory;
+import org.opensaml.saml.metadata.resolver.impl.DefaultLocalDynamicSourceKeyGenerator;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.core.io.FileSystemResource;
@@ -123,6 +127,7 @@ import net.shibboleth.shared.httpclient.HttpClientBuilder;
 import net.shibboleth.shared.logic.Constraint;
 import net.shibboleth.shared.net.URLBuilder;
 import net.shibboleth.shared.primitive.StringSupport;
+import net.shibboleth.shared.resolver.CriteriaSet;
 import net.shibboleth.shared.xml.ParserPool;
 
 
@@ -3556,4 +3561,71 @@ public abstract class BaseTest {
         logProcess(process, "Download artifact :");
     }
 
+    /**
+     * Set up local dynamic metadata provider using idp.home/dynamic/metadata source
+     * directory.
+     * 
+     * @throws Exception
+     *             if an error occurs
+     */
+    public void setUpLocalDynamicMetadataProvider() throws Exception {
+        final Path pathToMetadataProvidersXML = Paths.get("conf", "metadata-providers.xml");
+        final String oldText = "</MetadataProvider>";
+        final String newText = "<MetadataProvider id=\"LocalDynamic\" xsi:type=\"LocalDynamicMetadataProvider\" sourceDirectory=\"%{idp.home}/metadata/dynamic\" />";
+        replaceIdPHomeFile(pathToMetadataProvidersXML, oldText, newText + System.lineSeparator() + oldText);
+    }
+
+    /**
+     * Set up local dynamic metadata in idp.home/dynamic/metadata source directory.
+     * 
+     * Copies metadata/example-metadata.xml and replaces entityID and endpoint URLs.
+     * 
+     * @param numberToCreate
+     *            number of SPs to create
+     * @throws Exception
+     *             if an error occurs
+     */
+    public void setUpLocalDynamicSPMetadata(int numberToCreate) throws Exception {
+
+        final Path pathToMetadataDirectory = pathToIdPHome.resolve("metadata");
+
+        final Path pathToExampleMetadata = pathToMetadataDirectory.resolve("example-metadata.xml");
+
+        final Path pathToDynamicMetadataDirectory = pathToMetadataDirectory.resolve("dynamic");
+
+        Files.createDirectory(pathToDynamicMetadataDirectory);
+
+        final DefaultLocalDynamicSourceKeyGenerator gen = new DefaultLocalDynamicSourceKeyGenerator(null, ".xml", null);
+
+        final IntConsumer intConsumer = value -> {
+
+            final String entityID = "https://sp" + value + ".example.org";
+
+            final String metadataFileName = gen.apply(new CriteriaSet(new EntityIdCriterion(entityID)));
+
+            final Path pathToMetadataFile = pathToDynamicMetadataDirectory.resolve(metadataFileName);
+
+            try {
+                log.debug("Creating example metadata for {} to {}", entityID, pathToMetadataFile);
+                Files.copy(pathToExampleMetadata, pathToMetadataFile);
+
+                // Replace entityID
+                replaceFile(pathToMetadataFile, "entityID=\"https://sp.example.org\"", "entityID=\"" + entityID + "\"");
+
+                // Replace text
+                replaceFile(pathToMetadataFile, " SP ", " SP" + value + " ");
+
+                // Replace URLs
+                replaceFile(pathToMetadataFile, "/sp/", "/sp/sp" + value + "/");
+            } catch (final IOException e) {
+                log.error("Unable to copy example metadata to {}", pathToMetadataFile, e);
+                throw new RuntimeException(e);
+            }
+        };
+
+        IntStream.range(1, numberToCreate + 1).forEach(intConsumer);
+
+        setUpLocalDynamicMetadataProvider();
+    }
+
 }
diff --git a/src/test/java/net/shibboleth/idp/integration/tests/consent/BasePostgresConsentTest.java b/src/test/java/net/shibboleth/idp/integration/tests/consent/BasePostgresConsentTest.java
new file mode 100644
index 0000000..bf69eba
--- /dev/null
+++ b/src/test/java/net/shibboleth/idp/integration/tests/consent/BasePostgresConsentTest.java
@@ -0,0 +1,334 @@
+/*
+ * 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.consent;
+
+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.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.saml.saml2.core.AuthnContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testcontainers.containers.PostgreSQLContainer;
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
+
+import net.shibboleth.idp.integration.tests.saml2.AbstractSAML2IntegrationTest;
+import net.shibboleth.idp.integration.tests.util.TestbedStorageServiceClient;
+import net.shibboleth.idp.test.flows.saml2.SAML2TestResponseValidator;
+import net.shibboleth.shared.collection.CollectionSupport;
+import net.shibboleth.shared.collection.LazyList;
+
+/** Base class to test consent with a Postgres storage service. */
+public abstract class BasePostgresConsentTest extends AbstractSAML2IntegrationTest {
+
+    /** Class logger. */
+    @Nonnull
+    private final Logger log = LoggerFactory.getLogger(BasePostgresConsentTest.class);
+
+    /** Postgres container. */
+    private PostgreSQLContainer<?> postgresContainer;
+
+    /** Postgres connection URL from container. */
+    private String jdbcUrl;
+
+    @BeforeClass
+    public void setUpURLs() throws Exception {
+
+        startFlowURLPath = "/sp/SAML2/InitSSO/Redirect";
+
+        loginPageURLPath = "/idp/profile/SAML2/Redirect/SSO";
+
+        responsePageURLPath = "/sp/SAML2/POST/ACS";
+
+        isPassiveRequestURLPath = "/sp/SAML2/InitSSO/Passive";
+
+        forceAuthnRequestURLPath = "/sp/SAML2/InitSSO/ForceAuthn";
+
+        idpLogoutURLPath = "/idp/profile/SAML2/Redirect/SLO";
+
+        spLogoutURLPath = "/sp/SAML2/Redirect/SLO";
+
+        logoutTransientIDInputID = "InitSLO_Redirect";
+    }
+
+    /**
+     * Set up Postgres storage service.
+     * 
+     * Install JDBC Plugin.
+     * 
+     * Add testbed storage servlet to the IdP.
+     * 
+     * Add Postgres and HikariCP drivers to the IdP.
+     * 
+     * @throws IOException
+     *             if an error occurs
+     */
+    @BeforeClass
+    public void setUpPostgresStorageService() throws IOException {
+
+        installPlugin("net.shibboleth.plugin.storage.jdbc");
+
+        setUpStorageServlet();
+
+        downloadPostgresqlDriver();
+
+        downloadHikariCPDriver();
+
+        buildWAR();
+
+        enablePostgresStorageService();
+
+        replaceIdPProperty("idp.consent.StorageService", "my.JDBCStorageService");
+
+        enableAmbiguousPathSeparator();
+
+        // server.getAdditionalCommands().add("-Didp.loglevel.spring=DEBUG");
+    }
+
+    /**
+     * Build local dynamic SP response validator.
+     * 
+     * @throws IOException
+     *             if an I/O error occurs
+     */
+    public SAML2TestResponseValidator buildLocalDynamicSpValidator(@Nonnull final String spId) throws IOException {
+        final SAML2TestResponseValidator validator = new SAML2TestResponseValidator();
+        validator.spEntityID = "https://" + spId + ".example.org";
+        validator.nameID.setSPNameQualifier("https://" + spId + ".example.org");
+        validator.spCredential = getSPCredential();
+        validator.authnContextClassRef = AuthnContext.PPT_AUTHN_CTX;
+        validator.expectedAttributes.clear();
+        validator.expectedAttributes.add(validator.eppnAttribute);
+        validator.expectedAttributes.add(validator.homeOrgAttribute);
+        validator.expectedAttributes.add(validator.mailAttribute);
+        return validator;
+    }
+
+    /**
+     * Download latest Postgres driver to edit-webapp/WEB-INF/lib.
+     * 
+     * @throws IOException
+     *             if an error occurs
+     */
+    public void downloadPostgresqlDriver() throws IOException {
+        copyDependency("org.postgresql", "postgresql");
+    }
+
+    /**
+     * Download latest HikariCP driver to edit-webapp/WEB-INF/lib.
+     * 
+     * @throws IOException
+     *             if an error occurs
+     */
+    public void downloadHikariCPDriver() throws IOException {
+        copyDependency("com.zaxxer", "HikariCP");
+    }
+
+    /**
+     * Enable JDBC storage using Postgres and HikariCP.
+     * 
+     * Enables beans from
+     * "/net/shibboleth/idp/integration/tests/postgres/global.xml".
+     * 
+     * @throws IOException
+     *             if an error occurs
+     * @throws URISyntaxException
+     *             if an error occurs
+     */
+    public void enablePostgresStorageService() throws IOException {
+        try {
+            final Path pathToGlobalXML = pathToIdPHome.resolve(Paths.get("conf", "global.xml"));
+
+            assert pathToGlobalXML.toAbsolutePath().toFile().exists() : "Path to global.xml not found";
+
+            final String oldText = "</beans>";
+
+            final String resourcePath = "/net/shibboleth/idp/integration/tests/postgres/global.xml";
+
+            final Path pathToSnippetForGlobalXML = Paths.get(this.getClass().getResource(resourcePath).toURI());
+
+            final String newText = Files.readString(pathToSnippetForGlobalXML);
+
+            replaceFile(pathToGlobalXML, oldText, newText + "\n" + oldText);
+        } catch (URISyntaxException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Enable ambiguous path separator in Jetty for testbed storage service client.
+     * 
+     * @throws IOException
+     *             of an error occurs
+     */
+    public void enableAmbiguousPathSeparator() throws IOException {
+        if (Boolean.getBoolean("tomcat")) {
+            return;
+        } else {
+            final Path idpIni = pathToJettyBase.resolve(Paths.get("start.d", "idp.ini"));
+            replaceFile(idpIni, "\\z",
+                    System.lineSeparator() + "jetty.httpConfig.uriCompliance=DEFAULT,AMBIGUOUS_PATH_SEPARATOR");
+        }
+    }
+
+    /**
+     * Log in to local dynamic SP with given id.
+     * 
+     * @param spId
+     *            the numerical id of the SP
+     * @throws Exception
+     *             if an error occurs
+     */
+    public void loginToLocalDynamicSP(@Nonnull final String spId) throws Exception {
+
+        log.debug("Log in to SP '{}'", spId);
+
+        // login to sp
+
+        driver.get(getBaseURL() + "/sp/" + spId + "/SAML2/InitSSO/Redirect");
+
+        // attribute release
+
+        waitForAttributeReleasePage();
+
+        releaseAllAttributes();
+
+        rememberConsent();
+
+        submitForm();
+
+        // response
+
+        responsePageURLPath = "/sp/" + spId + "/SAML2/POST/ACS";
+
+        waitForResponsePage();
+
+        buildLocalDynamicSpValidator(spId).validateResponse(unmarshallResponse(getPageSource()));
+    }
+
+    /**
+     * Query database for a list of storage record rows represented as a map with
+     * column names as keys.
+     * 
+     * @return list of rows represented as a map with column names as keys
+     * @throws SQLException
+     *             if an error occurs
+     */
+    public List<Map<String, String>> queryDatabase() throws SQLException {
+        final String context = "intercept/attribute-release";
+        final String query = "SELECT * from storagerecords";
+        final List<String> columns = CollectionSupport.listOf("context", "id", "expires", "value");
+        final List<Map<String, String>> results = new LazyList<>();
+        log.debug("Query for '{}' records from '{}'", context, jdbcUrl);
+        try (final Connection conn = DriverManager.getConnection(jdbcUrl, "test", "test")) {
+            final Statement statement = conn.createStatement();
+            try (final ResultSet resultSet = statement.executeQuery(query)) {
+                while (resultSet.next()) {
+                    if (resultSet.getString("context").equals(context)) {
+                        final Map<String, String> row = new HashMap<>(columns.size());
+                        for (final String column : columns) {
+                            row.put(column, resultSet.getString(column));
+                        }
+                        results.add(row);
+                        log.trace("Found row {}", row);
+                    }
+                }
+            }
+        } catch (final SQLException e) {
+            log.error("Unable to query database '{}'", jdbcUrl, e);
+            throw e;
+        }
+        return results;
+    }
+
+    /**
+     * Start Postgres container.
+     * 
+     * Creates "StorageRecords" table.
+     * 
+     * @return the JDBC URL to connect to the Postgres container
+     * @throws IOException 
+     */
+    @SuppressWarnings("resource")
+    @BeforeMethod()
+    public void startPostgres() throws IOException {
+
+        final String initScriptPath = "net/shibboleth/idp/integration/tests/postgres/create-table.sql";
+
+        postgresContainer = new PostgreSQLContainer<>("postgres").withInitScript(initScriptPath);
+
+        log.debug("{} Starting Postgres ...");
+        postgresContainer.start();
+        log.debug("{} Started Postgres : '{}'", postgresContainer.getContainerName());
+
+        jdbcUrl = postgresContainer.getJdbcUrl();
+        log.info("{} Postgres JDBC URL : '{}'", jdbcUrl);
+
+        replaceIdPHomeFile(Paths.get("conf", "global.xml"), "jdbc:postgresql://localhost:\\d+/test\\?loggerLevel=OFF", jdbcUrl);
+    }
+
+    /**
+     * Stop Postgres.
+     */
+    @AfterMethod()
+    public void stopPostgres() {
+        if (postgresContainer != null) {
+            log.debug("{} Stopping Postgres ...");
+            postgresContainer.stop();
+            log.debug("{} Postgres stopped");
+            postgresContainer.close();
+        }
+    }
+
+    /**
+     * Write invalid JSON to a storage record value using testbed storage service
+     * wrapper to the IdP.
+     * 
+     * @throws IOException
+     *             if an error occurs
+     */
+    public void writeInvalidDataToIndexRecord() throws IOException {
+
+        final TestbedStorageServiceClient storageServiceClient = new TestbedStorageServiceClient() //
+                .setBaseUrl(getBaseURL(true)) //
+                .setStorageServiceId("my.JDBCStorageService") //
+                .setContext("intercept/attribute-release") //
+                .setKey("jdoe:_key_idx") //
+                .setValue("not JSON");
+
+        boolean result = storageServiceClient.create();
+
+        Assert.assertTrue(result);
+    }
+
+}
diff --git a/src/test/java/net/shibboleth/idp/integration/tests/consent/PostgresConsentTest.java b/src/test/java/net/shibboleth/idp/integration/tests/consent/PostgresConsentTest.java
index 03dafeb..c2e4610 100644
--- a/src/test/java/net/shibboleth/idp/integration/tests/consent/PostgresConsentTest.java
+++ b/src/test/java/net/shibboleth/idp/integration/tests/consent/PostgresConsentTest.java
@@ -17,203 +17,102 @@
 
 package net.shibboleth.idp.integration.tests.consent;
 
-import java.io.IOException;
-import java.net.URISyntaxException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-
 import javax.annotation.Nonnull;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.testcontainers.containers.PostgreSQLContainer;
-import org.testng.annotations.AfterMethod;
-import org.testng.annotations.BeforeClass;
-import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
-import net.shibboleth.idp.integration.tests.saml2.AbstractSAML2IntegrationTest;
 import net.shibboleth.idp.integration.tests.util.testng.annotation.LinuxOnly;
 
 /** Test consent with a Postgres storage service. */
-public class PostgresConsentTest extends AbstractSAML2IntegrationTest {
+public class PostgresConsentTest extends BasePostgresConsentTest {
 
     /** Class logger. */
     @Nonnull
     private final Logger log = LoggerFactory.getLogger(PostgresConsentTest.class);
 
-    /** Postgres container. */
-    private PostgreSQLContainer<?> postgresContainer;
-
-    @BeforeClass
-    public void setUpURLs() throws Exception {
-
-        startFlowURLPath = "/sp/SAML2/InitSSO/Redirect";
-
-        loginPageURLPath = "/idp/profile/SAML2/Redirect/SSO";
-
-        responsePageURLPath = "/sp/SAML2/POST/ACS";
-
-        isPassiveRequestURLPath = "/sp/SAML2/InitSSO/Passive";
-
-        forceAuthnRequestURLPath = "/sp/SAML2/InitSSO/ForceAuthn";
-
-        idpLogoutURLPath = "/idp/profile/SAML2/Redirect/SLO";
-
-        spLogoutURLPath = "/sp/SAML2/Redirect/SLO";
-
-        logoutTransientIDInputID = "InitSLO_Redirect";
+    @Test
+    @LinuxOnly
+    public void testSSOReleaseAllAttributes() throws Exception {
+        super.testSSOReleaseAllAttributes();
     }
 
-    /**
-     * Set up Postgres storage service.
-     * 
-     * Install JDBC Plugin.
-     * 
-     * Add testbed storage servlet to the IdP.
-     * 
-     * Add Postgres and HikariCP drivers to the IdP.
-     * 
-     * @throws IOException
-     *             if an error occurs
-     */
-    @BeforeClass
-    public void setUpPostgresStorageService() throws IOException  {
-
-        installPlugin("net.shibboleth.plugin.storage.jdbc");
-
-        setUpStorageServlet();
-
-        downloadPostgresqlDriver();
-
-        downloadHikariCPDriver();
-
-        buildWAR();
-
-        enablePostgresStorageService();
+    @Test
+    @LinuxOnly
+    public void testSSOReleaseOneAttribute() throws Exception {
+        super.testSSOReleaseOneAttribute();
+    }
 
-        replaceIdPProperty("idp.consent.StorageService", "my.JDBCStorageService");
+    @Test
+    @LinuxOnly
+    public void testSSODoNotRememberConsent() throws Exception {
+        super.testSSODoNotRememberConsent();
     }
 
-    /**
-     * Download latest Postgres driver to edit-webapp/WEB-INF/lib.
-     * 
-     * @throws IOException
-     *             if an error occurs
-     */
-    public void downloadPostgresqlDriver() throws IOException {
-        copyDependency("org.postgresql", "postgresql");
+    @Test
+    @LinuxOnly
+    public void testSSOGlobalConsent() throws Exception {
+        super.testSSOGlobalConsent();
     }
 
-    /**
-     * Download latest HikariCP driver to edit-webapp/WEB-INF/lib.
-     * 
-     * @throws IOException
-     *             if an error occurs
-     */
-    public void downloadHikariCPDriver() throws IOException {
-        copyDependency("com.zaxxer", "HikariCP");
+    @Test
+    @LinuxOnly
+    public void testSSOTermsOfUse() throws Exception {
+        super.testSSOTermsOfUse();
     }
 
-    /**
-     * Enable JDBC storage using Postgres and HikariCP.
-     * 
-     * Enables beans from
-     * "/net/shibboleth/idp/integration/tests/postgres/global.xml".
-     * 
-     * @throws IOException
-     *             if an error occurs
-     * @throws URISyntaxException
-     *             if an error occurs
-     */
-    public void enablePostgresStorageService() throws IOException {
-        try {
-            final Path pathToGlobalXML = pathToIdPHome.resolve(Paths.get("conf", "global.xml"));
+    @Test
+    @LinuxOnly
+    public void testInvalidData() throws Exception {
 
-            assert pathToGlobalXML.toAbsolutePath().toFile().exists() : "Path to global.xml not found";
+        startServer();
 
-            final String oldText = "</beans>";
+        writeInvalidDataToIndexRecord();
 
-            final String resourcePath = "/net/shibboleth/idp/integration/tests/postgres/global.xml";
+        startBrowser();
 
-            final Path pathToSnippetForGlobalXML = Paths.get(this.getClass().getResource(resourcePath).toURI());
+        startFlow();
 
-            final String newText = Files.readString(pathToSnippetForGlobalXML);
+        waitForLoginPage();
 
-            replaceFile(pathToGlobalXML, oldText, newText + "\n" + oldText);
+        login();
 
-        } catch (URISyntaxException e) {
-            throw new RuntimeException(e);
-        }
-    }
+        // attribute release
 
-    /**
-     * Start Postgres container.
-     * 
-     * Creates "StorageRecords" table.
-     * 
-     * @return the JDBC URL to connect to the Postgres container
-     * @throws IOException 
-     */
-    @BeforeMethod()
-    public String startPostgres() throws IOException {
+        waitForAttributeReleasePage();
 
-        final String initScriptPath = "net/shibboleth/idp/integration/tests/postgres/create-table.sql";
+        releaseAllAttributes();
 
-        postgresContainer = new PostgreSQLContainer<>("postgres").withInitScript(initScriptPath);
+        rememberConsent();
 
-        log.debug("{} Starting Postgres ...");
-        postgresContainer.start();
-        log.debug("{} Started Postgres : '{}'", postgresContainer.getContainerName());
+        submitForm();
 
-        final String jdbcUrl = postgresContainer.getJdbcUrl();
-        log.info("{} Postgres JDBC URL : '{}'", jdbcUrl);
+        if (true) {
+            sleep(1000);
+        }
 
-        replaceIdPHomeFile(Paths.get("conf", "global.xml"), "jdbc:postgresql://localhost:\\d+/test\\?loggerLevel=OFF", jdbcUrl);
+        // expect JSON runtime exception for IdP 5.1 (or earlier)
+        // TODO remove "5.2" once fixed
+        // if (idpVersion.startsWith("5.1")) {
+        if (idpVersion.startsWith("5.1") || idpVersion.startsWith("5.2")) {
+            waitForPageBodyContains("jakarta.json.stream.JsonParsingException");
+            return;
+        }
 
-        return jdbcUrl;
-    }
+        // response
 
-    /**
-     * Stop Postgres.
-     */
-    @AfterMethod()
-    public void stopPostgres() {
-        if (postgresContainer != null) {
-            log.debug("{} Stopping Postgres ...");
-            postgresContainer.stop();
-            log.debug("{} Postgres stopped");
-        }
-    }
+        waitForResponsePage();
 
-    @Test
-    @LinuxOnly
-    public void testSSOReleaseAllAttributes() throws Exception {
-        super.testSSOReleaseAllAttributes();
-    }
+        validateResponse();
 
-    @Test
-    @LinuxOnly
-    public void testSSOReleaseOneAttribute() throws Exception {
-        super.testSSOReleaseOneAttribute();
-    }
+        // twice
 
-    @Test
-    @LinuxOnly
-    public void testSSODoNotRememberConsent() throws Exception {
-        super.testSSODoNotRememberConsent();
-    }
+        startFlow();
 
-    @Test
-    @LinuxOnly
-    public void testSSOGlobalConsent() throws Exception {
-        super.testSSOGlobalConsent();
-    }
+        waitForResponsePage();
 
-    @Test
-    @LinuxOnly
-    public void testSSOTermsOfUse() throws Exception {
-        super.testSSOTermsOfUse();
+        validateResponse();
     }
+
 }
diff --git a/src/test/java/net/shibboleth/idp/integration/tests/consent/PruneRecordsPostgresConsentTest.java b/src/test/java/net/shibboleth/idp/integration/tests/consent/PruneRecordsPostgresConsentTest.java
new file mode 100644
index 0000000..6c1fa68
--- /dev/null
+++ b/src/test/java/net/shibboleth/idp/integration/tests/consent/PruneRecordsPostgresConsentTest.java
@@ -0,0 +1,124 @@
+/*
+ * 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.consent;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import javax.annotation.Nonnull;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import net.shibboleth.idp.integration.tests.util.testng.annotation.LinuxOnly;
+
+/** Test pruning consent storage records with a Postgres storage service. */
+public class PruneRecordsPostgresConsentTest extends BasePostgresConsentTest {
+
+    /** Class logger. */
+    @Nonnull
+    private final Logger log = LoggerFactory.getLogger(PruneRecordsPostgresConsentTest.class);
+
+    @Test
+    @LinuxOnly
+    public void testPruneStorageRecords() throws Exception {
+
+        setUpLocalDynamicSPMetadata(10);
+
+        startBrowser();
+
+        // TODO Assert that idp.consent.expandedMaxStoredRecords = 0
+
+        // Bump consent logging to TRACE
+        final Path pathToLogbackXML = Paths.get("conf", "logback.xml");
+        final String oldText = "</configuration>";
+        final String newText = "<logger name=\"net.shibboleth.idp.consent\" level=\"TRACE\"/>";
+        replaceIdPHomeFile(pathToLogbackXML, oldText, newText + System.lineSeparator() + oldText);
+
+        startServer();
+
+        // log in to sp.example.org
+
+        startFlow();
+
+        waitForLoginPage();
+
+        login();
+
+        // attribute release
+
+        waitForAttributeReleasePage();
+
+        releaseAllAttributes();
+
+        rememberConsent();
+
+        submitForm();
+
+        // response
+
+        waitForResponsePage();
+
+        validateResponse();
+
+        // log in to sp.example.org again
+
+        startFlow();
+
+        waitForResponsePage();
+
+        validateResponse();
+
+        // log in to more SPs
+
+        loginToLocalDynamicSP("sp1");
+
+        loginToLocalDynamicSP("sp2");
+
+        loginToLocalDynamicSP("sp3");
+
+        loginToLocalDynamicSP("sp4");
+
+        loginToLocalDynamicSP("sp5");
+
+        loginToLocalDynamicSP("sp6");
+
+        loginToLocalDynamicSP("sp7");
+
+        loginToLocalDynamicSP("sp8");
+
+        loginToLocalDynamicSP("sp9");
+
+        // Should be 10 records + 1 index record
+        Assert.assertEquals(queryDatabase().size(), 11);
+
+        stopServer();
+
+        replaceIdPProperty("idp.consent.expandedMaxStoredRecords", "5");
+
+        startServer();
+
+        loginToLocalDynamicSP("sp10");
+
+        // Should be max (5) + 1 records after pruning
+        Assert.assertEquals(queryDatabase().size(), 6);
+    }
+
+}
diff --git a/src/test/java/net/shibboleth/idp/integration/tests/util/TestbedStorageServiceClient.java b/src/test/java/net/shibboleth/idp/integration/tests/util/TestbedStorageServiceClient.java
new file mode 100644
index 0000000..3de24dd
--- /dev/null
+++ b/src/test/java/net/shibboleth/idp/integration/tests/util/TestbedStorageServiceClient.java
@@ -0,0 +1,193 @@
+
+package net.shibboleth.idp.integration.tests.util;
+
+import java.io.IOException;
+import java.net.Socket;
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.net.http.HttpResponse.BodyHandlers;
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+
+import javax.annotation.Nonnull;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509ExtendedTrustManager;
+import javax.net.ssl.X509TrustManager;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.util.UriUtils;
+
+/** Storage service client for the testbed StorageServiceWrapperController. */
+public class TestbedStorageServiceClient {
+
+    /** Class logger. */
+    @Nonnull
+    private final Logger log = LoggerFactory.getLogger(TestbedStorageServiceClient.class);
+
+    private String baseUrl = "https://idp.tests.shibboleth.net:8443";
+
+    /** Storage service ID. */
+    private String storageServiceId;
+
+    /** Storage service context. */
+    private String context;
+
+    /** Storage service key. */
+    private String key;
+
+    /** Storage service record value. */
+    private String value;
+
+    public TestbedStorageServiceClient setBaseUrl(@Nonnull final String baseUrl) {
+        this.baseUrl = baseUrl;
+        return this;
+    }
+
+    public TestbedStorageServiceClient setStorageServiceId(@Nonnull final String storageServiceId) {
+        this.storageServiceId = storageServiceId;
+        return this;
+    }
+
+    public TestbedStorageServiceClient setContext(@Nonnull final String context) {
+        this.context = context;
+        return this;
+    }
+
+    public TestbedStorageServiceClient setKey(@Nonnull final String key) {
+        this.key = key;
+        return this;
+    }
+
+    public TestbedStorageServiceClient setValue(@Nonnull final String value) {
+        this.value = value;
+        return this;
+    }
+
+    public boolean create() throws IOException {
+
+        log.debug("Create '{}' context='{}' '{}'='{}'", storageServiceId, context, key, value);
+
+        try {
+            final String encodedStorageServiceId = UriUtils.encode(storageServiceId, "UTF-8");
+
+            final String encodedContext = UriUtils.encode(context, "UTF-8");
+
+            final String encodedKey = UriUtils.encode(key, "UTF-8");
+
+            final String encodedValue = UriUtils.encode(value, "UTF-8");
+
+            final String url = baseUrl + //
+                    "/idp/storage/create/" + //
+                    String.join("/", encodedStorageServiceId, encodedContext, encodedKey) + //
+                    "?value=" + encodedValue;
+
+            log.debug("Create URL '{}'", url);
+
+            final HttpRequest request = HttpRequest.newBuilder() //
+                    .version(HttpClient.Version.HTTP_2) //
+                    .uri(URI.create(url)) //
+                    .POST(HttpRequest.BodyPublishers.noBody()) //
+                    .build();
+
+            final HttpClient client = trustAnyHttpClient();
+
+            final HttpResponse<String> response = client.send(request, BodyHandlers.ofString());
+
+            log.debug("Create response :\n{}", response.body());
+
+            final int responseStatusCode = response.statusCode();
+
+            log.debug("Create response status code '{}'", responseStatusCode);
+
+            if (response.statusCode() == 201) {
+                log.info("Created storage '{}' '{}' '{}'='{}'", storageServiceId, context, key, value);
+                return true;
+            }
+
+        } catch (final InterruptedException e) {
+            log.error("Unable to create '{}' context='{}' '{}'='{}'", storageServiceId, context, key, value, e);
+        }
+
+        return false;
+    }
+
+    /**
+     * An {@link HttpClient} that accepts any certificate as trusted.
+     */
+    public static HttpClient trustAnyHttpClient() {
+        return HttpClient.newBuilder().sslContext(trustAnyCertificateSSLContext()).build();
+    }
+
+    /**
+     * An {@link SSLContext} that accepts any certificate as trusted.
+     */
+    public static SSLContext trustAnyCertificateSSLContext() {
+        try {
+            final SSLContext context = SSLContext.getInstance("TLS");
+            context.init(null, new TrustManager[] { new TrustAnyCertificate() }, null);
+            return context;
+        } catch (final NoSuchAlgorithmException | KeyManagementException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * An {@link X509TrustManager} that accepts any certificate as trusted.
+     */
+    public static class TrustAnyCertificate extends X509ExtendedTrustManager {
+
+        /** {@inheritDoc} */
+        @Override
+        public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
+
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
+
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket)
+                throws CertificateException {
+
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket)
+                throws CertificateException {
+
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine)
+                throws CertificateException {
+
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine)
+                throws CertificateException {
+
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public X509Certificate[] getAcceptedIssuers() {
+            return new X509Certificate[] {};
+        }
+    }
+
+}

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


More information about the commits mailing list