[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