[java-oidc-common] branch main updated: JCOMOIDC-103 - Add metadata-driven naming to attribute transcoders

Henri Mikkonen henri.mikkonen at iki.fi
Thu Mar 14 08:37:58 UTC 2024


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

hjmikkon pushed a commit to branch main
in repository java-oidc-common.

View the commit online:
http://git.shibboleth.net/view/?p=java-oidc-common.git;a=commit;h=6c2f28d0eb28f4725d8759fc3d1994190c5bb7b9

The following commit(s) were added to refs/heads/main by this push:
     new 6c2f28d  JCOMOIDC-103 - Add metadata-driven naming to attribute transcoders
6c2f28d is described below

commit 6c2f28d0eb28f4725d8759fc3d1994190c5bb7b9
Author: Henri Mikkonen <henri.mikkonen at iki.fi>
AuthorDate: Thu Mar 14 10:37:38 2024 +0200

    JCOMOIDC-103 - Add metadata-driven naming to attribute transcoders
    
    https://shibboleth.atlassian.net/browse/JCOMOIDC-103
    
    If the transcoder property flag 'oidc.nameFromSamlMetadata' is set to true,
    then the OIDC claim name can be set via SAML metadata tag
    'http://shibboleth.net/ns/attributes/naming/oidc'. The logic for the value
    is '<attributeId> <claimName>', so for instance 'mail customEmailName'.
    
    The AbstractOIDCAttributeTranscoder contains one variable and two methods
    copied from IdP 5.1's AbstractAttributeTranscoder for keeping compatibility
    with IdP 5.0. TODO-tags were added as a reminder of that.
---
 .../AbstractOIDCAttributeTranscoder.java           | 115 +++++++++++++++++++--
 .../transcoding/OIDCAttributeTranscoder.java       |  13 +++
 2 files changed, 119 insertions(+), 9 deletions(-)

diff --git a/oidc-common-attribute-api/src/main/java/net/shibboleth/oidc/attribute/transcoding/AbstractOIDCAttributeTranscoder.java b/oidc-common-attribute-api/src/main/java/net/shibboleth/oidc/attribute/transcoding/AbstractOIDCAttributeTranscoder.java
index b539319..8f4cf3a 100644
--- a/oidc-common-attribute-api/src/main/java/net/shibboleth/oidc/attribute/transcoding/AbstractOIDCAttributeTranscoder.java
+++ b/oidc-common-attribute-api/src/main/java/net/shibboleth/oidc/attribute/transcoding/AbstractOIDCAttributeTranscoder.java
@@ -40,6 +40,7 @@ import net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry;
 import net.shibboleth.idp.attribute.transcoding.TranscodingRule;
 import net.shibboleth.shared.collection.CollectionSupport;
 import net.shibboleth.shared.primitive.LoggerFactory;
+import net.shibboleth.shared.primitive.StringSupport;
 
 /**
  * Abstract class for OIDC attribute encoders.
@@ -50,12 +51,31 @@ public abstract class AbstractOIDCAttributeTranscoder
     /** Class logger. */
     @Nonnull private final Logger log = LoggerFactory.getLogger(AbstractOIDCAttributeTranscoder.class);
 
+    /** Strategy to lookup naming overrides in metadata. */
+    //TODO: The variable exists in AbstractAttributeTranscoder since 5.1, but we replicate it for 5.0 compatibility
+    @Nullable private Function<ProfileRequestContext,Set<String>> nameFromMetadataLookupStrategy;
+
     /** {@inheritDoc} */
     @Override
     @Nonnull public Class<JSONObject> getEncodedType() {
         return JSONObject.class;
     }
 
+    /**
+     * Sets lookup strategy for obtaining tag-based naming overrides from metadata.
+     * 
+     * @param strategy  lookup strategy
+     * 
+     * @since 3.1.0
+     */
+    //TODO: The method exists in AbstractAttributeTranscoder since 5.1, but we replicate it for 5.0 compatibility
+    public void setNameFromMetadataLookupStrategy(
+            @Nullable final Function<ProfileRequestContext,Set<String>> strategy) {
+        checkSetterPreconditions();
+
+        nameFromMetadataLookupStrategy = strategy;
+    }
+
     /** {@inheritDoc} */
     @Override
     @Nullable public String getEncodedName(@Nonnull final TranscodingRule rule) {
@@ -67,7 +87,7 @@ public abstract class AbstractOIDCAttributeTranscoder
         }
         return null;
     }
-    
+
     /** {@inheritDoc} */
     @Override
     @Nullable public JSONObject doEncode(@Nullable final ProfileRequestContext profileRequestContext,
@@ -108,18 +128,58 @@ public abstract class AbstractOIDCAttributeTranscoder
         if (claimValues == null) {
             throw new AttributeEncodingException("Unable to build OIDC claim with no value(s)");
         }
-    
-        final String name = rule.getOrDefault(PROP_NAME, String.class, attribute != null ? attribute.getId() : null);
-        if (Strings.isNullOrEmpty(name)) {
-            throw new AttributeEncodingException("Required transcoder property '" + PROP_NAME + "' not found");
-        }
-        
+
+        final String name = getEncodedName(profileRequestContext, attribute, rule);
         final JSONObject claim = new JSONObject();
         claim.put(name, claimValues);
         
         return claim;
     }
 
+    /**
+     * Get the encoded name for the attribute via SAML metadata (if enabled) or rule's name property.
+     * 
+     * @param profileRequestContext current profile request context
+     * @param attribute the attribute being encoded
+     * @param rule properties to control encoding
+     * 
+     * @return the name of the OIDC claim
+     * 
+     * @throws AttributeEncodingException if there's a problem constructing the name
+     */
+    protected String getEncodedName(@Nullable final ProfileRequestContext profileRequestContext,
+            @Nullable final IdPAttribute attribute, @Nonnull final TranscodingRule rule)
+                    throws AttributeEncodingException {
+        final Boolean useMetadata = rule.getOrDefault(PROP_NAME_FROM_METADATA, Boolean.class, false);
+        if (useMetadata != null && useMetadata) {
+            final String id = attribute != null ? attribute.getId() :
+                rule.get(AttributeTranscoderRegistry.PROP_ID, String.class);
+            if (id == null) {
+                log.warn("Rule specified {} but no attribute ID available", PROP_NAME_FROM_METADATA);
+            } else {
+                final String tagValue = getNameFromMetadata(profileRequestContext, id);
+                if (tagValue != null) {
+                    final int lastSpace = tagValue.lastIndexOf(' ');
+                    final String name;
+                    if (lastSpace < 0) {
+                        name = StringSupport.trimOrNull(tagValue);
+                    } else {
+                        name = StringSupport.trimOrNull(tagValue.substring(0, lastSpace));
+                    }
+                    if (name != null) {
+                        return name;
+                    }
+                    log.warn("Metadata tag {}, value {}, was not in the expected form", METADATA_TAG_NAME, tagValue);
+                }
+            }
+        }
+        final String name = rule.getOrDefault(PROP_NAME, String.class, attribute != null ? attribute.getId() : null);
+        if (Strings.isNullOrEmpty(name)) {
+            throw new AttributeEncodingException("Required transcoder property '" + PROP_NAME + "' not found");
+        }
+        return name;
+    }
+
     /**
      * Performs encoding of {@link IdPAttribute}'s values based on rule into a claim value.
      * 
@@ -216,8 +276,45 @@ public abstract class AbstractOIDCAttributeTranscoder
             @Nullable final ProfileRequestContext profileRequestContext, @Nonnull final Object input,
             @Nonnull final TranscodingRule rule) throws AttributeDecodingException;
     
-    
-   /**
+
+    /**
+     * If enabled, search metadata for a tag value prefixed by the input attribute ID followed by
+     * a space character.
+     * 
+     * <p>The remaining portion of the value will be interpreted in a protocol-specific way.</p>
+     * 
+     * @param profileRequestContext profile request context
+     * @param attributeId attribute ID
+     * 
+     * @return the first matching tag value from the installed lookup function
+     * 
+     * @since 3.1.0
+     */
+    //TODO: The method exists in AbstractAttributeTranscoder since 5.1, but we replicate it for 5.0 compatibility
+    @Nullable protected String getNameFromMetadata(@Nullable final ProfileRequestContext profileRequestContext,
+            @Nonnull final String attributeId) {
+
+        if (nameFromMetadataLookupStrategy != null) {
+            final Set<String> tagValues = nameFromMetadataLookupStrategy.apply(profileRequestContext);
+            if (tagValues != null) {
+                for (final String tagValue : tagValues) {
+                    if (tagValue != null && tagValue.startsWith(attributeId + ' ')) {
+                        final String ret = tagValue.substring(attributeId.length() + 1);
+                        if (!ret.isEmpty()) {
+                            return ret;
+                        }
+                    }
+                }
+                log.debug("No applicable tag value found for metadata-driven naming for {}", attributeId);
+            } else {
+                log.debug("No tag values found for metadata-driven naming for {}", attributeId);
+            }
+        }
+
+        return null;
+    }
+
+    /**
      * A function to produce a "canonical" name for an OIDC claim for transcoding rules.
      */
     public static class NamingFunction implements Function<JSONObject,String> {
diff --git a/oidc-common-attribute-api/src/main/java/net/shibboleth/oidc/attribute/transcoding/OIDCAttributeTranscoder.java b/oidc-common-attribute-api/src/main/java/net/shibboleth/oidc/attribute/transcoding/OIDCAttributeTranscoder.java
index 0712c2b..00b816b 100644
--- a/oidc-common-attribute-api/src/main/java/net/shibboleth/oidc/attribute/transcoding/OIDCAttributeTranscoder.java
+++ b/oidc-common-attribute-api/src/main/java/net/shibboleth/oidc/attribute/transcoding/OIDCAttributeTranscoder.java
@@ -49,4 +49,17 @@ public interface OIDCAttributeTranscoder extends AttributeTranscoder<JSONObject>
     /** Whether to decode a JSON Array into a serialized JSON string. */
     @Nonnull @NotEmpty static final String PROP_ARRAY_AS_STRING = "oidc.arrayAsString";
 
+    /**
+     * Flag to signal use of metadata to override name to encode.
+     * 
+     * @since 3.1.0
+     */
+    @Nonnull @NotEmpty static final String PROP_NAME_FROM_METADATA = "oidc.nameFromSamlMetadata";
+
+    /**
+     * Name of metadata tag/attribute to check for in the event that {@link #PROP_NAME_FROM_METADATA} is used.
+     * 
+     * @since 3.1.0
+     */
+    @Nonnull @NotEmpty static final String METADATA_TAG_NAME = "http://shibboleth.net/ns/attributes/naming/oidc";
 }
\ 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