[java-plugin-shibd] 02/02: Flow to parse SP RequestMap for agents.

Scott Cantor cantor.2 at osu.edu
Mon Jun 17 19:14:21 UTC 2024


This is an automated email from the git hooks/post-receive script.

scantor pushed a commit to branch main
in repository java-plugin-shibd.

View the commit online:
http://git.shibboleth.net/view/?p=java-plugin-shibd.git;a=commit;h=a18bc87493d341b777688016f22f88d762ed8c2f

commit a18bc87493d341b777688016f22f88d762ed8c2f
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Mon Jun 17 15:14:16 2024 -0400

    Flow to parse SP RequestMap for agents.
---
 .../parse-request-map/parse-request-map-beans.xml  |  34 +++
 .../parse-request-map/parse-request-map-flow.xml   |  26 +++
 .../main/java/net/shibboleth/sp/SPConstants.java   |  40 ++++
 .../schema/shibboleth-sp-requestmap-4.0.xsd        | 228 +++++++++++++++++++++
 sp-server-impl/pom.xml                             |   7 +-
 .../sp/config/impl/ParseAgentRequestMap.java       | 162 +++++++++++++++
 .../profile/impl/AbstractAgentRequestAction.java   |  17 ++
 .../sp/config/impl/ParseAgentRequestMapTest.java   | 153 ++++++++++++++
 .../sp/profile/impl/BaseAgentRequestTest.java      |  53 +++++
 .../shibboleth/sp/authn/impl/SealerKeyStore.jks    | Bin 984 -> 0 bytes
 .../shibboleth/sp/authn/impl/SealerKeyStore.kver   |   1 -
 .../net/shibboleth/sp/config/impl/example-map.ddf  |   1 +
 .../net/shibboleth/sp/config/impl/example-map.xml  | 124 +----------
 13 files changed, 729 insertions(+), 117 deletions(-)

diff --git a/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/parse-request-map/parse-request-map-beans.xml b/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/parse-request-map/parse-request-map-beans.xml
new file mode 100644
index 0000000..d817289
--- /dev/null
+++ b/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/parse-request-map/parse-request-map-beans.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans" xmlns:c="http://www.springframework.org/schema/c"
+    xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
+    xmlns:util="http://www.springframework.org/schema/util" 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">
+
+    <bean id="shibboleth.sp.profileId" class="java.lang.String" c:_0="http://shibboleth.net/ns/profiles/sp/parse-request-map" />
+    <bean id="shibboleth.sp.loggingId" class="java.lang.String" c:_0="%{idp.service.logging.sp:SPAgent}" />
+
+    <bean id="ParseAgentRequestMap"
+        class="net.shibboleth.sp.config.impl.ParseAgentRequestMap" scope="prototype"
+        p:parserPool-ref="shibboleth.ParserPool"
+        p:schemaBuilder-ref="RequestMap.SchemaBuilder" />
+
+    <bean id="RequestMap.SchemaBuilder"
+            class="org.opensaml.saml.common.xml.SAMLSchemaBuilder" c:ver="SAML_11" c:strict="%{idp.schemaValidation.strict:false}">
+        <property name="schemaBuilder">
+            <bean class="net.shibboleth.shared.xml.SchemaBuilder"
+                    p:schemaResources-ref="RequestMap.Schemas">
+                <property name="resourceResolver">
+                    <bean class="net.shibboleth.shared.xml.ClasspathResolver" />
+                </property>
+            </bean>
+        </property>
+    </bean>
+
+    <util:list id="RequestMap.Schemas">
+        <value>#{'classpath:' + T(net.shibboleth.sp.SPConstants).SHIBSP4_REQUESTMAP_SCHEMA_LOCATION}</value>
+    </util:list>
+
+</beans>
diff --git a/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/parse-request-map/parse-request-map-flow.xml b/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/parse-request-map/parse-request-map-flow.xml
new file mode 100644
index 0000000..9f7e0fa
--- /dev/null
+++ b/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/parse-request-map/parse-request-map-flow.xml
@@ -0,0 +1,26 @@
+<flow xmlns="http://www.springframework.org/schema/webflow" 
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://www.springframework.org/schema/webflow http://www.springframework.org/schema/webflow/spring-webflow.xsd"
+    parent="sp/abstract">
+
+    <action-state id="InitializeProfileRequestContext">
+        <evaluate expression="InitializeProfileRequestContext" />
+        <evaluate expression="'proceed'" />
+        
+        <!-- Branch to parent flow for authentication. -->
+        <transition on="proceed" to="AuthenticationSetup" />
+    </action-state>
+
+    <!-- Resume flow operation after set up by parent. -->
+    <action-state id="DoOperation">
+        <evaluate expression="ParseAgentRequestMap" />
+        <evaluate expression="'proceed'" />
+        
+        <!-- Branch to parent flow to send response. -->
+        <transition on="proceed" to="EncodeAgentResponse" />
+    </action-state>
+    
+    <!-- The file really exists in this directory, but it's referenced from extending flow-directories -->
+    <bean-import resource="classpath:/META-INF/net/shibboleth/idp/flows/sp/parse-request-map/parse-request-map-beans.xml" />
+
+</flow>
diff --git a/sp-server-api/src/main/java/net/shibboleth/sp/SPConstants.java b/sp-server-api/src/main/java/net/shibboleth/sp/SPConstants.java
new file mode 100644
index 0000000..d42ca81
--- /dev/null
+++ b/sp-server-api/src/main/java/net/shibboleth/sp/SPConstants.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed 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.sp;
+
+import javax.annotation.Nonnull;
+
+import net.shibboleth.shared.annotation.constraint.NotEmpty;
+
+/** SP constants. */
+public final class SPConstants {
+        
+    /** Relative resource path for XML schema for SP agent RequestMap. */
+    @Nonnull @NotEmpty public static final String SHIBSP4_REQUESTMAP_SCHEMA_LOCATION = "/schema/shibboleth-sp-requestmap-4.0.xsd";
+    
+    /** URI for XML schema namespace for SP agent RequestMap. */
+    @Nonnull @NotEmpty public static final String SHIBSP4_REQUESTMAP_NS = "urn:mace:shibboleth:sp:requestmap:4.0";
+
+    /** Namespace QName prefix for SP agent RequestMap. */
+    @Nonnull @NotEmpty public static final String SHIBSP4_REQUESTMAP_PREFIX = "map";
+
+    /** Name of root element for SP agent RequestMap. */
+    @Nonnull @NotEmpty public static final String SHIBSP4_REQUESTMAP_ELEMENT_NAME = "RequestMap";
+
+    /** Constructor. */
+    private SPConstants() {
+    }
+
+}
\ No newline at end of file
diff --git a/sp-server-api/src/main/resources/schema/shibboleth-sp-requestmap-4.0.xsd b/sp-server-api/src/main/resources/schema/shibboleth-sp-requestmap-4.0.xsd
new file mode 100644
index 0000000..35b3dcf
--- /dev/null
+++ b/sp-server-api/src/main/resources/schema/shibboleth-sp-requestmap-4.0.xsd
@@ -0,0 +1,228 @@
+<?xml version="1.0" encoding="US-ASCII"?>
+<schema targetNamespace="urn:mace:shibboleth:sp:requestmap:4.0"
+	xmlns="http://www.w3.org/2001/XMLSchema"
+	xmlns:map="urn:mace:shibboleth:sp:requestmap:4.0"
+	elementFormDefault="qualified"
+	attributeFormDefault="unqualified"
+	blockDefault="substitution"
+	version="4.0">
+
+  <annotation>
+    <documentation>
+      Schema for XML-based configuration of the Shibboleth SP RequestMap and access control features.
+      First appearing in Shibboleth SP 4.0 release.
+    </documentation>
+  </annotation>
+
+  <simpleType name="string">
+    <restriction base="string">
+      <minLength value="1"/>
+    </restriction>
+  </simpleType>
+
+  <simpleType name="listOfStrings">
+    <list itemType="map:string"/>
+  </simpleType>
+
+  <simpleType name="anyURI">
+    <restriction base="anyURI">
+      <minLength value="1"/>
+    </restriction>
+  </simpleType>
+
+  <simpleType name="listOfURIs">
+    <list itemType="map:anyURI"/>
+  </simpleType>
+
+  <element name="AccessControl" type="map:UniOperatorType">
+    <annotation>
+      <documentation>
+        A simple example access policy language extension that supersedes Apache .htaccess
+      </documentation>
+    </annotation>
+  </element>
+  <complexType name="UniOperatorType">
+    <choice>
+      <element name="AND" type="map:MultiOperatorType"/>
+      <element name="OR" type="map:MultiOperatorType"/>
+      <element name="NOT" type="map:UniOperatorType"/>
+      <element name="Rule" type="map:RuleType"/>
+      <element name="RuleRegex" type="map:RuleRegexType"/>
+    </choice>
+  </complexType>
+  <complexType name="MultiOperatorType">
+    <choice minOccurs="2" maxOccurs="unbounded">
+      <element name="AND" type="map:MultiOperatorType"/>
+      <element name="OR" type="map:MultiOperatorType"/>
+      <element name="NOT" type="map:UniOperatorType"/>
+      <element name="Rule" type="map:RuleType"/>
+      <element name="RuleRegex" type="map:RuleRegexType"/>
+    </choice>
+  </complexType>
+  <complexType name="RuleType">
+    <simpleContent>
+      <extension base="map:listOfStrings">
+        <attribute name="require" type="map:string" use="required"/>
+        <attribute name="list" type="boolean"/>
+      </extension>
+    </simpleContent>
+  </complexType>
+  <complexType name="RuleRegexType">
+    <simpleContent>
+      <extension base="map:string">
+        <attribute name="require" type="map:string" use="required"/>
+        <attribute name="caseSensitive" type="boolean"/>
+      </extension>
+    </simpleContent>
+  </complexType>
+
+  <attributeGroup name="ContentSettings">
+    <attribute name="applicationId" type="map:string"/>
+    <attribute name="authType" type="map:string"/>
+    <attribute name="requireSession" type="boolean"/>
+    <attribute name="requireSessionWith" type="map:string"/>
+    <attribute name="requireLogoutWith" type="map:anyURI"/>
+    <attribute name="exportAssertion" type="boolean"/>
+    <attribute name="exportStdVars" type="boolean"/>
+    <attribute name="exportCookie" type="boolean"/>
+    <attribute name="exportDuplicateValues" type="boolean"/>
+    <attribute name="redirectToSSL" type="unsignedInt"/>
+    <attribute name="entityID" type="map:anyURI"/>
+    <attribute name="entityIDSelf" type="map:anyURI"/>
+    <attribute name="discoveryURL" type="map:anyURI"/>
+    <attribute name="discoveryPolicy" type="map:string"/>
+    <attribute name="isPassive" type="boolean"/>
+    <attribute name="returnOnError" type="boolean"/>
+    <attribute name="forceAuthn" type="boolean"/>
+    <attribute name="authnContextClassRef" type="map:listOfURIs"/>
+    <attribute name="authnContextComparison">
+      <simpleType>
+        <restriction base="map:string">
+          <enumeration value="exact"/>
+          <enumeration value="minimum"/>
+          <enumeration value="maximum"/>
+          <enumeration value="better"/>
+        </restriction>
+      </simpleType>
+    </attribute>
+    <attribute name="NameIDFormat" type="map:anyURI"/>
+    <attribute name="SPNameQualifier" type="map:string"/>
+    <attribute name="redirectErrors" type="map:anyURI"/>
+    <attribute name="sessionError" type="map:anyURI"/>
+    <attribute name="metadataError" type="map:anyURI"/>
+    <attribute name="accessError" type="map:anyURI"/>
+    <attribute name="sslError" type="map:anyURI"/>
+    <attribute name="target" type="map:anyURI"/>
+    <attribute name="acsIndex" type="unsignedShort"/>
+    <attribute name="attributeIndex" type="map:string"/>
+    <attribute name="REMOTE_ADDR" type="map:string"/>
+    <attribute name="encoding" type="map:string"/>
+    <attribute name="attributeValueDelimiter" type="map:string"/>
+    <attribute name="unset" type="map:listOfStrings"/>
+    <anyAttribute namespace="##other" processContents="lax"/>
+  </attributeGroup>
+
+  <element name="RequestMap">
+    <annotation>
+      <documentation>
+        Built-in request mapping syntax, decomposes URLs into Host/Path/Path/...
+      </documentation>
+    </annotation>
+    <complexType>
+      <sequence>
+        <choice minOccurs="0">
+          <element ref="map:AccessControl"/>
+        </choice>
+        <choice minOccurs="0" maxOccurs="unbounded">
+          <element name="Host" type="map:HostType"/>
+          <element name="HostRegex" type="map:HostRegexType"/>
+        </choice>
+      </sequence>
+      <attribute name="unicodeAware" type="boolean"/>
+      <attributeGroup ref="map:ContentSettings"/>
+    </complexType>
+  </element>
+
+  <complexType name="HostType">
+    <sequence>
+      <choice minOccurs="0">
+        <element ref="map:AccessControl"/>
+      </choice>
+      <choice minOccurs="0" maxOccurs="unbounded">
+        <element name="Path" type="map:PathType"/>
+        <element name="PathRegex" type="map:PathRegexType"/>
+        <element name="Query" type="map:QueryType"/>
+      </choice>
+    </sequence>
+    <attribute name="scheme">
+      <simpleType>
+        <restriction base="map:string">
+          <enumeration value="http"/>
+          <enumeration value="https"/>
+          <enumeration value="ftp"/>
+          <enumeration value="ldap"/>
+          <enumeration value="ldaps"/>
+        </restriction>
+      </simpleType>
+    </attribute>
+    <attribute name="name" type="map:string" use="required"/>
+    <attribute name="port" type="unsignedInt"/>
+    <attributeGroup ref="map:ContentSettings"/>
+  </complexType>
+
+  <complexType name="HostRegexType">
+    <sequence>
+      <choice minOccurs="0">
+        <element ref="map:AccessControl"/>
+      </choice>
+      <choice minOccurs="0" maxOccurs="unbounded">
+        <element name="Path" type="map:PathType"/>
+        <element name="PathRegex" type="map:PathRegexType"/>
+        <element name="Query" type="map:QueryType"/>
+      </choice>
+    </sequence>
+    <attribute name="regex" type="map:string" use="required"/>
+    <attribute name="caseSensitive" type="boolean"/>
+    <attributeGroup ref="map:ContentSettings"/>
+  </complexType>
+
+  <complexType name="PathType">
+    <sequence>
+      <choice minOccurs="0">
+        <element ref="map:AccessControl"/>
+      </choice>
+      <choice minOccurs="0" maxOccurs="unbounded">
+        <element name="Path" type="map:PathType"/>
+        <element name="PathRegex" type="map:PathRegexType"/>
+        <element name="Query" type="map:QueryType"/>
+      </choice>
+    </sequence>
+    <attribute name="name" type="map:string" use="required"/>
+    <attributeGroup ref="map:ContentSettings"/>
+  </complexType>
+
+  <complexType name="PathRegexType">
+    <sequence>
+      <choice minOccurs="0">
+        <element ref="map:AccessControl"/>
+      </choice>
+      <element name="Query" type="map:QueryType" minOccurs="0" maxOccurs="unbounded"/>
+    </sequence>
+    <attribute name="regex" type="map:string" use="required"/>
+    <attribute name="caseSensitive" type="boolean"/>
+    <attributeGroup ref="map:ContentSettings"/>
+  </complexType>
+
+  <complexType name="QueryType">
+    <sequence>
+      <choice minOccurs="0">
+        <element ref="map:AccessControl"/>
+      </choice>
+      <element name="Query" type="map:QueryType" minOccurs="0" maxOccurs="unbounded"/>
+    </sequence>
+    <attribute name="name" type="map:string" use="required"/>
+    <attribute name="regex" type="map:string"/>
+    <attributeGroup ref="map:ContentSettings"/>
+  </complexType>
+
+</schema>
diff --git a/sp-server-impl/pom.xml b/sp-server-impl/pom.xml
index 01dc975..c094248 100644
--- a/sp-server-impl/pom.xml
+++ b/sp-server-impl/pom.xml
@@ -86,6 +86,11 @@
             <artifactId>opensaml-profile-api</artifactId>
             <scope>provided</scope>
         </dependency>
+        <dependency>
+            <groupId>${opensaml.groupId}</groupId>
+            <artifactId>opensaml-saml-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
         <dependency>
             <groupId>${opensaml.groupId}</groupId>
             <artifactId>opensaml-xmlsec-api</artifactId>
@@ -145,7 +150,7 @@
             <artifactId>idp-testing</artifactId>
             <scope>test</scope>
         </dependency>
-        
+
         <dependency>
             <groupId>${spring.groupId}</groupId>
             <artifactId>spring-test</artifactId>
diff --git a/sp-server-impl/src/main/java/net/shibboleth/sp/config/impl/ParseAgentRequestMap.java b/sp-server-impl/src/main/java/net/shibboleth/sp/config/impl/ParseAgentRequestMap.java
new file mode 100644
index 0000000..0a96a2e
--- /dev/null
+++ b/sp-server-impl/src/main/java/net/shibboleth/sp/config/impl/ParseAgentRequestMap.java
@@ -0,0 +1,162 @@
+/*
+ * Licensed 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.sp.config.impl;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.annotation.Nonnull;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.validation.Validator;
+
+import org.opensaml.profile.action.ActionSupport;
+import org.opensaml.profile.action.EventIds;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.saml.common.xml.SAMLSchemaBuilder;
+import org.slf4j.Logger;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.xml.sax.SAXException;
+
+import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.shared.component.ComponentInitializationException;
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.primitive.LoggerFactory;
+import net.shibboleth.shared.xml.ElementSupport;
+import net.shibboleth.shared.xml.ParserPool;
+import net.shibboleth.shared.xml.QNameSupport;
+import net.shibboleth.shared.xml.XMLParserException;
+import net.shibboleth.sp.SPConstants;
+import net.shibboleth.sp.ddf.DDF;
+import net.shibboleth.sp.ddf.DDFSupport;
+import net.shibboleth.sp.profile.impl.AbstractAgentRequestAction;
+
+/**
+ * Action that handles parsing the legacy 3.0 XML configuration for an agent.
+ * 
+ * <p>The input must be an unsafe_string.</p>
+ * 
+ * <p>The output if parsing succeeds will be the DDF conversion of the DOM tree in a
+ * structure named "xml".</p>
+ * 
+ * @event {@link EventIds#PROCEED_EVENT_ID}
+ * @event {@link EventIds#INVALID_MESSAGE}
+ */
+public class ParseAgentRequestMap extends AbstractAgentRequestAction {
+
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(ParseAgentRequestMap.class);
+    
+    /** Parser pool. */
+    @NonnullAfterInit private ParserPool parserPool;
+    
+    /** SAML schema source. */
+    @NonnullAfterInit private SAMLSchemaBuilder samlSchemaBuilder;
+    
+    /**
+     * Set the {@link ParserPool} instance to use.
+     * 
+     * @param parser parser pool
+     */
+    public void setParserPool(@Nonnull final ParserPool parser) {
+        checkSetterPreconditions();
+        
+        parserPool = Constraint.isNotNull(parser, "ParserPool cannot be null");
+    }
+    
+    /**
+     * Set the {@link SAMLSchemaBuilder} to validate with.
+     * 
+     * @param builder SAML schema builder to use
+     */
+    public void setSchemaBuilder(@Nonnull final SAMLSchemaBuilder builder) {
+        samlSchemaBuilder = Constraint.isNotNull(builder, "SAMLSchemaBuilder cannot be null");
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void doInitialize() throws ComponentInitializationException {
+        super.doInitialize();
+        
+        if (parserPool == null) {
+            throw new ComponentInitializationException("ParserPool cannot be null");
+        } else if (samlSchemaBuilder == null) {
+            throw new ComponentInitializationException("SAMLSchemaBuilder cannot be null");
+        }
+    }
+    
+    /** {@inheritDoc} */
+    @Override
+    protected void doExecute(@Nonnull ProfileRequestContext profileRequestContext) {
+        final DDF input = ensureAgentRequestContext().getInput();
+        
+        if (input == null || !input.isunsafestring()) {
+            log.warn("{} input was not an unsafe string", getLogPrefix());
+            ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MESSAGE);
+            return;
+        }
+        
+        try (final InputStream data = new ByteArrayInputStream(input.unsafe_string())) {
+            final Document doc = parserPool.parse(data);
+            final Element root = doc.getDocumentElement();
+            assert root != null;
+            
+            if (!ElementSupport.isElementNamed(root, SPConstants.SHIBSP4_REQUESTMAP_NS,
+                    SPConstants.SHIBSP4_REQUESTMAP_ELEMENT_NAME)) {
+                log.warn("{} Root element was invalid: {}", getLogPrefix(),
+                        QNameSupport.getNodeQName(root));
+                ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MESSAGE);
+                return;
+            }
+            
+            validate(root);
+            final DDF output = new DDF("xml").structure();
+            output.add(DDFSupport.fromElement(root));
+            
+            ensureAgentRequestContext().setOutput(output);
+            
+        } catch (final IOException | XMLParserException e) {
+            log.warn("{} Unable to parse or validate supplied RequestMap", getLogPrefix(), e);
+            ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MESSAGE);
+        }
+    }
+ 
+    /**
+     * Apply schema validation to DOM.
+     * 
+     * @param root root of document
+     * 
+     * @throws IOException if an I/O error occurs
+     * @throws XMLParserException if a general validation failure occurs
+     */
+    private void validate(@Nonnull final Element root) throws IOException, XMLParserException {
+        final Validator schemaValidator;
+        try {
+            schemaValidator = samlSchemaBuilder.getSAMLSchema().newValidator();
+        } catch (final SAXException e) {
+            log.error("{} Unable to build RequestMap validation schema: {}", getLogPrefix(), e.getMessage());
+            throw new XMLParserException("Unable to build RequestMap validation schema", e);
+        }
+
+        try {
+            schemaValidator.validate(new DOMSource(root));
+        } catch (final SAXException e) {
+            log.error("{} RequestMap was not schema valid: {}", getLogPrefix(), e.getMessage());
+            throw new XMLParserException("RequestMap was not schema valid", e);
+        }
+    }
+    
+}
\ No newline at end of file
diff --git a/sp-server-impl/src/main/java/net/shibboleth/sp/profile/impl/AbstractAgentRequestAction.java b/sp-server-impl/src/main/java/net/shibboleth/sp/profile/impl/AbstractAgentRequestAction.java
index da5adc5..a60b97e 100644
--- a/sp-server-impl/src/main/java/net/shibboleth/sp/profile/impl/AbstractAgentRequestAction.java
+++ b/sp-server-impl/src/main/java/net/shibboleth/sp/profile/impl/AbstractAgentRequestAction.java
@@ -29,6 +29,7 @@ import net.shibboleth.idp.profile.AbstractProfileAction;
 import net.shibboleth.shared.annotation.constraint.NonnullBeforeExec;
 import net.shibboleth.shared.logic.Constraint;
 import net.shibboleth.shared.primitive.LoggerFactory;
+import net.shibboleth.sp.Agent;
 import net.shibboleth.sp.context.AgentRequestContext;
 
 /**
@@ -104,5 +105,21 @@ public abstract class AbstractAgentRequestAction extends AbstractProfileAction {
         
         return true;
     }
+
+    /** {@inheritDoc} */
+    @SuppressWarnings("null")
+    @Override
+    @Nonnull protected String getLogPrefix() {
+        final StringBuilder s = new StringBuilder(super.getLogPrefix());
+        final AgentRequestContext ctx = getAgentRequestContext();
+        if (ctx != null) {
+            final Agent agent = ctx.getAgent();
+            if (agent != null) {
+                s.append("Agent: ").append(agent.getId()).append(": ");
+            }
+        }
+        
+        return s.toString();
+    }
     
 }
\ No newline at end of file
diff --git a/sp-server-impl/src/test/java/net/shibboleth/sp/config/impl/ParseAgentRequestMapTest.java b/sp-server-impl/src/test/java/net/shibboleth/sp/config/impl/ParseAgentRequestMapTest.java
new file mode 100644
index 0000000..98e86fa
--- /dev/null
+++ b/sp-server-impl/src/test/java/net/shibboleth/sp/config/impl/ParseAgentRequestMapTest.java
@@ -0,0 +1,153 @@
+/*
+ * Licensed 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.sp.config.impl;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.Charset;
+
+import org.opensaml.profile.action.EventIds;
+import org.opensaml.saml.common.xml.SAMLSchemaBuilder;
+import org.opensaml.saml.common.xml.SAMLSchemaBuilder.SAML1Version;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.webflow.execution.Event;
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import net.shibboleth.idp.profile.testing.ActionTestingSupport;
+import net.shibboleth.shared.component.ComponentInitializationException;
+import net.shibboleth.shared.primitive.StringSupport;
+import net.shibboleth.shared.spring.resource.ResourceHelper;
+import net.shibboleth.shared.xml.ClasspathResolver;
+import net.shibboleth.shared.xml.SchemaBuilder;
+import net.shibboleth.shared.xml.impl.BasicParserPool;
+import net.shibboleth.sp.SPConstants;
+import net.shibboleth.sp.ddf.DDF;
+import net.shibboleth.sp.ddf.DDFSupport;
+import net.shibboleth.sp.profile.impl.BaseAgentRequestTest;
+
+/**
+ * Unit test for {@link ParseAgentRequestMap} action.
+ */
+ at SuppressWarnings("javadoc")
+public class ParseAgentRequestMapTest extends BaseAgentRequestTest {
+
+    private ParseAgentRequestMap action;
+    
+    @BeforeMethod
+    public void setUp() throws ComponentInitializationException {
+        super.beforeMethod();
+
+        final BasicParserPool parser = new BasicParserPool();
+        parser.initialize();
+
+        final SchemaBuilder extBuilder = new SchemaBuilder();
+        extBuilder.setResourceResolver(new ClasspathResolver());
+        extBuilder.addSchema(ResourceHelper.of(new ClassPathResource(SPConstants.SHIBSP4_REQUESTMAP_SCHEMA_LOCATION)));
+        
+        final SAMLSchemaBuilder schemaBuilder = new SAMLSchemaBuilder(SAML1Version.SAML_11);
+        schemaBuilder.setSchemaBuilder(extBuilder);
+        
+        action = new ParseAgentRequestMap();
+        action.setParserPool(parser);
+        action.setSchemaBuilder(schemaBuilder);
+        action.initialize();
+    }
+    
+    @Test
+    public void testNoInput() {
+        Event event = action.execute(src);
+        ActionTestingSupport.assertEvent(event, EventIds.INVALID_MESSAGE);
+        
+        arc.setInput(new DDF().string("test"));
+
+        event = action.execute(src);
+        ActionTestingSupport.assertEvent(event, EventIds.INVALID_MESSAGE);
+    }
+ 
+    @Test
+    public void testNonWellFormed() throws UnsupportedEncodingException {
+        arc.setInput(new DDF().unsafe_string("<RequestMap".getBytes(Charset.forName("UTF-8"))));
+
+        final Event event = action.execute(src);
+        ActionTestingSupport.assertEvent(event, EventIds.INVALID_MESSAGE);
+    }
+
+    @Test
+    public void testInvalid() throws UnsupportedEncodingException {
+        arc.setInput(new DDF().unsafe_string("<RequestMap/>".getBytes(Charset.forName("UTF-8"))));
+
+        final Event event = action.execute(src);
+        ActionTestingSupport.assertEvent(event, EventIds.INVALID_MESSAGE);
+    }
+    
+    @Test
+    public void testEmpty() throws UnsupportedEncodingException {
+        arc.setInput(new DDF().unsafe_string("<RequestMap xmlns='urn:mace:shibboleth:sp:requestmap:4.0'/>".getBytes(Charset.forName("UTF-8"))));
+
+        final Event event = action.execute(src);
+        ActionTestingSupport.assertProceedEvent(event);
+        
+        final DDF output = arc.getOutput();
+        assert output != null;
+        Assert.assertEquals(output.name(), "xml");
+        Assert.assertTrue(output.isstruct());
+        Assert.assertTrue(output.getmember("RequestMap").isstruct());
+        Assert.assertEquals(output.getmember("RequestMap.xmlns").string(), SPConstants.SHIBSP4_REQUESTMAP_NS);
+    }
+
+    @Test
+    public void testFull() throws IOException {
+        
+        try (final InputStream in = getClass().getResourceAsStream("example-map.xml")) {
+            assert in != null;
+            arc.setInput(new DDF().unsafe_string(
+                    StringSupport.inputStreamToString(in, Charset.forName("UTF-8").newDecoder()).getBytes(Charset.forName("UTF-8"))));
+        }
+        
+        // To capture wire input needed to drive flow.
+        // arc.getInput().serialize(System.err);
+        
+        // Sample command to run flow:
+        // $ cd sp-server-impl/src/test/resources/net/shibboleth/sp/config/impl/
+        // $ curl -k -X POST  -H "Content-Type: text/plain" --data-binary @example-map.ddf -u sp.example.org:foo https://localhost/idp/profile/sp/parse-request-map
+
+        final Event event = action.execute(src);
+        ActionTestingSupport.assertProceedEvent(event);
+        
+        final DDF output = arc.getOutput();
+        assert output != null;
+        Assert.assertEquals(output.name(), "xml");
+        Assert.assertTrue(output.isstruct());
+        Assert.assertTrue(output.getmember("RequestMap").isstruct());
+        
+        final DDF nestedPath = output.getmember("RequestMap")
+                .getmember(DDFSupport.CHILD_ELEMENTS_MEMBER)
+                .asList().get(0)
+                .getmember(DDFSupport.CHILD_ELEMENTS_MEMBER)
+                .asList().get(0)
+                .getmember(DDFSupport.CHILD_ELEMENTS_MEMBER)
+                .asList().get(0)
+                .getmember(DDFSupport.CHILD_ELEMENTS_MEMBER)
+                .asList().get(0);
+        
+        Assert.assertEquals(nestedPath.name(), "Path");
+        Assert.assertTrue(nestedPath.isstruct());
+        Assert.assertEquals(nestedPath.getmember("applicationId").string(), "example-special");
+    }
+
+}
\ No newline at end of file
diff --git a/sp-server-impl/src/test/java/net/shibboleth/sp/profile/impl/BaseAgentRequestTest.java b/sp-server-impl/src/test/java/net/shibboleth/sp/profile/impl/BaseAgentRequestTest.java
new file mode 100644
index 0000000..f0b6bc7
--- /dev/null
+++ b/sp-server-impl/src/test/java/net/shibboleth/sp/profile/impl/BaseAgentRequestTest.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed 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.sp.profile.impl;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.springframework.webflow.execution.RequestContext;
+
+import net.shibboleth.idp.profile.context.navigate.WebflowRequestContextProfileRequestContextLookup;
+import net.shibboleth.idp.profile.testing.RequestContextBuilder;
+import net.shibboleth.shared.component.ComponentInitializationException;
+import net.shibboleth.sp.context.AgentRequestContext;
+import net.shibboleth.sp.impl.BasicAgent;
+
+/**
+ * Base class for unit tests that rely on set up of an agent request.
+ */
+public abstract class BaseAgentRequestTest {
+
+    protected RequestContext src;
+    protected ProfileRequestContext prc;
+    protected AgentRequestContext arc;
+    protected BasicAgent agent;
+
+    /**
+     * Call from the before-method set up hook.
+     * 
+     * @throws ComponentInitializationException
+     */
+    public void beforeMethod() throws ComponentInitializationException {
+        src = new RequestContextBuilder().buildRequestContext();
+        prc = new WebflowRequestContextProfileRequestContextLookup().apply(src);
+        arc = prc.ensureSubcontext(AgentRequestContext.class);
+        
+        agent = new BasicAgent();
+        agent.setId("test");
+        agent.initialize();
+        
+        arc.setAgent(agent);
+    }
+    
+}
\ No newline at end of file
diff --git a/sp-server-impl/src/test/resources/net/shibboleth/sp/authn/impl/SealerKeyStore.jks b/sp-server-impl/src/test/resources/net/shibboleth/sp/authn/impl/SealerKeyStore.jks
deleted file mode 100644
index 147d92b..0000000
Binary files a/sp-server-impl/src/test/resources/net/shibboleth/sp/authn/impl/SealerKeyStore.jks and /dev/null differ
diff --git a/sp-server-impl/src/test/resources/net/shibboleth/sp/authn/impl/SealerKeyStore.kver b/sp-server-impl/src/test/resources/net/shibboleth/sp/authn/impl/SealerKeyStore.kver
deleted file mode 100644
index 2cd48df..0000000
--- a/sp-server-impl/src/test/resources/net/shibboleth/sp/authn/impl/SealerKeyStore.kver
+++ /dev/null
@@ -1 +0,0 @@
-CurrentVersion = 1
diff --git a/sp-server-impl/src/test/resources/net/shibboleth/sp/config/impl/example-map.ddf b/sp-server-impl/src/test/resources/net/shibboleth/sp/config/impl/example-map.ddf
new file mode 100644
index 0000000..63e295c
--- /dev/null
+++ b/sp-server-impl/src/test/resources/net/shibboleth/sp/config/impl/example-map.ddf
@@ -0,0 +1 @@
+. 7 <RequestMap%20xmlns=%22urn:mace:shibboleth:sp:requestmap:4%2E0%22%20applicationId=%22default%22>%0A%20%20%20%20<Host%20name=%22www%2Eexample%2Eorg%22>%0A%20%20%20%20%20%20%20%20<Path%20name=%22secure%22%20requireSession=%22true%22>%0A%20%20%20%20%20%20%20%20%20%20%20%20<Path%20name=%22apps%22>%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20<Path%20name=%22special%22%20applicationId=%22example%2Dspecial%22%2F>%0A%20%20%20%20%20%20%20%20%20%20%20%20<%2FPath>%0A%20%20%20%20%20%20%20% [...]
diff --git a/sp-server-impl/src/test/resources/net/shibboleth/sp/config/impl/example-map.xml b/sp-server-impl/src/test/resources/net/shibboleth/sp/config/impl/example-map.xml
index cbe08c0..ec8f7d4 100644
--- a/sp-server-impl/src/test/resources/net/shibboleth/sp/config/impl/example-map.xml
+++ b/sp-server-impl/src/test/resources/net/shibboleth/sp/config/impl/example-map.xml
@@ -1,115 +1,9 @@
-<SPConfig xmlns="urn:mace:shibboleth:3.0:native:sp:config"
-    xmlns:conf="urn:mace:shibboleth:3.0:native:sp:config"
-    clockSkew="180">
-
-    <OutOfProcess tranLogFormat="%u|%s|%IDP|%i|%ac|%t|%attr|%n|%b|%E|%S|%SS|%L|%UA|%a" />
-  
-    <!--
-    By default, in-memory StorageService, ReplayCache, ArtifactMap, and SessionCache
-    are used. See example-shibboleth2.xml for samples of explicitly configuring them.
-    -->
-
-    <!-- The ApplicationDefaults element is where most of Shibboleth's SAML bits are defined. -->
-    <ApplicationDefaults entityID="https://sp.example.org/shibboleth"
-        REMOTE_USER="eppn subject-id pairwise-id persistent-id"
-        cipherSuites="DEFAULT:!EXP:!LOW:!aNULL:!eNULL:!DES:!IDEA:!SEED:!RC4:!3DES:!kRSA:!SSLv2:!SSLv3:!TLSv1:!TLSv1.1">
-
-        <!--
-        Controls session lifetimes, address checks, cookie handling, and the protocol handlers.
-        Each Application has an effectively unique handlerURL, which defaults to "/Shibboleth.sso"
-        and should be a relative path, with the SP computing the full value based on the virtual
-        host. Use of TLS is now assumed because browsers are enforcing it due to SameSite
-        restrictions. Note that while we default checkAddress to "false", this makes an assertion
-        stolen in transit easier for attackers to misuse.
-        -->
-       <Sessions lifetime="28800" timeout="3600" relayState="ss:mem"
-                  checkAddress="false" handlerSSL="true" cookieProps="https"
-                  redirectLimit="exact">
-
-            <!--
-            Configures SSO for a default IdP. To properly allow for >1 IdP, remove
-            entityID property and adjust discoveryURL to point to discovery service.
-            You can also override entityID on /Login query string, or in RequestMap/htaccess.
-            -->
-            <SSO entityID="https://idp.example.org/idp/shibboleth"
-                 discoveryProtocol="SAMLDS" discoveryURL="https://ds.example.org/DS/WAYF">
-              SAML2
-            </SSO>
-
-            <!-- SAML and local-only logout. -->
-            <Logout>SAML2 Local</Logout>
-
-            <!-- Administrative logout. -->
-            <LogoutInitiator type="Admin" Location="/Logout/Admin" acl="127.0.0.1 ::1" />
-          
-            <!-- Extension service that generates "approximate" metadata based on SP configuration. -->
-            <Handler type="MetadataGenerator" Location="/Metadata" signing="false"/>
-
-            <!-- Status reporting service. -->
-            <Handler type="Status" Location="/Status" acl="127.0.0.1 ::1"/>
-
-            <!-- Session diagnostic service. -->
-            <Handler type="Session" Location="/Session" showAttributeValues="false"/>
-
-            <!-- JSON feed of discovery information. -->
-            <Handler type="DiscoveryFeed" Location="/DiscoFeed"/>
-        </Sessions>
-
-        <!--
-        Allows overriding of error template information/filenames. You can
-        also add your own attributes with values that can be plugged into the
-        templates, e.g., helpLocation below.
-        -->
-        <Errors supportContact="root at localhost"
-            helpLocation="/about.html"
-            styleSheet="/shibboleth-sp/main.css"/>
-
-        <!-- Example of locally maintained metadata. -->
-        <!--
-        <MetadataProvider type="XML" validate="true" path="partner-metadata.xml"/>
-        -->
-
-        <!-- Example of remotely supplied batch of signed metadata. -->
-        <!--
-        <MetadataProvider type="XML" validate="true"
-	            url="http://federation.org/federation-metadata.xml"
-              backingFilePath="federation-metadata.xml" maxRefreshDelay="7200">
-            <MetadataFilter type="RequireValidUntil" maxValidityInterval="2419200"/>
-            <MetadataFilter type="Signature" certificate="fedsigner.pem" verifyBackup="false"/>
-            <DiscoveryFilter type="Blacklist" matcher="EntityAttributes" trimTags="true" 
-              attributeName="http://macedir.org/entity-category"
-              attributeNameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"
-              attributeValue="http://refeds.org/category/hide-from-discovery" />
-        </MetadataProvider>
-        -->
-
-        <!-- Example of remotely supplied "on-demand" signed metadata. -->
-        <!--
-        <MetadataProvider type="MDQ" validate="true" cacheDirectory="mdq"
-	            baseUrl="http://mdq.federation.org" ignoreTransport="true">
-            <MetadataFilter type="RequireValidUntil" maxValidityInterval="2419200"/>
-            <MetadataFilter type="Signature" certificate="mdqsigner.pem" />
-        </MetadataProvider>
-        -->
-
-        <!-- Map to extract attributes from SAML assertions. -->
-        <AttributeExtractor type="XML" validate="true" reloadChanges="false" path="attribute-map.xml"/>
-
-        <!-- Default filtering policy for recognized attributes, lets other data pass. -->
-        <AttributeFilter type="XML" validate="true" path="attribute-policy.xml"/>
-
-        <!-- Simple file-based resolvers for separate signing/encryption keys. -->
-        <CredentialResolver type="File" use="signing"
-            key="sp-signing-key.pem" certificate="sp-signing-cert.pem"/>
-        <CredentialResolver type="File" use="encryption"
-            key="sp-encrypt-key.pem" certificate="sp-encrypt-cert.pem"/>
-        
-    </ApplicationDefaults>
-    
-    <!-- Policies that determine how to process and authenticate runtime messages. -->
-    <SecurityPolicyProvider type="XML" validate="true" path="security-policy.xml"/>
-
-    <!-- Low-level configuration about protocols and bindings available for use. -->
-    <ProtocolProvider type="XML" validate="true" reloadChanges="false" path="protocols.xml"/>
-
-</SPConfig>
+<RequestMap xmlns="urn:mace:shibboleth:sp:requestmap:4.0" applicationId="default">
+    <Host name="www.example.org">
+        <Path name="secure" requireSession="true">
+            <Path name="apps">
+                <Path name="special" applicationId="example-special"/>
+            </Path>
+        </Path>
+    </Host>
+</RequestMap>

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


More information about the commits mailing list