[java-identity-provider] branch master updated: IDP-701 Signing certificates imply CAS proxy authz.

Marvin S. Addison marvin.addison at gmail.com
Mon Jun 18 16:32:29 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=2bc5501ca5f42d234044943182fade8d73dba00d

The following commit(s) were added to refs/heads/master by this push:
       new  2bc5501   IDP-701 Signing certificates imply CAS proxy authz.
2bc5501 is described below

commit 2bc5501ca5f42d234044943182fade8d73dba00d
Author: Marvin S. Addison <serac at vt.edu>
AuthorDate: Mon Jun 18 16:29:39 2018 -0400

    IDP-701 Signing certificates imply CAS proxy authz.
    
    Let the presence of signing certificates in the SAML metadata for
    a CAS endpoint imply proxy authorization. It remains to be done to
    use the certificates as trust material for validating proxy callback
    URLs.
---
 .../net/shibboleth/idp/cas/service/Service.java    | 24 ++++++
 .../flow/impl/BuildSAMLMetadataContextAction.java  |  8 +-
 .../cas/service/impl/MetadataServiceRegistry.java  | 88 ++++++++--------------
 .../test/resources/metadata/cas-test-metadata.xml  | 78 +++++++++++++++++--
 4 files changed, 132 insertions(+), 66 deletions(-)

diff --git a/idp-cas-api/src/main/java/net/shibboleth/idp/cas/service/Service.java b/idp-cas-api/src/main/java/net/shibboleth/idp/cas/service/Service.java
index b427c1a..5c3cd8a 100644
--- a/idp-cas-api/src/main/java/net/shibboleth/idp/cas/service/Service.java
+++ b/idp-cas-api/src/main/java/net/shibboleth/idp/cas/service/Service.java
@@ -20,6 +20,7 @@ package net.shibboleth.idp.cas.service;
 import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
 import net.shibboleth.utilities.java.support.logic.Constraint;
 import net.shibboleth.utilities.java.support.primitive.StringSupport;
+import org.opensaml.saml.saml2.metadata.EntityDescriptor;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
@@ -47,6 +48,10 @@ public class Service implements Principal {
     /** Indicates whether a service wants to receive SLO messages. */
     private final boolean singleLogoutParticipant;
 
+    /** Source of service metadata derived from a SAML entity. */
+    @Nullable
+    private transient EntityDescriptor entityDescriptor;
+
 
     /**
      * Creates a new service that does not participate in SLO.
@@ -117,6 +122,25 @@ public class Service implements Principal {
         return singleLogoutParticipant;
     }
 
+    /**
+     * Gets the SAML entity that is the source of service metadata.
+     *
+     * @return Entity descriptor for service defined in SAML metadata, otherwise null.
+     */
+    @Nullable
+    public EntityDescriptor getEntityDescriptor() {
+        return entityDescriptor;
+    }
+
+    /**
+     * Sets the SAML entity that is the source of service metadata.
+     *
+     * @param ed SAML entity descriptor.
+     */
+    public void setEntityDescriptor(@Nullable final EntityDescriptor ed) {
+        this.entityDescriptor = ed;
+    }
+
     @Override
     public String toString() {
         return serviceURL;
diff --git a/idp-cas-impl/src/main/java/net/shibboleth/idp/cas/flow/impl/BuildSAMLMetadataContextAction.java b/idp-cas-impl/src/main/java/net/shibboleth/idp/cas/flow/impl/BuildSAMLMetadataContextAction.java
index 2cd43f1..ecc10cb 100644
--- a/idp-cas-impl/src/main/java/net/shibboleth/idp/cas/flow/impl/BuildSAMLMetadataContextAction.java
+++ b/idp-cas-impl/src/main/java/net/shibboleth/idp/cas/flow/impl/BuildSAMLMetadataContextAction.java
@@ -20,10 +20,12 @@ package net.shibboleth.idp.cas.flow.impl;
 import javax.annotation.Nonnull;
 
 import net.shibboleth.idp.cas.protocol.ProtocolError;
+import net.shibboleth.idp.cas.service.Service;
 import net.shibboleth.idp.cas.service.impl.ServiceEntityDescriptor;
 import net.shibboleth.idp.profile.context.RelyingPartyContext;
 import org.opensaml.profile.context.ProfileRequestContext;
 import org.opensaml.saml.common.messaging.context.SAMLMetadataContext;
+import org.opensaml.saml.saml2.metadata.EntityDescriptor;
 import org.springframework.webflow.execution.Event;
 import org.springframework.webflow.execution.RequestContext;
 
@@ -48,7 +50,11 @@ public class BuildSAMLMetadataContextAction extends AbstractCASProtocolAction {
             throw new IllegalStateException("RelyingPartyContext not found");
         }
         final SAMLMetadataContext mdCtx = new SAMLMetadataContext();
-        mdCtx.setEntityDescriptor(new ServiceEntityDescriptor(getCASService(profileRequestContext)));
+        final Service service = getCASService(profileRequestContext);
+        final EntityDescriptor entity = service.getEntityDescriptor() != null
+                ? service.getEntityDescriptor()
+                : new ServiceEntityDescriptor(service);
+        mdCtx.setEntityDescriptor(entity);
         rpCtx.setRelyingPartyIdContextTree(mdCtx);
 
         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
index f286e00..63a2c96 100644
--- 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
@@ -17,8 +17,12 @@
 
 package net.shibboleth.idp.cas.service.impl;
 
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.HashSet;
 import java.util.List;
-import java.util.Objects;
+import java.util.Set;
+
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
@@ -26,25 +30,22 @@ 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.KeyDescriptor;
 import org.opensaml.saml.saml2.metadata.SPSSODescriptor;
 import org.opensaml.saml.saml2.metadata.SingleLogoutService;
 import org.opensaml.saml.saml2.metadata.impl.AssertionConsumerServiceBuilder;
+import org.opensaml.xmlsec.keyinfo.KeyInfoSupport;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -66,9 +67,6 @@ import org.slf4j.LoggerFactory;
  */
 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);
 
@@ -117,8 +115,7 @@ public class MetadataServiceRegistry implements ServiceRegistry {
                 new EntityRoleCriterion(SPSSODescriptor.DEFAULT_ELEMENT_NAME),
                 new EndpointCriterion<>(acs),
                 new ProtocolCriterion(AbstractProtocolConfiguration.PROTOCOL_URI),
-                new StartsWithLocationCriterion()
-        );
+                new StartsWithLocationCriterion());
     }
 
     /**
@@ -133,60 +130,37 @@ public class MetadataServiceRegistry implements ServiceRegistry {
     @Nonnull
     protected Service create(@Nonnull final String serviceURL, @Nonnull final EntityDescriptor entity) {
         final XMLObject parent = entity.getParent();
-        return new Service(
+        final SPSSODescriptor descriptor = entity.getSPSSODescriptor(AbstractProtocolConfiguration.PROTOCOL_URI);
+        if (descriptor == null) {
+            throw new IllegalStateException("SPSSODescriptor element not found for entity " + entity.getEntityID());
+        }
+        final Service service = 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;
+                isAuthorizedToProxy(descriptor),
+                hasSingleLogoutService(descriptor));
+        service.setEntityDescriptor(entity);
+        return service;
     }
 
-    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);
+    private boolean isAuthorizedToProxy(@Nonnull final SPSSODescriptor descriptor) {
+        final Set<X509Certificate> certs = new HashSet<>();
+        for (KeyDescriptor kd : descriptor.getKeyDescriptors()) {
+            if (kd.getKeyInfo() != null) {
+                try {
+                    certs.addAll(KeyInfoSupport.getCertificates(kd.getKeyInfo()));
+                } catch (CertificateException e) {
+                    throw new RuntimeException("Error decoding metadata certificate", e);
+                }
             }
         }
-        return false;
+        return certs.size() > 0;
     }
 
-    /**
-     * 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;
-                    }
-                }
-            }
+    private boolean hasSingleLogoutService(@Nonnull final SPSSODescriptor descriptor) {
+        if (descriptor != null) {
+            return descriptor.getEndpoints(SingleLogoutService.DEFAULT_ELEMENT_NAME).size() > 0;
         }
-        return null;
+        return false;
     }
 }
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
index 7855d7c..02489e2 100644
--- a/idp-cas-impl/src/test/resources/metadata/cas-test-metadata.xml
+++ b/idp-cas-impl/src/test/resources/metadata/cas-test-metadata.xml
@@ -124,15 +124,77 @@
        | 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">
+            <KeyDescriptor use="signing">
+                <ds:KeyInfo>
+                    <ds:X509Data>
+                        <ds:X509Certificate>
+                            MIIDODCCAiCgAwIBAgIJAKpLQTw/WPXCMA0GCSqGSIb3DQEBCwUAMBwxGjAYBgNV
+                            BAMTEWFscGhhLmV4YW1wbGUub3JnMB4XDTE4MDYxODE2NDE0NVoXDTE4MDcxODE2
+                            NDE0NVowHDEaMBgGA1UEAxMRYWxwaGEuZXhhbXBsZS5vcmcwggEiMA0GCSqGSIb3
+                            DQEBAQUAA4IBDwAwggEKAoIBAQDHSzRUcM0WBtAjR3P1vHYkaaATjNKTxbNHn3zS
+                            3mLnEgukOVFrr+cRByKKUQQb8MIPkuvKrz3lnoCoOwlFMRPigtChjo3UJGTYEMY9
+                            2SQQr24U6nE/3d2qFaf2PNIW1SinSjxbE1xeT0bdLcTZHUcE2yEfHKFhcgXIJprv
+                            R1ceBJBvYYnATuPgUxMjq2ks4kXxG0nNlT13QwBfykBv6I1Wkkc06mEvkMzKNtzr
+                            ayBK1PygVBNVMUQAFn7Tv6c28BtVLFE9SIKj+5ZcpuWkujVNJF1dYdNmfAz3PiuE
+                            dPt2yl3t2r/v4CP+U8kBlQs6A83xYrA0MsHnUYOrfL3UTWtZAgMBAAGjfTB7MB0G
+                            A1UdDgQWBBT/5yBm3mXtsYDvz11kTHsPVGeRcDBMBgNVHSMERTBDgBT/5yBm3mXt
+                            sYDvz11kTHsPVGeRcKEgpB4wHDEaMBgGA1UEAxMRYWxwaGEuZXhhbXBsZS5vcmeC
+                            CQCqS0E8P1j1wjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAb/o/M
+                            mt/nSHOfcjnNJS/LpouaewkoWkQn+FaXZOOvHDYhWur+mHVDpjoszUfgrTX2npmL
+                            e8Q94bHd+cQrJpZFiYRX8l0p7dAH5Q6Ya/AnHuzGeyQ9fXiDMSWcsg2INcWi7oL9
+                            h9+V3idcSzgAo1b7+ESSToPj7OG8tgjEp2C9jy0IKEwoApuQtRzxD1XHZFBFwwuH
+                            nIXWxgctJPU1C+1W9b4bkFSyEGz8/HM7D9feDHbn2AKuRgd99aaOY9D59topf2Zg
+                            t5sUTWWl54eaF5qoXKY/jdl84Tnmo8GeUufCrS0T6YQGI1LTpicPbqf7zHihQTao
+                            I1TQuJgghwPvPE9x
+                        </ds:X509Certificate>
+                    </ds:X509Data>
+                    <ds:X509Data>
+                        <ds:X509Certificate>
+                            MIIDRTCCAi2gAwIBAgIJAJWAmqfrwZdvMA0GCSqGSIb3DQEBCwUAMCAxHjAcBgNV
+                            BAMTFWFscGhhLmRldi5leGFtcGxlLm9yZzAeFw0xODA2MTgxNjUwMThaFw0xODA3
+                            MTgxNjUwMThaMCAxHjAcBgNVBAMTFWFscGhhLmRldi5leGFtcGxlLm9yZzCCASIw
+                            DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMdLNFRwzRYG0CNHc/W8diRpoBOM
+                            0pPFs0effNLeYucSC6Q5UWuv5xEHIopRBBvwwg+S68qvPeWegKg7CUUxE+KC0KGO
+                            jdQkZNgQxj3ZJBCvbhTqcT/d3aoVp/Y80hbVKKdKPFsTXF5PRt0txNkdRwTbIR8c
+                            oWFyBcgmmu9HVx4EkG9hicBO4+BTEyOraSziRfEbSc2VPXdDAF/KQG/ojVaSRzTq
+                            YS+QzMo23OtrIErU/KBUE1UxRAAWftO/pzbwG1UsUT1IgqP7llym5aS6NU0kXV1h
+                            02Z8DPc+K4R0+3bKXe3av+/gI/5TyQGVCzoDzfFisDQywedRg6t8vdRNa1kCAwEA
+                            AaOBgTB/MB0GA1UdDgQWBBT/5yBm3mXtsYDvz11kTHsPVGeRcDBQBgNVHSMESTBH
+                            gBT/5yBm3mXtsYDvz11kTHsPVGeRcKEkpCIwIDEeMBwGA1UEAxMVYWxwaGEuZGV2
+                            LmV4YW1wbGUub3JnggkAlYCap+vBl28wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0B
+                            AQsFAAOCAQEAZJvp0luHvSlb1pSNpH1roT3R35FyZc+rLJWzmtVAdjt0eQU4q6da
+                            /lQ/83ntRj82GOxZEbyJwyhXLaav2nTe7N+wQoz6maTYXMX8Q9DZVLihy1SSrCY6
+                            bLi2+byxKORw9GXrVaul8yckElyvx2HxMg8iXcLmuG1pVb1bk8BlnwHNDPZYTNMY
+                            iPgHtdsquziKrb08y/fjNiyeEIFlHloK+b4jggjOUbQ/jTkLkG6mkRQwu1NolvvB
+                            BBr0q/P8Z86TMmdp1deZEqQMVY6uWNgVs5Ci0piyQdKJjOvaGE/XXItD8blH3d4O
+                            SsADjh/HEFpp0Pu5ypQNryzdNL+6sw4XyQ==
+                        </ds:X509Certificate>
+                    </ds:X509Data>
+                    <ds:X509Data>
+                        <ds:X509Certificate>
+                            MIIDSTCCAjGgAwIBAgIJAI01q+m9qC5gMA0GCSqGSIb3DQEBCwUAMCExHzAdBgNV
+                            BAMTFmFscGhhLnRlc3QuZXhhbXBsZS5vcmcwHhcNMTgwNjE4MTY1MDQzWhcNMTgw
+                            NzE4MTY1MDQzWjAhMR8wHQYDVQQDExZhbHBoYS50ZXN0LmV4YW1wbGUub3JnMIIB
+                            IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx0s0VHDNFgbQI0dz9bx2JGmg
+                            E4zSk8WzR5980t5i5xILpDlRa6/nEQciilEEG/DCD5Lryq895Z6AqDsJRTET4oLQ
+                            oY6N1CRk2BDGPdkkEK9uFOpxP93dqhWn9jzSFtUop0o8WxNcXk9G3S3E2R1HBNsh
+                            HxyhYXIFyCaa70dXHgSQb2GJwE7j4FMTI6tpLOJF8RtJzZU9d0MAX8pAb+iNVpJH
+                            NOphL5DMyjbc62sgStT8oFQTVTFEABZ+07+nNvAbVSxRPUiCo/uWXKblpLo1TSRd
+                            XWHTZnwM9z4rhHT7dspd7dq/7+Aj/lPJAZULOgPN8WKwNDLB51GDq3y91E1rWQID
+                            AQABo4GDMIGAMB0GA1UdDgQWBBT/5yBm3mXtsYDvz11kTHsPVGeRcDBRBgNVHSME
+                            SjBIgBT/5yBm3mXtsYDvz11kTHsPVGeRcKElpCMwITEfMB0GA1UEAxMWYWxwaGEu
+                            dGVzdC5leGFtcGxlLm9yZ4IJAI01q+m9qC5gMAwGA1UdEwQFMAMBAf8wDQYJKoZI
+                            hvcNAQELBQADggEBAFL7Xe5jaIE/f6KbQweDTLEGLZ6CpYFwgjCCI6Kgik2H6+XI
+                            daX5FI8IZ9VThfsbCbr55lIKlmmcR32O9xpLuQ792IJY9D2/I6ltW2iKnTKmaZSE
+                            /S4p7hYu9EKkxkg8MFCRvfVonf9oOUGzoPvfzt9teXG2xzjetgCoY3taaH5UyEHK
+                            pNynStKB0kzfoFOn4pdQWKX5UEZa0fLqzWTfrrikW4PitWrTE5zrn5vsxfBVNPnH
+                            LlCxgWwWYeVi5XgpPoKy+So0dri7caGeNXjXW2ND0waHvp/LSmO8cfXbVX+1VqIw
+                            L65ZJv2FIAm9LMIFVnEkD7sk1LsYdglvXBDz4BA=
+                        </ds:X509Certificate>
+                    </ds:X509Data>
+                </ds:KeyInfo>
+            </KeyDescriptor>
             <AssertionConsumerService
                     Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact"
                     Location="https://alpha.example.org/"

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


More information about the commits mailing list