[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