[java-shib-attribute] branch main updated: JSATTR-45 - Port over NameID transcoders from SP in-development work

Scott Cantor cantor.2 at osu.edu
Thu Sep 25 18:09:34 UTC 2025


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

scantor pushed a commit to branch main
in repository java-shib-attribute.

View the commit online:
http://git.shibboleth.net/view/?p=java-shib-attribute.git;a=commit;h=61333373c120a5926bc6e8b84c805852dcc91a7b

The following commit(s) were added to refs/heads/main by this push:
     new 61333373c JSATTR-45 - Port over NameID transcoders from SP in-development work
61333373c is described below

commit 61333373c120a5926bc6e8b84c805852dcc91a7b
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Thu Sep 25 14:08:59 2025 -0400

    JSATTR-45 - Port over NameID transcoders from SP in-development work
    
    https://shibboleth.atlassian.net/browse/JSATTR-45
    
    Redo scoped NameID decoder to "make sense".
---
 .../impl/SAML2ScopedStringNameIDTranscoder.java    | 37 +++++++++++++++++++---
 .../SAML2ScopedStringNameIDTranscoderTest.java     | 29 ++++++++++++++---
 2 files changed, 57 insertions(+), 9 deletions(-)

diff --git a/shib-saml-attribute-impl/src/main/java/net/shibboleth/idp/saml/saml2/nameid/transcoding/impl/SAML2ScopedStringNameIDTranscoder.java b/shib-saml-attribute-impl/src/main/java/net/shibboleth/idp/saml/saml2/nameid/transcoding/impl/SAML2ScopedStringNameIDTranscoder.java
index 0a38c46df..4a575cd65 100644
--- a/shib-saml-attribute-impl/src/main/java/net/shibboleth/idp/saml/saml2/nameid/transcoding/impl/SAML2ScopedStringNameIDTranscoder.java
+++ b/shib-saml-attribute-impl/src/main/java/net/shibboleth/idp/saml/saml2/nameid/transcoding/impl/SAML2ScopedStringNameIDTranscoder.java
@@ -21,6 +21,7 @@ import org.opensaml.core.xml.XMLObject;
 import org.opensaml.profile.context.ProfileRequestContext;
 import org.opensaml.saml.saml2.core.NameID;
 import org.opensaml.saml.saml2.core.NameIDType;
+import org.slf4j.Logger;
 
 import net.shibboleth.idp.attribute.AttributeEncodingException;
 import net.shibboleth.idp.attribute.IdPAttribute;
@@ -30,20 +31,30 @@ import net.shibboleth.idp.attribute.transcoding.AttributeTranscoder;
 import net.shibboleth.idp.attribute.transcoding.TranscodingRule;
 import net.shibboleth.idp.saml.saml2.nameid.transcoding.AbstractSAML2NameIDTranscoder;
 import net.shibboleth.shared.annotation.constraint.NotEmpty;
+import net.shibboleth.shared.primitive.LoggerFactory;
 
 /**
  * {@link AttributeTranscoder} that supports {@link NameID} and {@link ScopedStringAttributeValue} objects.
  * 
- * <p>The scope is defined to be the NameQualifier or the issuing entityID and must exist.</p>
+ * <p>A delimter is used to extract the scope from the element value to split it.</p>
  */
 public class SAML2ScopedStringNameIDTranscoder extends AbstractSAML2NameIDTranscoder<ScopedStringAttributeValue> {
 
     /** The decoding template. */
     @Nonnull @NotEmpty public static final String PROP_TEMPLATE = "saml2.valueTemplate";
 
+    /** Scope delimiter when scopeType property is "inline". */
+    @Nonnull @NotEmpty public static final String PROP_SCOPE_DELIMITER = "saml2.scopeDelimiter";
+    
     /** The default template. */
     @Nonnull @NotEmpty public static final String DEFAULT_TEMPLATE = "$Name";
 
+    /** The default delimiter. */
+    @Nonnull @NotEmpty public static final String DEFAULT_DELIMITER = "@";
+
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(SAML2ScopedStringNameIDTranscoder.class);
+    
     /** {@inheritDoc} */
     @Override
     protected boolean canEncodeValue(@Nonnull final IdPAttribute attribute, @Nonnull final IdPAttributeValue value) {
@@ -64,6 +75,24 @@ public class SAML2ScopedStringNameIDTranscoder extends AbstractSAML2NameIDTransc
     @Nullable protected IdPAttributeValue decodeValue(@Nullable final ProfileRequestContext profileRequestContext,
             @Nonnull final NameID nameID, @Nonnull final TranscodingRule rule, @Nullable final XMLObject value) {
 
+        String scope = null;
+        String stringValue = nameID.getValue();
+        
+        if (stringValue != null) {
+            final String scopeDelimiter = rule.getOrDefault(PROP_SCOPE_DELIMITER, String.class, "@");
+            assert scopeDelimiter != null;
+            final int offset = stringValue.indexOf(scopeDelimiter);
+            if (offset < 0) {
+                log.warn("Ignoring value with no scope delimiter ({})", scopeDelimiter);
+                return null;
+            }
+            scope = stringValue.substring(offset + scopeDelimiter.length());
+            stringValue = stringValue.substring(0, offset);
+        } else {
+            log.warn("Ignoring empty value since scope is subsequently null");
+            return null;
+        }
+        
         final StringBuilder builder =
                 new StringBuilder(rule.getOrDefault(PROP_TEMPLATE, String.class, DEFAULT_TEMPLATE));
         
@@ -89,12 +118,10 @@ public class SAML2ScopedStringNameIDTranscoder extends AbstractSAML2NameIDTransc
 
         i = builder.indexOf("$Name");
         if (i >= 0) {
-            final String val = nameID.getValue();
-            builder.replace(i, i + 5, val != null ? val : "");
+            builder.replace(i, i + 5, stringValue != null ? stringValue : "");
         }
         
-        return new ScopedStringAttributeValue(builder.toString(),
-                getNameQualifier(profileRequestContext, nameID, true));
+        return new ScopedStringAttributeValue(builder.toString(), scope);
     }
     
 }
\ No newline at end of file
diff --git a/shib-saml-attribute-impl/src/test/java/net/shibboleth/idp/saml/saml2/nameid/transcoding/impl/SAML2ScopedStringNameIDTranscoderTest.java b/shib-saml-attribute-impl/src/test/java/net/shibboleth/idp/saml/saml2/nameid/transcoding/impl/SAML2ScopedStringNameIDTranscoderTest.java
index 5bc90b537..7f4d95589 100644
--- a/shib-saml-attribute-impl/src/test/java/net/shibboleth/idp/saml/saml2/nameid/transcoding/impl/SAML2ScopedStringNameIDTranscoderTest.java
+++ b/shib-saml-attribute-impl/src/test/java/net/shibboleth/idp/saml/saml2/nameid/transcoding/impl/SAML2ScopedStringNameIDTranscoderTest.java
@@ -29,6 +29,7 @@ import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
+import net.shibboleth.idp.attribute.AttributeDecodingException;
 import net.shibboleth.idp.attribute.AttributeEncodingException;
 import net.shibboleth.idp.attribute.IdPAttribute;
 import net.shibboleth.idp.attribute.ScopedStringAttributeValue;
@@ -56,6 +57,7 @@ public class SAML2ScopedStringNameIDTranscoderTest extends OpenSAMLInitBaseTestC
     @Nonnull @NotEmpty private final static String ID = "NameIDAttribute";
     @Nonnull @NotEmpty private final static String NAMEID_FORMAT = "Format";
     @Nonnull @NotEmpty private final static String STRING_1 = "Value The First";
+    @Nonnull @NotEmpty private final static String SCOPED_STRING_1 = "ValueThe at First";
     @Nonnull @NotEmpty private final static String STRING_2 = "Second string the value is";
 
     @BeforeClass public void setUp() throws ComponentInitializationException {
@@ -78,7 +80,7 @@ public class SAML2ScopedStringNameIDTranscoderTest extends OpenSAMLInitBaseTestC
         ruleset1.put(AttributeTranscoderRegistry.PROP_ID, ID);
         ruleset1.put(AttributeTranscoderRegistry.PROP_TRANSCODER, transcoder);
         ruleset1.put(SAML2AttributeTranscoder.PROP_NAME_FORMAT, NAMEID_FORMAT);
-        ruleset1.put(SAML2StringNameIDTranscoder.PROP_TEMPLATE, "$Name");
+        ruleset1.put(SAML2ScopedStringNameIDTranscoder.PROP_TEMPLATE, "$Name!!$NameQualifier");
         
         registry.setTranscoderRegistry(CollectionSupport.singletonList(new TranscodingRule(ruleset1)));
         registry.setApplicationContext(new MockApplicationContext());
@@ -120,13 +122,32 @@ public class SAML2ScopedStringNameIDTranscoderTest extends OpenSAMLInitBaseTestC
         Assert.assertEquals(attr.getId(), ID);
         Assert.assertTrue(attr.getValues().isEmpty());
     }
+
+    @Test(expectedExceptions=AttributeDecodingException.class)
+    public void noScope() throws Exception {
+        
+        final NameID nameID = nameIDBuilder.buildObject();
+        Collection<TranscodingRule> rulesets = registry.getTranscodingRules(nameID);
+        Assert.assertEquals(rulesets.size(), 0);
+        
+        nameID.setFormat(NAMEID_FORMAT);
+        nameID.setValue(STRING_1);
+        nameID.setNameQualifier(STRING_2);
+
+        rulesets = registry.getTranscodingRules(nameID);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        assert ruleset != null;
+        
+        TranscoderSupport.<NameID>getTranscoder(ruleset).decode(null, nameID, ruleset);
+    }
     
     @Test public void decode() throws Exception {
                 
         final NameID nameID = nameIDBuilder.buildObject();
         nameID.setFormat(NAMEID_FORMAT);
         nameID.setNameQualifier(STRING_2);
-        nameID.setValue(STRING_1);
+        nameID.setValue(SCOPED_STRING_1);
 
         final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(nameID);
         Assert.assertEquals(rulesets.size(), 1);
@@ -140,8 +161,8 @@ public class SAML2ScopedStringNameIDTranscoderTest extends OpenSAMLInitBaseTestC
         Assert.assertEquals(attr.getValues().size(), 1);
         
         final ScopedStringAttributeValue value = (ScopedStringAttributeValue) attr.getValues().get(0);
-        Assert.assertEquals(value.getValue(), STRING_1);
-        Assert.assertEquals(value.getScope(), STRING_2);
+        Assert.assertEquals(value.getValue(), "ValueThe!!" + STRING_2);
+        Assert.assertEquals(value.getScope(), "First");
     }
 
 }
\ No newline at end of file

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


More information about the commits mailing list