[java-identity-provider] branch master updated: IDP-701 First pass at SAML metadata for CAS.

Marvin S. Addison marvin.addison at gmail.com
Mon Jun 18 07:38:49 EDT 2018


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

serac pushed a commit to branch master
in repository java-identity-provider.

View the commit online:
http://git.shibboleth.net/view/?p=java-identity-provider.git;a=commit;h=cbfbd4c24e26d55d3de73f76a5d3916292096062

The following commit(s) were added to refs/heads/master by this push:
       new  cbfbd4c   IDP-701 First pass at SAML metadata for CAS.
cbfbd4c is described below

commit cbfbd4c24e26d55d3de73f76a5d3916292096062
Author: Marvin S. Addison <serac at vt.edu>
AuthorDate: Mon Jun 18 07:37:07 2018 -0400

    IDP-701 First pass at SAML metadata for CAS.
    
    Index AssertionConsumerService URLs of CAS-specific SPSSODescriptor and
    perform starts-with matching to find a matching entity.
---
 idp-cas-impl/pom.xml                               |   5 +
 .../flow/impl/BuildRelyingPartyContextAction.java  |  34 +++-
 .../cas/service/impl/MetadataServiceRegistry.java  | 192 +++++++++++++++++++++
 .../service/impl/MetadataServiceRegistryTest.java  | 107 ++++++++++++
 .../test/resources/metadata/cas-test-metadata.xml  | 174 +++++++++++++++++++
 idp-conf/src/main/resources/conf/cas-protocol.xml  |  10 ++
 .../resources/system/conf/cas-protocol-system.xml  |   7 +-
 .../main/resources/system/conf/services-system.xml |   4 +
 .../system/flows/cas/cas-abstract-beans.xml        |   4 +-
 9 files changed, 527 insertions(+), 10 deletions(-)

diff --git a/idp-cas-impl/pom.xml b/idp-cas-impl/pom.xml
index 06d501f..6707b15 100644
--- a/idp-cas-impl/pom.xml
+++ b/idp-cas-impl/pom.xml
@@ -49,6 +49,11 @@
             <version>${opensaml.version}</version>
         </dependency>
         <dependency>
+            <groupId>${opensaml.groupId}</groupId>
+            <artifactId>opensaml-saml-impl</artifactId>
+            <version>${opensaml.version}</version>
+        </dependency>
+        <dependency>
             <groupId>${httpclient.groupId}</groupId>
             <artifactId>httpclient</artifactId>
         </dependency>
diff --git a/idp-cas-impl/src/main/java/net/shibboleth/idp/cas/flow/impl/BuildRelyingPartyContextAction.java b/idp-cas-impl/src/main/java/net/shibboleth/idp/cas/flow/impl/BuildRelyingPartyContextAction.java
index 21928a2..d23eab8 100644
--- a/idp-cas-impl/src/main/java/net/shibboleth/idp/cas/flow/impl/BuildRelyingPartyContextAction.java
+++ b/idp-cas-impl/src/main/java/net/shibboleth/idp/cas/flow/impl/BuildRelyingPartyContextAction.java
@@ -17,6 +17,8 @@
 
 package net.shibboleth.idp.cas.flow.impl;
 
+import java.util.Arrays;
+import java.util.List;
 import javax.annotation.Nonnull;
 
 import net.shibboleth.idp.cas.protocol.ProxyTicketRequest;
@@ -25,15 +27,19 @@ import net.shibboleth.idp.cas.protocol.TicketValidationRequest;
 import net.shibboleth.idp.cas.service.Service;
 import net.shibboleth.idp.cas.service.ServiceRegistry;
 import net.shibboleth.idp.profile.context.RelyingPartyContext;
+import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
 import net.shibboleth.utilities.java.support.logic.Constraint;
 import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.saml.metadata.resolver.MetadataResolver;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.webflow.execution.Event;
 import org.springframework.webflow.execution.RequestContext;
 
 /**
- * Creates the {@link RelyingPartyContext} as a child of the {@link ProfileRequestContext}.
+ * Creates the {@link RelyingPartyContext} as a child of the {@link ProfileRequestContext}. The component queries
+ * a configured list of {@link ServiceRegistry} until a result is found, otherwise the relying party is treated as
+ * unverified.
  *
  * @author Marvin S. Addison
  */
@@ -45,17 +51,19 @@ public class BuildRelyingPartyContextAction extends AbstractCASProtocolAction {
     /** Class logger. */
     private final Logger log = LoggerFactory.getLogger(BuildRelyingPartyContextAction.class);
 
-    /** Repository for verified CAS services (relying parties). */
+    /** List of registries to query for verified CAS services (relying parties). */
     @Nonnull
-    private final ServiceRegistry serviceRegistry;
+    @NotEmpty
+    private final List<ServiceRegistry> serviceRegistries;
+
 
     /**
      * Creates a new instance.
      *
-     * @param registry Service registry.
+     * @param registries One or more service registries to query for CAS services.
      */
-    public BuildRelyingPartyContextAction(@Nonnull final ServiceRegistry registry) {
-        this.serviceRegistry = Constraint.isNotNull(registry, "Service registry cannot be null");
+    public BuildRelyingPartyContextAction(@Nonnull @NotEmpty final ServiceRegistry ... registries) {
+        serviceRegistries = Arrays.asList(Constraint.isNotEmpty(registries, "Service registries cannot be null"));
     }
 
     @Nonnull
@@ -75,7 +83,7 @@ public class BuildRelyingPartyContextAction extends AbstractCASProtocolAction {
         } else {
             throw new IllegalStateException("Service URL not found in flow state");
         }
-        Service service = serviceRegistry.lookup(serviceURL);
+        Service service = query(serviceURL);
         final RelyingPartyContext rpc = new RelyingPartyContext();
         rpc.setVerified(service != null);
         rpc.setRelyingPartyId(serviceURL);
@@ -85,8 +93,20 @@ public class BuildRelyingPartyContextAction extends AbstractCASProtocolAction {
             service = new Service(serviceURL, UNVERIFIED_GROUP, false);
             log.debug("Setting up RP context for unverified relying party {}", service);
         }
+        log.debug("Relying party context created for {}", service);
         profileRequestContext.addSubcontext(rpc);
         setCASService(profileRequestContext, service);
         return null;
     }
+
+    private Service query(final String serviceURL) {
+        for (ServiceRegistry registry : serviceRegistries) {
+            log.debug("Querying {} for CAS service URL {}", registry.getClass().getName(), serviceURL);
+            final Service service = registry.lookup(serviceURL);
+            if (service != null) {
+                return service;
+            }
+        }
+        return null;
+    }
 }
diff --git a/idp-cas-impl/src/main/java/net/shibboleth/idp/cas/service/impl/MetadataServiceRegistry.java b/idp-cas-impl/src/main/java/net/shibboleth/idp/cas/service/impl/MetadataServiceRegistry.java
new file mode 100644
index 0000000..f286e00
--- /dev/null
+++ b/idp-cas-impl/src/main/java/net/shibboleth/idp/cas/service/impl/MetadataServiceRegistry.java
@@ -0,0 +1,192 @@
+/*
+ * 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.cas.service.impl;
+
+import java.util.List;
+import java.util.Objects;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import com.google.common.collect.Lists;
+import net.shibboleth.idp.cas.config.impl.AbstractProtocolConfiguration;
+import net.shibboleth.idp.cas.service.Service;
+import net.shibboleth.idp.cas.service.ServiceRegistry;
+import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
+import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
+import net.shibboleth.utilities.java.support.resolver.ResolverException;
+import org.opensaml.core.xml.XMLObject;
+import org.opensaml.core.xml.schema.XSAny;
+import org.opensaml.saml.criterion.EndpointCriterion;
+import org.opensaml.saml.criterion.EntityRoleCriterion;
+import org.opensaml.saml.criterion.ProtocolCriterion;
+import org.opensaml.saml.criterion.StartsWithLocationCriterion;
+import org.opensaml.saml.ext.saml2mdattr.EntityAttributes;
+import org.opensaml.saml.metadata.resolver.MetadataResolver;
+import org.opensaml.saml.saml2.core.Attribute;
+import org.opensaml.saml.saml2.metadata.AssertionConsumerService;
+import org.opensaml.saml.saml2.metadata.EntitiesDescriptor;
+import org.opensaml.saml.saml2.metadata.EntityDescriptor;
+import org.opensaml.saml.saml2.metadata.Extensions;
+import org.opensaml.saml.saml2.metadata.SPSSODescriptor;
+import org.opensaml.saml.saml2.metadata.SingleLogoutService;
+import org.opensaml.saml.saml2.metadata.impl.AssertionConsumerServiceBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * CAS service registry implementation that queries SAML metadata for a CAS service given a CAS service URL using
+ * the following strategy. A {@link MetadataResolver} is queried for an {@link EntityDescriptor} that contains at
+ * least one <code>AssertionConsumerService</code> endpoint that meets the following criteria:
+ *
+ * <ol>
+ *     <li>Defines <code>https://www.apereo.org/cas/protocol</code> in its <code>protocolSupportEnumeration</code>
+ *         attribute.</li>
+ *     <li>Defines a <code>Location</code> URL where the given service URL starts with the ACS URL.</li>
+ * </ol>
+ *
+ * If a single match is found, it is converted to a {@link Service} and returned; if more than result is found, a
+ * {@link ResolverException} is raised, otherwise null is returned.
+ *
+ * @author Marvin S. Addison
+ */
+public class MetadataServiceRegistry implements ServiceRegistry {
+
+    /** Metadata attribute used to tag an entity as authorized to request proxy-granting tickets. */
+    private static final String PROXY_ATTRIBUTE = AbstractProtocolConfiguration.PROTOCOL_URI + "/authorizedToProxy";
+
+    /** Class logger. */
+    private final Logger log = LoggerFactory.getLogger(MetadataServiceRegistry.class);
+
+    /** SAML metadata resolver. */
+    @Nonnull
+    private final MetadataResolver metadataResolver;
+
+
+    /**
+     * Create a new instance that queries the given metadata resolver.
+     *
+     * @param resolver SAML metadata resolver.
+     */
+    public MetadataServiceRegistry(@Nonnull final MetadataResolver resolver) {
+        metadataResolver = resolver;
+    }
+
+    @Nullable
+    @Override
+    public Service lookup(@Nonnull String serviceURL) {
+        try {
+            final List<EntityDescriptor> entities = Lists.newArrayList(metadataResolver.resolve(criteria(serviceURL)));
+            if (entities.size() > 1) {
+                throw new ResolverException("Multiple results found");
+            } else if (entities.size() == 1) {
+                return create(serviceURL, entities.get(0));
+            }
+        } catch (ResolverException e) {
+            log.warn("Metadata resolution failed for {}", serviceURL, e);
+        }
+        return null;
+    }
+
+    /**
+     * Create the set of criteria used to find a unique CAS service given a CAS service URL.
+     *
+     * @param serviceURL CAS service URL.
+     *
+     * @return Metadata resolver criteria set.
+     */
+    @Nonnull
+    protected CriteriaSet criteria(@Nonnull final String serviceURL) {
+        final AssertionConsumerService acs = new AssertionConsumerServiceBuilder().buildObject();
+        acs.setLocation(serviceURL);
+        return new CriteriaSet(
+                new EntityRoleCriterion(SPSSODescriptor.DEFAULT_ELEMENT_NAME),
+                new EndpointCriterion<>(acs),
+                new ProtocolCriterion(AbstractProtocolConfiguration.PROTOCOL_URI),
+                new StartsWithLocationCriterion()
+        );
+    }
+
+    /**
+     * Create a CAS {@link Service} from an input service URL and the matching {@link EntityDescriptor} that was
+     * resolved from the metadata source.
+     *
+     * @param serviceURL CAS service URL.
+     * @param entity Entity resolved from metadata.
+     *
+     * @return CAS service created from inputs.
+     */
+    @Nonnull
+    protected Service create(@Nonnull final String serviceURL, @Nonnull final EntityDescriptor entity) {
+        final XMLObject parent = entity.getParent();
+        return new Service(
+                serviceURL,
+                parent instanceof EntitiesDescriptor ? ((EntitiesDescriptor) parent).getName() : "unknown",
+                isAllowedToProxy(entity),
+                hasSingleLogoutService(entity));
+    }
+
+    private boolean hasSingleLogoutService(@Nonnull final EntityDescriptor entity) {
+        final SPSSODescriptor casSP = entity.getSPSSODescriptor(AbstractProtocolConfiguration.PROTOCOL_URI);
+        if (casSP != null) {
+            return casSP.getEndpoints(SingleLogoutService.DEFAULT_ELEMENT_NAME).size() > 0;
+        }
+        return false;
+    }
+
+    private boolean isAllowedToProxy(@Nonnull final EntityDescriptor entity) {
+        final Attribute allowedToProxy = findAttribute(entity, PROXY_ATTRIBUTE);
+        if (allowedToProxy != null) {
+            final XMLObject first = allowedToProxy.getAttributeValues().iterator().next();
+            if (first instanceof XSAny) {
+                return Boolean.parseBoolean(((XSAny) first).getTextContent());
+            } else {
+                throw new RuntimeException("Expected boolean value for " + PROXY_ATTRIBUTE);
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Find a matching entity attribute in the input metadata.
+     *
+     * @param entity the metadata to examine
+     * @param name the attribute name to search for
+     *
+     * @return matching attribute or null
+     */
+    @Nullable
+    private Attribute findAttribute(
+            @Nonnull final EntityDescriptor entity, @Nonnull @NotEmpty final String name) {
+
+        // Check for a tag match in the EntityAttributes extension of the entity and its parent(s).
+        Extensions exts = entity.getExtensions();
+        if (exts != null) {
+            final List<XMLObject> children = exts.getUnknownXMLObjects(EntityAttributes.DEFAULT_ELEMENT_NAME);
+            if (!children.isEmpty() && children.get(0) instanceof EntityAttributes) {
+                final EntityAttributes ea = (EntityAttributes) children.get(0);
+                for (Attribute attribute : ea.getAttributes()) {
+                    if (Objects.equals(attribute.getName(), name) &&
+                        Objects.equals(attribute.getNameFormat(), Attribute.URI_REFERENCE)) {
+                        return attribute;
+                    }
+                }
+            }
+        }
+        return null;
+    }
+}
diff --git a/idp-cas-impl/src/test/java/net/shibboleth/idp/cas/service/impl/MetadataServiceRegistryTest.java b/idp-cas-impl/src/test/java/net/shibboleth/idp/cas/service/impl/MetadataServiceRegistryTest.java
new file mode 100644
index 0000000..e2d5dad
--- /dev/null
+++ b/idp-cas-impl/src/test/java/net/shibboleth/idp/cas/service/impl/MetadataServiceRegistryTest.java
@@ -0,0 +1,107 @@
+/*
+ * 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.cas.service.impl;
+
+import java.util.Collections;
+import java.util.Timer;
+
+import net.shibboleth.ext.spring.resource.ResourceHelper;
+import net.shibboleth.idp.cas.service.Service;
+import net.shibboleth.utilities.java.support.resource.Resource;
+import org.opensaml.core.config.InitializationException;
+import org.opensaml.core.config.InitializationService;
+import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
+import org.opensaml.saml.metadata.resolver.impl.ResourceBackedMetadataResolver;
+import org.opensaml.saml.metadata.resolver.index.MetadataIndex;
+import org.opensaml.saml.metadata.resolver.index.impl.EndpointMetadataIndex;
+import org.springframework.core.io.ClassPathResource;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeSuite;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.*;
+
+/**
+ * Unit test for {@link MetadataServiceRegistry}.
+ *
+ * @author Marvin S. Addison
+ */
+public class MetadataServiceRegistryTest {
+
+    private ResourceBackedMetadataResolver metadataResolver;
+
+    @DataProvider(name = "parameters")
+    public Object[][] parameters() {
+        final String group = "urn:mace:example.org";
+        return new Object[][] {
+            {"https://alpha.example.org/", new Service("https://alpha.example.org/", group, true, true)},
+            {"https://alpha.example.org/a/b/", new Service("https://alpha.example.org/a/b/", group, true, true)},
+            {"https://alpha.dev.example.org/", new Service("https://alpha.dev.example.org/", group, true, true)},
+            {"https://alpha.dev.example.org/#1", new Service("https://alpha.dev.example.org/#1", group, true, true)},
+            {"https://alpha.dev.example.org", null},
+            {"https://beta.example.org/", new Service("https://beta.example.org/", group, false)},
+            {
+                "https://betatest.example.org:8443/a?b=2",
+                new Service("https://betatest.example.org:8443/a?b=2", group, false),
+            },
+        };
+    }
+
+    /**
+     *  Initialize OpenSAML.
+     *
+     * @throws InitializationException
+     */
+    @BeforeSuite
+    public void initOpenSAML() throws InitializationException {
+        InitializationService.initialize();
+    }
+
+    @BeforeClass
+    public void setUp() throws Exception {
+        final Resource metadata = ResourceHelper.of(new ClassPathResource("/metadata/cas-test-metadata.xml"));
+        metadataResolver = new ResourceBackedMetadataResolver(new Timer(true), metadata);
+        metadataResolver.setParserPool(XMLObjectProviderRegistrySupport.getParserPool());
+        metadataResolver.setMaxRefreshDelay(500000);
+        metadataResolver.setId("cas");
+        metadataResolver.setIndexes(Collections.<MetadataIndex>singleton(new EndpointMetadataIndex()));
+        metadataResolver.initialize();
+    }
+
+    @AfterClass
+    public void tearDown() {
+        metadataResolver.destroy();
+    }
+
+    @Test(dataProvider = "parameters")
+    public void testLookup(final String serviceURL, final Service expected) {
+        final MetadataServiceRegistry registry = new MetadataServiceRegistry(metadataResolver);
+        final Service actual = registry.lookup(serviceURL);
+        if (expected == null) {
+            assertNull(actual);
+        } else {
+            assertNotNull(actual);
+            assertEquals(actual.getName(), expected.getName());
+            assertEquals(actual.getGroup(), expected.getGroup());
+            assertEquals(actual.isAuthorizedToProxy(), expected.isAuthorizedToProxy());
+            assertEquals(actual.isSingleLogoutParticipant(), expected.isSingleLogoutParticipant());
+        }
+    }
+}
\ No newline at end of file
diff --git a/idp-cas-impl/src/test/resources/metadata/cas-test-metadata.xml b/idp-cas-impl/src/test/resources/metadata/cas-test-metadata.xml
new file mode 100644
index 0000000..7855d7c
--- /dev/null
+++ b/idp-cas-impl/src/test/resources/metadata/cas-test-metadata.xml
@@ -0,0 +1,174 @@
+<EntitiesDescriptor
+        Name="urn:mace:example.org"
+        xmlns="urn:oasis:names:tc:SAML:2.0:metadata"
+        xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
+        xmlns:mdattr="urn:oasis:names:tc:SAML:metadata:attribute"
+        xmlns:mdui="urn:oasis:names:tc:SAML:metadata:ui"
+        xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
+        validUntil="2050-12-31T23:59:59Z">
+
+    <!--
+       | Middleware test SP shib-sp.middleware.vt.edu
+       -->
+    <EntityDescriptor entityID="https://shib-sp.middleware.vt.edu/shibboleth">
+        <SPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"
+                         AuthnRequestsSigned="false"
+                         WantAssertionsSigned="false">
+            <Extensions>
+                <mdui:UIInfo>
+                    <mdui:DisplayName xml:lang="en">Middleware Test Shibboelth SP</mdui:DisplayName>
+                </mdui:UIInfo>
+            </Extensions>
+            <KeyDescriptor use="signing">
+                <ds:KeyInfo>
+                    <ds:KeyName>shib-sp.middleware.vt.edu</ds:KeyName>
+                    <ds:X509Data>
+                        <ds:X509SubjectName>CN=shib-sp.middleware.vt.edu</ds:X509SubjectName>
+                        <ds:X509IssuerSerial>
+                            <ds:X509IssuerName>CN=shib-sp.middleware.vt.edu</ds:X509IssuerName>
+                            <ds:X509SerialNumber>13911054624363863887</ds:X509SerialNumber>
+                        </ds:X509IssuerSerial>
+                        <ds:X509Certificate>
+                            MIIDEjCCAfqgAwIBAgIJAMEN/noYQ19PMA0GCSqGSIb3DQEBBQUAMCQxIjAgBgNV
+                            BAMTGXNoaWItc3AubWlkZGxld2FyZS52dC5lZHUwHhcNMDkxMDEyMTkwOTI5WhcN
+                            MTkxMDEwMTkwOTI5WjAkMSIwIAYDVQQDExlzaGliLXNwLm1pZGRsZXdhcmUudnQu
+                            ZWR1MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAq1+/HSUxoAKfvUi1
+                            D+D0YMVQqaGcWmXndnsR4dBuHaVsl48F6c428bJAwC/u5KEO74PiNFw5tQIY3Msk
+                            pR0CGM41Hj9f+jZF5K0Q9QdHOQEyNJjQtLCwyXXSJzvOHVgZhTbyrTyGCxCqs0jb
+                            w9oCLLtclZcSxO/OgC/PpYN03juTu7EsoyTqB1zkAz+LXM8nd3/egts7EGzVmmo4
+                            n4fAwtfeNjwHxdonRj99UvDernxTe9o671qPcTOSyaAgxC8k3XN1CXFArWDF0aHH
+                            ZuYyfLf2ANvI0EHtvBTkwtjSK32hEAK90FU2AVw4KS4JO/+5kO0E7AfQdefCK5WR
+                            pbaO8wIDAQABo0cwRTAkBgNVHREEHTAbghlzaGliLXNwLm1pZGRsZXdhcmUudnQu
+                            ZWR1MB0GA1UdDgQWBBSkQ5Kf9DS2iZPBzxaM8hgarRUGKjANBgkqhkiG9w0BAQUF
+                            AAOCAQEAM6rz9hcTaDj521/q2BIPmx2Kw3nhmcUgi/tjrBsaR2UeGLwH1NokEr0r
+                            9eiJNRLO8JpnVEFRJ3DOJdgYoUD9Ic6HYBAo0ezPCRPHQ+ugh5vqKMypV7syTz8R
+                            q1F3BuT9gGRNiYosZl/Zm+Gaf6rTwXIOq45RJOTd9m1maM0TSkl4eR13hOewR0ry
+                            BomWaAR/z2m4k1eAggeH1gRufI7NiDx2UfYlrCdU1aTXHkx9i2Wcz4NZu8o3mFOY
+                            Q0cjExJWVeywifStwQT+JOALdQWBIVX56d8n+XH8f6ChJKqP+KZXtUuDf5ARdHIN
+                            L4EFJSmh5lpx88LwgxTsPXUpvcARfQ==
+                        </ds:X509Certificate>
+                    </ds:X509Data>
+                </ds:KeyInfo>
+            </KeyDescriptor>
+            <KeyDescriptor use="encryption">
+                <ds:KeyInfo>
+                    <ds:KeyName>shib-sp.middleware.vt.edu</ds:KeyName>
+                    <ds:X509Data>
+                        <ds:X509SubjectName>CN=shib-sp.middleware.vt.edu</ds:X509SubjectName>
+                        <ds:X509IssuerSerial>
+                            <ds:X509IssuerName>CN=shib-sp.middleware.vt.edu</ds:X509IssuerName>
+                            <ds:X509SerialNumber>13911054624363863887</ds:X509SerialNumber>
+                        </ds:X509IssuerSerial>
+                        <ds:X509Certificate>
+                            MIIDEjCCAfqgAwIBAgIJAMEN/noYQ19PMA0GCSqGSIb3DQEBBQUAMCQxIjAgBgNV
+                            BAMTGXNoaWItc3AubWlkZGxld2FyZS52dC5lZHUwHhcNMDkxMDEyMTkwOTI5WhcN
+                            MTkxMDEwMTkwOTI5WjAkMSIwIAYDVQQDExlzaGliLXNwLm1pZGRsZXdhcmUudnQu
+                            ZWR1MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAq1+/HSUxoAKfvUi1
+                            D+D0YMVQqaGcWmXndnsR4dBuHaVsl48F6c428bJAwC/u5KEO74PiNFw5tQIY3Msk
+                            pR0CGM41Hj9f+jZF5K0Q9QdHOQEyNJjQtLCwyXXSJzvOHVgZhTbyrTyGCxCqs0jb
+                            w9oCLLtclZcSxO/OgC/PpYN03juTu7EsoyTqB1zkAz+LXM8nd3/egts7EGzVmmo4
+                            n4fAwtfeNjwHxdonRj99UvDernxTe9o671qPcTOSyaAgxC8k3XN1CXFArWDF0aHH
+                            ZuYyfLf2ANvI0EHtvBTkwtjSK32hEAK90FU2AVw4KS4JO/+5kO0E7AfQdefCK5WR
+                            pbaO8wIDAQABo0cwRTAkBgNVHREEHTAbghlzaGliLXNwLm1pZGRsZXdhcmUudnQu
+                            ZWR1MB0GA1UdDgQWBBSkQ5Kf9DS2iZPBzxaM8hgarRUGKjANBgkqhkiG9w0BAQUF
+                            AAOCAQEAM6rz9hcTaDj521/q2BIPmx2Kw3nhmcUgi/tjrBsaR2UeGLwH1NokEr0r
+                            9eiJNRLO8JpnVEFRJ3DOJdgYoUD9Ic6HYBAo0ezPCRPHQ+ugh5vqKMypV7syTz8R
+                            q1F3BuT9gGRNiYosZl/Zm+Gaf6rTwXIOq45RJOTd9m1maM0TSkl4eR13hOewR0ry
+                            BomWaAR/z2m4k1eAggeH1gRufI7NiDx2UfYlrCdU1aTXHkx9i2Wcz4NZu8o3mFOY
+                            Q0cjExJWVeywifStwQT+JOALdQWBIVX56d8n+XH8f6ChJKqP+KZXtUuDf5ARdHIN
+                            L4EFJSmh5lpx88LwgxTsPXUpvcARfQ==
+                        </ds:X509Certificate>
+                    </ds:X509Data>
+                </ds:KeyInfo>
+            </KeyDescriptor>
+            <SingleLogoutService
+                    Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP"
+                    Location="https://shib-sp.middleware.vt.edu:8443/Shibboleth.sso/SLO/SOAP"/>
+            <SingleLogoutService
+                    Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
+                    Location="https://shib-sp.middleware.vt.edu:8443/Shibboleth.sso/SLO/Redirect"/>
+            <SingleLogoutService
+                    Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
+                    Location="https://shib-sp.middleware.vt.edu:8443/Shibboleth.sso/SLO/POST"/>
+            <SingleLogoutService
+                    Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact"
+                    Location="https://shib-sp.middleware.vt.edu:8443/Shibboleth.sso/SLO/Artifact"/>
+            <AssertionConsumerService
+                    Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
+                    Location="https://shib-sp.middleware.vt.edu:8443/Shibboleth.sso/SAML2/POST"
+                    index="1"/>
+            <AssertionConsumerService
+                    Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST-SimpleSign"
+                    Location="https://shib-sp.middleware.vt.edu:8443/Shibboleth.sso/SAML2/POST-SimpleSign"
+                    index="2"/>
+            <AssertionConsumerService
+                    Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact"
+                    Location="https://shib-sp.middleware.vt.edu:8443/Shibboleth.sso/SAML2/Artifact"
+                    index="3"/>
+            <AssertionConsumerService
+                    Binding="urn:oasis:names:tc:SAML:2.0:bindings:PAOS"
+                    Location="https://shib-sp.middleware.vt.edu:8443/Shibboleth.sso/SAML2/ECP"
+                    index="4"/>
+            <AssertionConsumerService
+                    Binding="urn:oasis:names:tc:SAML:1.0:profiles:browser-post"
+                    Location="https://shib-sp.middleware.vt.edu:8443/Shibboleth.sso/SAML/POST"
+                    index="5"/>
+            <AssertionConsumerService
+                    Binding="urn:oasis:names:tc:SAML:1.0:profiles:artifact-01"
+                    Location="https://shib-sp.middleware.vt.edu:8443/Shibboleth.sso/SAML/Artifact"
+                    index="6"/>
+        </SPSSODescriptor>
+    </EntityDescriptor>
+
+    <!--
+       | Alpha (authorizedToProxy="true", singleLogoutParticipant="true")
+       -->
+    <EntityDescriptor entityID="https://alpha.example.org/">
+        <Extensions>
+            <mdattr:EntityAttributes>
+                <saml:Attribute Name="https://www.apereo.org/cas/protocol/authorizedToProxy"
+                                NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
+                    <saml:AttributeValue>true</saml:AttributeValue>
+                </saml:Attribute>
+            </mdattr:EntityAttributes>
+        </Extensions>
+        <SPSSODescriptor protocolSupportEnumeration="https://www.apereo.org/cas/protocol">
+            <AssertionConsumerService
+                    Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact"
+                    Location="https://alpha.example.org/"
+                    index="1"/>
+            <AssertionConsumerService
+                    Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact"
+                    Location="https://alpha.dev.example.org/"
+                    index="2"/>
+            <AssertionConsumerService
+                    Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact"
+                    Location="https://alpha.test.example.org/"
+                    index="3"/>
+            <SingleLogoutService
+                    Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP"
+                    Location="https://not.used.invalid/"/>
+        </SPSSODescriptor>
+    </EntityDescriptor>
+
+    <!--
+       | Beta (authorizedToProxy="false", singleLogoutParticipant="false")
+       -->
+    <EntityDescriptor entityID="https://beta.example.org/">
+        <SPSSODescriptor protocolSupportEnumeration="https://www.apereo.org/cas/protocol">
+            <AssertionConsumerService
+                    Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact"
+                    Location="https://beta.example.org/"
+                    index="1"/>
+            <AssertionConsumerService
+                    Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact"
+                    Location="https://betatest.example.org/"
+                    index="2"/>
+            <AssertionConsumerService
+                    Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact"
+                    Location="https://betatest.example.org:8443/"
+                    index="3"/>
+        </SPSSODescriptor>
+    </EntityDescriptor>
+
+</EntitiesDescriptor>
diff --git a/idp-conf/src/main/resources/conf/cas-protocol.xml b/idp-conf/src/main/resources/conf/cas-protocol.xml
index 1c2ce92..bddae19 100644
--- a/idp-conf/src/main/resources/conf/cas-protocol.xml
+++ b/idp-conf/src/main/resources/conf/cas-protocol.xml
@@ -36,6 +36,16 @@
     </bean>
 
     <!--
+       | Uncomment this bean if you want to override the default list of CAS service registries.
+       | The default configuration tries to find the relying party in a SAML metadata source and falls back to
+       | reloadableServiceRegistry if a match is not found.
+       -->
+    <!--<util:list id="shibboleth.CASServiceRegistries">
+        <ref bean="shibboleth.CASMetadataServiceRegistry" />
+        <ref bean="shibboleth.CASServiceRegistry" />
+    </util:list>-->
+
+    <!--
        | The default ticket service as of 3.3.0 serializes ticket data into the opaque section of the ticket ID
        | for service tickets and proxy tickets. Proxy-granting tickets still require server-side storage, and
        | a StorageService defined by the idp.cas.StorageService is used. Thus for deployers that do not require
diff --git a/idp-conf/src/main/resources/system/conf/cas-protocol-system.xml b/idp-conf/src/main/resources/system/conf/cas-protocol-system.xml
index 3591d0b..f8ddc30 100644
--- a/idp-conf/src/main/resources/system/conf/cas-protocol-system.xml
+++ b/idp-conf/src/main/resources/system/conf/cas-protocol-system.xml
@@ -3,8 +3,8 @@
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:c="http://www.springframework.org/schema/c"
        xmlns:p="http://www.springframework.org/schema/p"
+       xmlns:util="http://www.springframework.org/schema/util"
 	   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"
@@ -46,6 +46,11 @@
     <bean id="shibboleth.DefaultCASServiceComparator"
           class="net.shibboleth.idp.cas.service.impl.DefaultServiceComparator" />
 
+    <util:list id="shibboleth.DefaultCASServiceRegistries">
+        <ref bean="shibboleth.CASMetadataServiceRegistry" />
+        <ref bean="shibboleth.CASServiceRegistry" />
+    </util:list>
+
     <import resource="../../conf/cas-protocol.xml" />
 
 </beans>
\ No newline at end of file
diff --git a/idp-conf/src/main/resources/system/conf/services-system.xml b/idp-conf/src/main/resources/system/conf/services-system.xml
index 198ac1c..224a452 100644
--- a/idp-conf/src/main/resources/system/conf/services-system.xml
+++ b/idp-conf/src/main/resources/system/conf/services-system.xml
@@ -149,4 +149,8 @@
           class="net.shibboleth.idp.cas.service.impl.ReloadingServiceRegistry"
           c:delegate-ref="shibboleth.ReloadableCASServiceRegistry" />
 
+    <bean id="shibboleth.CASMetadataServiceRegistry"
+          class="net.shibboleth.idp.cas.service.impl.MetadataServiceRegistry"
+          c:resolver-ref="shibboleth.MetadataResolver" />
+
  </beans>
diff --git a/idp-conf/src/main/resources/system/flows/cas/cas-abstract-beans.xml b/idp-conf/src/main/resources/system/flows/cas/cas-abstract-beans.xml
index b056b20..f7c84e8 100644
--- a/idp-conf/src/main/resources/system/flows/cas/cas-abstract-beans.xml
+++ b/idp-conf/src/main/resources/system/flows/cas/cas-abstract-beans.xml
@@ -24,10 +24,10 @@
     <bean id="PopulateMetricContext"
         class="org.opensaml.profile.action.impl.PopulateMetricContext" scope="prototype"
         p:metricStrategy="#{getObject('shibboleth.metrics.MetricStrategy')}" />
-        
+
     <bean id="BuildRelyingPartyContext"
           class="net.shibboleth.idp.cas.flow.impl.BuildRelyingPartyContextAction"
-          c:registry-ref="shibboleth.CASServiceRegistry"/>
+          c:registries="#{getObject('shibboleth.CASServiceRegistries') ?: getObject('shibboleth.DefaultCASServiceRegistries')}" />
 
     <bean id="BuildSAMLMetadataContext"
           class="net.shibboleth.idp.cas.flow.impl.BuildSAMLMetadataContextAction" />

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


More information about the commits mailing list