[java-identity-provider] branch main updated: IDP-2076 - Implement new SAML profile settings

Scott Cantor cantor.2 at osu.edu
Thu Feb 23 14:24:10 UTC 2023


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

scantor pushed a commit to branch main
in repository java-identity-provider.

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

The following commit(s) were added to refs/heads/main by this push:
     new 34c6649b1 IDP-2076 - Implement new SAML profile settings
34c6649b1 is described below

commit 34c6649b13bc93a7ae3ebc4048e5b11bc050968b
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Thu Feb 23 09:24:03 2023 -0500

    IDP-2076 - Implement new SAML profile settings
    
    https://shibboleth.atlassian.net/browse/IDP-2076
    
    Add SPNameQualifier, ACS Index, and RequestedAttrbutes support.
---
 .../saml/saml2/profile/impl/AddAuthnRequest.java   | 63 +++++++++++++++-
 .../saml2/profile/impl/AddAuthnRequestTest.java    | 85 +++++++++++++++++++---
 2 files changed, 135 insertions(+), 13 deletions(-)

diff --git a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml2/profile/impl/AddAuthnRequest.java b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml2/profile/impl/AddAuthnRequest.java
index 5da1dbf48..ebbab37f5 100644
--- a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml2/profile/impl/AddAuthnRequest.java
+++ b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml2/profile/impl/AddAuthnRequest.java
@@ -19,6 +19,7 @@ package net.shibboleth.idp.saml.saml2.profile.impl;
 
 import java.security.Principal;
 import java.time.Instant;
+import java.util.Collection;
 import java.util.List;
 import java.util.Set;
 import java.util.function.Function;
@@ -42,6 +43,9 @@ import net.shibboleth.shared.security.IdentifierGenerationStrategy;
 
 import org.opensaml.core.xml.XMLObjectBuilderFactory;
 import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
+import org.opensaml.core.xml.io.MarshallingException;
+import org.opensaml.core.xml.io.UnmarshallingException;
+import org.opensaml.core.xml.util.XMLObjectSupport;
 import org.opensaml.messaging.context.MessageContext;
 import org.opensaml.messaging.context.navigate.ChildContextLookup;
 import org.opensaml.messaging.context.navigate.ParentContextLookup;
@@ -53,8 +57,10 @@ import org.opensaml.profile.context.ProxiedRequesterContext;
 import org.opensaml.profile.context.navigate.InboundMessageContextLookup;
 import org.opensaml.saml.common.SAMLObjectBuilder;
 import org.opensaml.saml.common.SAMLVersion;
+import org.opensaml.saml.ext.reqattr.RequestedAttributes;
 import org.opensaml.saml.saml2.core.AuthnContextComparisonTypeEnumeration;
 import org.opensaml.saml.saml2.core.AuthnRequest;
+import org.opensaml.saml.saml2.core.Extensions;
 import org.opensaml.saml.saml2.core.IDPEntry;
 import org.opensaml.saml.saml2.core.IDPList;
 import org.opensaml.saml.saml2.core.Issuer;
@@ -62,6 +68,7 @@ import org.opensaml.saml.saml2.core.NameIDPolicy;
 import org.opensaml.saml.saml2.core.RequestedAuthnContext;
 import org.opensaml.saml.saml2.core.RequesterID;
 import org.opensaml.saml.saml2.core.Scoping;
+import org.opensaml.saml.saml2.metadata.RequestedAttribute;
 import org.slf4j.Logger;
 import net.shibboleth.shared.primitive.LoggerFactory;
 
@@ -259,6 +266,13 @@ public class AddAuthnRequest extends AbstractAuthenticationAction {
         object.setID(idGenerator.generateIdentifier());
         object.setIssueInstant(Instant.now());
         object.setVersion(SAMLVersion.VERSION_20);
+        
+        final Integer index = profileConfiguration.getAttributeIndex(profileRequestContext);
+        if (index != null) {
+            log.debug("{} Setting AttributeConsumingServiceIndex to '{}' for SAML AuthnRequest", getLogPrefix(),
+                    index);
+            object.setAttributeConsumingServiceIndex(index);
+        }
 
         if (issuerId != null) {
             log.debug("{} Setting Issuer to {}", getLogPrefix(), issuerId);
@@ -286,6 +300,12 @@ public class AddAuthnRequest extends AbstractAuthenticationAction {
         
         final NameIDPolicy nip = nipBuilder.buildObject();
         nip.setAllowCreate(true);
+        final String qualifier = profileConfiguration.getSPNameQualifier(profileRequestContext);
+        if (qualifier != null) {
+            log.debug("{} Setting NameIDPolicy SPNameQualifier to '{}' for SAML AuthnRequest", getLogPrefix(),
+                    qualifier);
+            nip.setSPNameQualifier(qualifier);
+        }
         
         // TODO: use metadata for NameID Formats too?
         final List<String> formats = profileConfiguration.getNameIDFormatPrecedence(profileRequestContext);
@@ -293,7 +313,7 @@ public class AddAuthnRequest extends AbstractAuthenticationAction {
             log.debug("{} Setting NameIDPolicy Format to '{}' for SAML AuthnRequest", getLogPrefix(), formats.get(0));
             nip.setFormat(formats.get(0));
         }
-        
+
         object.setNameIDPolicy(nip);
 
         final RequestedAuthnContext rac = getRequestedAuthnContext(profileRequestContext);
@@ -309,6 +329,8 @@ public class AddAuthnRequest extends AbstractAuthenticationAction {
         
         object.setScoping(buildScoping(profileRequestContext, authenticationContext.getProxyCount(),
                 authenticationContext.getProxiableAuthorities()));
+
+        object.setExtensions(buildExtensions(profileRequestContext));
         
         profileRequestContext.getOutboundMessageContext().setMessage(object);
     }
@@ -393,7 +415,7 @@ public class AddAuthnRequest extends AbstractAuthenticationAction {
      * 
      * @return populated {@link Scoping}
      */
-    @Nullable public Scoping buildScoping(@Nonnull final ProfileRequestContext profileRequestContext,
+    @Nullable private Scoping buildScoping(@Nonnull final ProfileRequestContext profileRequestContext,
             @Nullable final Integer count, @Nonnull @NonnullElements final Set<String> idplist) {
 
         boolean include = false;
@@ -454,5 +476,40 @@ public class AddAuthnRequest extends AbstractAuthenticationAction {
         
         return include ? scoping : null;
     }
+ 
+    /**
+     * Build {@link RequestedAttributes} extension if required.
+     * 
+     * @param profileRequestContext current profile request context
+     * 
+     * @return extension or null
+     */
+    @Nullable private Extensions buildExtensions(
+            @Nonnull final ProfileRequestContext profileRequestContext) {
+        
+        final Collection<RequestedAttribute> attrs = profileConfiguration.getRequestedAttributes(profileRequestContext);
+        if (!attrs.isEmpty()) {
+            final XMLObjectBuilderFactory bf = XMLObjectProviderRegistrySupport.getBuilderFactory();
+            final SAMLObjectBuilder<Extensions> extBuilder =
+                    (SAMLObjectBuilder<Extensions>) bf.<Extensions>getBuilderOrThrow(
+                            Extensions.DEFAULT_ELEMENT_NAME);
+            final SAMLObjectBuilder<RequestedAttributes> reqExtBuilder =
+                    (SAMLObjectBuilder<RequestedAttributes>) bf.<RequestedAttributes>getBuilderOrThrow(
+                            RequestedAttributes.DEFAULT_ELEMENT_NAME);
+            final RequestedAttributes reqExt = reqExtBuilder.buildObject();
+            attrs.forEach(attr -> {
+                try {
+                    reqExt.getRequestedAttributes().add(XMLObjectSupport.cloneXMLObject(attr));
+                } catch (final MarshallingException|UnmarshallingException e) {
+                    log.error("{} Error cloning RequestedAttribute from profile configuration", getLogPrefix(), e);
+                }
+            });
+            final Extensions ext = extBuilder.buildObject();
+            ext.getUnknownXMLObjects().add(reqExt);
+            return ext;
+        }
+        
+        return null;
+    }
     
-}
+}
\ No newline at end of file
diff --git a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/saml2/profile/impl/AddAuthnRequestTest.java b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/saml2/profile/impl/AddAuthnRequestTest.java
index 88e997b64..25ea792a4 100644
--- a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/saml2/profile/impl/AddAuthnRequestTest.java
+++ b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/saml2/profile/impl/AddAuthnRequestTest.java
@@ -39,13 +39,19 @@ import net.shibboleth.idp.saml.authn.principal.AuthenticationMethodPrincipal;
 import net.shibboleth.idp.saml.authn.principal.AuthnContextClassRefPrincipal;
 import net.shibboleth.idp.saml.saml2.profile.config.impl.BrowserSSOProfileConfiguration;
 import net.shibboleth.profile.context.RelyingPartyContext;
+import net.shibboleth.shared.collection.CollectionSupport;
 import net.shibboleth.shared.component.ComponentInitializationException;
 
 import org.opensaml.core.testing.OpenSAMLInitBaseTestCase;
+import org.opensaml.core.xml.XMLObjectBuilderFactory;
+import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
 import org.opensaml.messaging.context.MessageContext;
 import org.opensaml.messaging.context.navigate.ChildContextLookup;
 import org.opensaml.messaging.context.navigate.ParentContextLookup;
+import org.opensaml.saml.common.SAMLObjectBuilder;
+import org.opensaml.saml.ext.reqattr.RequestedAttributes;
 import org.opensaml.saml.saml1.core.AuthenticationStatement;
+import org.opensaml.saml.saml2.core.Attribute;
 import org.opensaml.saml.saml2.core.AuthnContext;
 import org.opensaml.saml.saml2.core.AuthnContextComparisonTypeEnumeration;
 import org.opensaml.saml.saml2.core.AuthnRequest;
@@ -54,6 +60,7 @@ import org.opensaml.saml.saml2.core.NameIDPolicy;
 import org.opensaml.saml.saml2.core.NameIDType;
 import org.opensaml.saml.saml2.core.RequestedAuthnContext;
 import org.opensaml.saml.saml2.core.Scoping;
+import org.opensaml.saml.saml2.metadata.RequestedAttribute;
 import org.opensaml.xmlsec.config.BasicXMLSecurityConfiguration;
 import org.springframework.webflow.execution.Event;
 import org.springframework.webflow.execution.RequestContext;
@@ -138,10 +145,13 @@ public class AddAuthnRequestTest extends OpenSAMLInitBaseTestCase {
         assertEquals(request.getIssuer().getValue(), ActionTestingSupport.OUTBOUND_MSG_ISSUER);
         assertFalse(request.isForceAuthn());
         assertFalse(request.isPassive());
+        assertNull(request.getAttributeConsumingServiceIndex());
+        assertNull(request.getExtensions());
         
         final NameIDPolicy nid = request.getNameIDPolicy();
         assertNotNull(nid);
         assertNull(nid.getFormat());
+        assertNull(nid.getSPNameQualifier());
         assertTrue(nid.getAllowCreate());
         
         assertNull(request.getRequestedAuthnContext());
@@ -177,9 +187,9 @@ public class AddAuthnRequestTest extends OpenSAMLInitBaseTestCase {
     /** Test that the action works with a NameID format set. */
     @Test public void testNameIDFormat() {
         ((BrowserSSOProfileConfiguration) rpc.getProfileConfig()).setNameIDFormatPrecedence(
-                Arrays.asList(NameIDType.EMAIL, NameIDType.KERBEROS));
+                CollectionSupport.listOf(NameIDType.EMAIL, NameIDType.KERBEROS));
         
-        Event event = action.execute(rc);
+        final Event event = action.execute(rc);
         ActionTestingSupport.assertProceedEvent(event);
         
         assertNotNull(prc2.getOutboundMessageContext().getMessage());
@@ -192,12 +202,71 @@ public class AddAuthnRequestTest extends OpenSAMLInitBaseTestCase {
         assertTrue(nid.getAllowCreate());
     }
 
+    /** Test that the action works with SPNameQualifier set. */
+    @Test public void testSPNameQualifier() {
+        ((BrowserSSOProfileConfiguration) rpc.getProfileConfig()).setSPNameQualifier(ActionTestingSupport.INBOUND_MSG_ISSUER);
+        
+        final Event event = action.execute(rc);
+        ActionTestingSupport.assertProceedEvent(event);
+        
+        assertNotNull(prc2.getOutboundMessageContext().getMessage());
+        assertTrue(prc2.getOutboundMessageContext().getMessage() instanceof AuthnRequest);
+
+        final AuthnRequest request = (AuthnRequest) prc2.getOutboundMessageContext().getMessage();
+        final NameIDPolicy nid = request.getNameIDPolicy();
+        assertNotNull(nid);
+        assertEquals(nid.getSPNameQualifier(), ActionTestingSupport.INBOUND_MSG_ISSUER);
+    }
+
+    /** Test that the action works with AttributeConsumingrServiceIndex set. */
+    @Test public void testAttributeIndex() {
+        ((BrowserSSOProfileConfiguration) rpc.getProfileConfig()).setAttributeIndex(42);
+        
+        final Event event = action.execute(rc);
+        ActionTestingSupport.assertProceedEvent(event);
+        
+        assertNotNull(prc2.getOutboundMessageContext().getMessage());
+        assertTrue(prc2.getOutboundMessageContext().getMessage() instanceof AuthnRequest);
+
+        final AuthnRequest request = (AuthnRequest) prc2.getOutboundMessageContext().getMessage();
+        assertEquals(request.getAttributeConsumingServiceIndex(), 42);
+    }
+
+    /** Test that the action works with RequestedAttributes set. */
+    @Test public void testRequestedAttributes() {
+        final XMLObjectBuilderFactory bf = XMLObjectProviderRegistrySupport.getBuilderFactory();
+        final SAMLObjectBuilder<RequestedAttribute> reqAttrBuilder =
+                (SAMLObjectBuilder<RequestedAttribute>) bf.<RequestedAttribute>getBuilderOrThrow(
+                        RequestedAttribute.DEFAULT_ELEMENT_NAME);
+        final RequestedAttribute attr1 = reqAttrBuilder.buildObject();
+        attr1.setNameFormat(Attribute.URI_REFERENCE);
+        attr1.setName("https://attr1.example.org");
+        final RequestedAttribute attr2 = reqAttrBuilder.buildObject();
+        attr2.setNameFormat(Attribute.URI_REFERENCE);
+        attr2.setName("https://attr2.example.org");
+
+        ((BrowserSSOProfileConfiguration) rpc.getProfileConfig()).setRequestedAttributes(CollectionSupport.listOf(attr1, attr2));
+        
+        final Event event = action.execute(rc);
+        ActionTestingSupport.assertProceedEvent(event);
+        
+        assertNotNull(prc2.getOutboundMessageContext().getMessage());
+        assertTrue(prc2.getOutboundMessageContext().getMessage() instanceof AuthnRequest);
+
+        final AuthnRequest request = (AuthnRequest) prc2.getOutboundMessageContext().getMessage();
+        assertNotNull(request.getExtensions());
+        assertEquals(request.getExtensions().getUnknownXMLObjects(RequestedAttributes.DEFAULT_ELEMENT_NAME).size(), 1);
+        final RequestedAttributes extension =
+                (RequestedAttributes) request.getExtensions().getUnknownXMLObjects(RequestedAttributes.DEFAULT_ELEMENT_NAME).get(0);
+        assertEquals(extension.getRequestedAttributes().size(), 2);
+    }
+
     /** Test with Scoping element but no count. */
     @Test public void testScopingNoCount() {
         ac.getProxiableAuthorities().add("foo");
         ac.getProxiableAuthorities().add("bar");
         
-        Event event = action.execute(rc);
+        final Event event = action.execute(rc);
         ActionTestingSupport.assertProceedEvent(event);
         
         assertNotNull(prc2.getOutboundMessageContext().getMessage());
@@ -221,10 +290,9 @@ public class AddAuthnRequestTest extends OpenSAMLInitBaseTestCase {
 
     /** Test with Scoping element and count of 1. */
     @Test public void testScopingCount1() {
-
         ac.setProxyCount(1);
         
-        Event event = action.execute(rc);
+        final Event event = action.execute(rc);
         ActionTestingSupport.assertProceedEvent(event);
         
         assertNotNull(prc2.getOutboundMessageContext().getMessage());
@@ -240,10 +308,9 @@ public class AddAuthnRequestTest extends OpenSAMLInitBaseTestCase {
 
     /** Test with Scoping element and count of 5. */
     @Test public void testScopingCount5() {
-
         ac.setProxyCount(5);
         
-        Event event = action.execute(rc);
+        final Event event = action.execute(rc);
         ActionTestingSupport.assertProceedEvent(event);
         
         assertNotNull(prc2.getOutboundMessageContext().getMessage());
@@ -259,10 +326,9 @@ public class AddAuthnRequestTest extends OpenSAMLInitBaseTestCase {
 
     /** Test with Scoping element and count of 0 (this shouldn't really happen). */
     @Test public void testScopingCount0() {
-
         ac.setProxyCount(0);
         
-        Event event = action.execute(rc);
+        final Event event = action.execute(rc);
         ActionTestingSupport.assertProceedEvent(event);
         
         assertNotNull(prc2.getOutboundMessageContext().getMessage());
@@ -278,7 +344,6 @@ public class AddAuthnRequestTest extends OpenSAMLInitBaseTestCase {
 
     /** Test that the action works for RequestedAuthnContext. */
     @Test public void testAuthnContext() {
-        
         final RequestedPrincipalContext reqctx = ac.getSubcontext(RequestedPrincipalContext.class, true);
         reqctx.setOperator("exact");
         reqctx.setRequestedPrincipals(

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


More information about the commits mailing list