[java-oidc-common] branch main updated: JCOMOIDC-40 - Complete OIDCAttributeTranscoder decode values function

Phil Smart philip.smart at jisc.ac.uk
Fri Feb 25 14:40:53 UTC 2022


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

philsmart 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=d4155614eab8baa256c7c860d837d52c833f0692

The following commit(s) were added to refs/heads/main by this push:
     new d415561  JCOMOIDC-40 - Complete OIDCAttributeTranscoder decode values function
d415561 is described below

commit d4155614eab8baa256c7c860d837d52c833f0692
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Fri Feb 25 14:40:41 2022 +0000

    JCOMOIDC-40 - Complete OIDCAttributeTranscoder decode values function
    
     - Completed (subject to review) the decode method in the String OIDC
    attribute transcoder.
     - Changed the logic in the Abstract OIDC encoder to pull out the claim
    values from the claim object without using the getEncodedName method.
    
    https://shibboleth.atlassian.net/browse/JCOMOIDC-40
---
 .../AbstractOIDCAttributeTranscoder.java           |  30 +-
 .../transcoding/OIDCAttributeTranscoder.java       |   9 +
 .../impl/OIDCStringAttributeTranscoder.java        |  66 ++-
 .../impl/OIDCStringAttributeTranscoderTest.java    | 478 ++++++++++++++++++++-
 4 files changed, 564 insertions(+), 19 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 acc7379..c26d4e1 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
@@ -17,6 +17,7 @@
 
 package net.shibboleth.oidc.attribute.transcoding;
 
+import java.util.Collections;
 import java.util.List;
 import java.util.Set;
 import java.util.function.Function;
@@ -29,7 +30,6 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.google.common.base.Strings;
-import com.nimbusds.openid.connect.sdk.OIDCClaimsRequest;
 import com.nimbusds.openid.connect.sdk.claims.ClaimRequirement;
 import com.nimbusds.openid.connect.sdk.claims.ClaimsSetRequest;
 
@@ -55,11 +55,13 @@ public abstract class AbstractOIDCAttributeTranscoder
     @Nonnull private final Logger log = LoggerFactory.getLogger(AbstractOIDCAttributeTranscoder.class);
 
     /** {@inheritDoc} */
+    @Override
     public Class<JSONObject> getEncodedType() {
         return JSONObject.class;
     }
 
     /** {@inheritDoc} */
+    @Override
     @Nullable public String getEncodedName(@Nonnull final TranscodingRule rule) {
 
         final String name = rule.getOrDefault(PROP_NAME, String.class,
@@ -140,15 +142,24 @@ public abstract class AbstractOIDCAttributeTranscoder
             @Nonnull final JSONObject input, @Nonnull final TranscodingRule rule)
                     throws AttributeDecodingException {
 
-        final String attributeName = getEncodedName(rule);
+        final String attributeName = rule.getOrDefault(PROP_NAME, String.class,
+                rule.get(AttributeTranscoderRegistry.PROP_ID, String.class));
         
-        log.trace("Beginning to decode claim {}", attributeName);
-
-        final List<IdPAttributeValue> idpAttributeValues =
-                decodeValues(profileRequestContext, input.get(attributeName), rule);
-
-        log.trace("Decoded {} values for claim {}", idpAttributeValues.size(), attributeName);
-        return buildIdPAttribute(profileRequestContext, input, rule, idpAttributeValues);
+        log.trace("Beginning to decode claim '{}'", attributeName);
+               
+        final Object inputToDecode = input.get(attributeName);
+        if (inputToDecode == null) {
+            log.debug("Skipping null value(s) of attribute '{}'", attributeName);
+        } else {
+            final List<IdPAttributeValue> decodedValues = decodeValues(profileRequestContext,inputToDecode, rule);
+            if (decodedValues.isEmpty()) {
+                log.trace("Unable to decode value(s) of attribute '{}'", attributeName);
+            } else {
+                log.trace("Decoded {} value(s) for attribute '{}'", decodedValues.size(), attributeName);
+                return buildIdPAttribute(profileRequestContext, input, rule, decodedValues);
+            }    
+        }
+        return buildIdPAttribute(profileRequestContext, input, rule, Collections.emptyList());
     }
 
     /**
@@ -212,6 +223,7 @@ public abstract class AbstractOIDCAttributeTranscoder
     public static class NamingFunction implements Function<JSONObject,String> {
 
         /** {@inheritDoc} */
+        @Override
         @Nullable public String apply(@Nullable final JSONObject input) {
             
             if (input == null) {
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 672d487..1d8c06f 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
@@ -42,5 +42,14 @@ public interface OIDCAttributeTranscoder extends AttributeTranscoder<JSONObject>
 
     /** Separator to use when not encoding multiple values to array. */
     @Nonnull @NotEmpty static final String PROP_STRING_DELIMITER = "oidc.stringDelimiter";
+    
+    /** Whether to decode JSON Numbers to IdP Attribute Strings. */
+    @Nonnull @NotEmpty static final String PROP_NUMBER_AS_STRING = "oidc.numberAsString";
+    
+    /** Whether to decode JSON Booleans to IdP Attribute Strings. */
+    @Nonnull @NotEmpty static final String PROP_BOOLEAN_AS_STRING = "oidc.booleanAsString";
+    
+    /** Whether to decode a JSON Array into a serialized JSON string. */
+    @Nonnull @NotEmpty static final String PROP_ARRAY_AS_STRING = "oidc.arrayAsString";
 
 }
\ No newline at end of file
diff --git a/oidc-common-attribute-impl/src/main/java/net/shibboleth/oidc/attribute/transcoding/impl/OIDCStringAttributeTranscoder.java b/oidc-common-attribute-impl/src/main/java/net/shibboleth/oidc/attribute/transcoding/impl/OIDCStringAttributeTranscoder.java
index 47d3fbd..595a249 100644
--- a/oidc-common-attribute-impl/src/main/java/net/shibboleth/oidc/attribute/transcoding/impl/OIDCStringAttributeTranscoder.java
+++ b/oidc-common-attribute-impl/src/main/java/net/shibboleth/oidc/attribute/transcoding/impl/OIDCStringAttributeTranscoder.java
@@ -17,6 +17,7 @@
 
 package net.shibboleth.oidc.attribute.transcoding.impl;
 
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.stream.Collectors;
@@ -33,6 +34,7 @@ import org.slf4j.LoggerFactory;
 import com.google.common.base.Predicates;
 
 import net.minidev.json.JSONArray;
+import net.minidev.json.JSONObject;
 import net.minidev.json.parser.JSONParser;
 import net.minidev.json.parser.ParseException;
 import net.shibboleth.idp.attribute.AttributeDecodingException;
@@ -40,6 +42,7 @@ import net.shibboleth.idp.attribute.AttributeEncodingException;
 import net.shibboleth.idp.attribute.IdPAttribute;
 import net.shibboleth.idp.attribute.IdPAttributeValue;
 import net.shibboleth.idp.attribute.StringAttributeValue;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry;
 import net.shibboleth.idp.attribute.transcoding.TranscodingRule;
 import net.shibboleth.oidc.attribute.transcoding.AbstractOIDCAttributeTranscoder;
 import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
@@ -55,6 +58,24 @@ import net.shibboleth.utilities.java.support.annotation.constraint.Unmodifiable;
  * interpreted as boolean the value is true if string value equals to "true" ignoring the case. If boolean values are
  * not set to array the first string value is considered to be the result. Finally, the result may be placed to json
  * Object.
+ * 
+ * <p>For decoding, attempt to convert the input JSON type into a {@link StringAttributeValue} in the following way for
+ * the following JSON types:</p>
+ * <ul>
+ * <li>If a String, convert directly to a string attribute value.</li>
+ * <li>If an Integer or Double (a number type) and PROP_NUMBER_AS_STRING is true, convert to string, 
+ * else return an empty values list.</li>
+ * <li>If a Boolean and PROP_BOOLEAN_AS_STRING is true, convert boolean to 'true' or 'false' string, 
+ * else return an empty values list.</li>
+ * <li>If a JSON Array and PROP_ARRAY_AS_STRING is true, convert to a string by serializing the array to a
+ * JSON string.</li>
+ * <li>If a JSON Array and PROP_ARRAY_AS_STRING is false, attempt to recursively convert elements of the array
+ * to string attribute values using the same set of rules. If any element fails to convert it will be excluded
+ * from the result.</li>
+ * <li>If a JSON Object and PROP_ASOBJECT is true, convert the object to a string by serializing into JSON. Else,
+ * return an empty values list.</li>
+ * <li>For any other unknown type, return an empty values list.</li>
+ * </ul>
  */
 public class OIDCStringAttributeTranscoder extends AbstractOIDCAttributeTranscoder {
 
@@ -149,10 +170,51 @@ public class OIDCStringAttributeTranscoder extends AbstractOIDCAttributeTranscod
             @Nullable final ProfileRequestContext profileRequestContext, @Nonnull final Object input,
             @Nonnull final TranscodingRule rule) throws AttributeDecodingException {
         
-        // TODO: implement value decoding
-        return Collections.emptyList();
+        // Input could be any JSON data type e.g. String, Number, JSONObject, Array, Boolean.
+             
+        if (input instanceof String) {            
+            return List.of(StringAttributeValue.valueOf((String)input));  
+            
+        } else if (input instanceof Integer && rule.getOrDefault(PROP_NUMBER_AS_STRING, Boolean.class, false)) {              
+            return List.of(StringAttributeValue.valueOf(Integer.toString((Integer)input)));         
+            
+        } else if (input instanceof Double && rule.getOrDefault(PROP_NUMBER_AS_STRING, Boolean.class, false)) {            
+            return List.of(StringAttributeValue.valueOf(Double.toString((Double)input)));            
+            
+        } else if (input instanceof Boolean && rule.getOrDefault(PROP_BOOLEAN_AS_STRING, Boolean.class, false)) {           
+            return List.of(StringAttributeValue.valueOf(Boolean.toString((Boolean)input)));            
+            
+        } else if (input instanceof JSONArray) {
+            if (rule.getOrDefault(PROP_ARRAY_AS_STRING, Boolean.class, false)) {
+                // Serialize JSON Array to JSON string
+                return List.of(StringAttributeValue.valueOf(((JSONArray)input).toJSONString()));
+            } else {
+                // Deal with array recursively, if any nested element can not be converted to a string
+                // the entire attribute will fail to be decoded.
+                final JSONArray array = (JSONArray)input;
+                final List<IdPAttributeValue> arrayValuesDecoded = new ArrayList<>();
+                for (final Object element : array) {
+                    arrayValuesDecoded.addAll(decodeValues(profileRequestContext, element, rule));
+                }
+                return arrayValuesDecoded;  
+            }
+            
+        } else if (input instanceof JSONObject && rule.getOrDefault(PROP_ASOBJECT, Boolean.class, false)) {
+            // Serialize JSONObject to a JSON string.
+            return List.of(StringAttributeValue.valueOf(((JSONObject)input).toJSONString()));
+            
+        } else {
+            log.trace("Could not convert values of type {} for the claim '{}' to a string attribute using "
+                    + "transcoding rule '{} -> {}'",
+                    input.getClass().getName(), input, rule.getOrDefault(PROP_NAME, String.class, "unknown"), 
+                    rule.getOrDefault(AttributeTranscoderRegistry.PROP_ID, String.class, "unknown"));
+            
+            return Collections.emptyList();
+        }
+        
     }
     
+    
     /**
      * Parses string as JSONObject.
      * 
diff --git a/oidc-common-attribute-impl/src/test/java/net/shibboleth/oidc/attribute/transcoding/impl/OIDCStringAttributeTranscoderTest.java b/oidc-common-attribute-impl/src/test/java/net/shibboleth/oidc/attribute/transcoding/impl/OIDCStringAttributeTranscoderTest.java
index 00c78cb..0d77420 100644
--- a/oidc-common-attribute-impl/src/test/java/net/shibboleth/oidc/attribute/transcoding/impl/OIDCStringAttributeTranscoderTest.java
+++ b/oidc-common-attribute-impl/src/test/java/net/shibboleth/oidc/attribute/transcoding/impl/OIDCStringAttributeTranscoderTest.java
@@ -24,20 +24,22 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.nimbusds.openid.connect.sdk.claims.ClaimsSetRequest;
+
 import net.minidev.json.JSONArray;
 import net.minidev.json.JSONObject;
 import net.shibboleth.ext.spring.testing.MockApplicationContext;
+import net.shibboleth.idp.attribute.AttributeDecodingException;
 import net.shibboleth.idp.attribute.AttributeEncodingException;
 import net.shibboleth.idp.attribute.ByteAttributeValue;
 import net.shibboleth.idp.attribute.IdPAttribute;
 import net.shibboleth.idp.attribute.IdPAttributeValue;
-import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
-
-import org.testng.Assert;
-import org.testng.annotations.AfterMethod;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
-
+import net.shibboleth.idp.attribute.IdPRequestedAttribute;
 import net.shibboleth.idp.attribute.StringAttributeValue;
 import net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry;
 import net.shibboleth.idp.attribute.transcoding.BasicNamingFunction;
@@ -46,7 +48,7 @@ import net.shibboleth.idp.attribute.transcoding.TranscodingRule;
 import net.shibboleth.idp.attribute.transcoding.impl.AttributeTranscoderRegistryImpl;
 import net.shibboleth.oidc.attribute.transcoding.AbstractOIDCAttributeTranscoder;
 import net.shibboleth.oidc.attribute.transcoding.OIDCAttributeTranscoder;
-import net.shibboleth.oidc.attribute.transcoding.impl.OIDCStringAttributeTranscoder;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
 
 public class OIDCStringAttributeTranscoderTest {
 
@@ -253,5 +255,465 @@ public class OIDCStringAttributeTranscoderTest {
 
         TranscoderSupport.<JSONObject>getTranscoder(tr).encode(null, inputAttribute, JSONObject.class, tr);
     }
+    
+    @Test
+    public void testDecodingString() throws ComponentInitializationException, AttributeDecodingException {
+        
+        registry.setTranscoderRegistry(Collections.singletonList(new TranscodingRule(ruleset)));
+        registry.setApplicationContext(new MockApplicationContext());        
+        registry.initialize();        
+       
+        // Map from oidc claim.name 'bar' to shib attribute id 'foo'. 
+        final JSONObject inputAttribute = new JSONObject();
+        inputAttribute.put(ATTR_NAME, "The bar value");
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(inputAttribute);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule tr = rulesets.iterator().next();
+
+        final IdPAttribute decodedAttribute = TranscoderSupport.<JSONObject>getTranscoder(tr)
+                .decode(null, inputAttribute, tr);
+
+        Assert.assertNotNull(decodedAttribute);
+        Assert.assertEquals(decodedAttribute.getId(), ATTR_ID);
+        Assert.assertEquals(decodedAttribute.getValues().size(), 1);
+        Assert.assertNotNull(decodedAttribute.getValues().get(0).getNativeValue());
+        Assert.assertEquals(decodedAttribute.getValues().get(0).getNativeValue(), "The bar value");
+    }
+    
+    @Test
+    public void testDecodingNoValues() throws ComponentInitializationException, AttributeDecodingException {
+        
+        registry.setTranscoderRegistry(Collections.singletonList(new TranscodingRule(ruleset)));
+        registry.setApplicationContext(new MockApplicationContext());        
+        registry.initialize();        
+       
+        // Map from oidc claim.name 'bar' to shib attribute id 'foo'. 
+        final JSONObject inputAttribute = new JSONObject();
+        inputAttribute.put(ATTR_NAME, null);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(inputAttribute);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule tr = rulesets.iterator().next();
+
+        final IdPAttribute decodedAttribute = TranscoderSupport.<JSONObject>getTranscoder(tr)
+                .decode(null, inputAttribute, tr);
+
+        Assert.assertNotNull(decodedAttribute);
+        Assert.assertEquals(decodedAttribute.getId(), ATTR_ID);
+        Assert.assertEquals(decodedAttribute.getValues().size(), 0);
+    }
+    
+    /* Specifically test the ClaimsSetRequested type for the OP's SetRequiredClaimsToResponseContext.*/
+    @Test
+    public void testDecodingWrongType_ClaimsRequest() throws ComponentInitializationException, AttributeDecodingException {
+        
+        registry.setTranscoderRegistry(Collections.singletonList(new TranscodingRule(ruleset)));
+        registry.setApplicationContext(new MockApplicationContext());        
+        registry.initialize();        
+       
+        
+        final ClaimsSetRequest.Entry entry = new ClaimsSetRequest.Entry("test");
+        // Map from oidc claim.name 'bar' to shib attribute id 'foo'. 
+        final JSONObject inputAttribute = new JSONObject();
+        inputAttribute.put(ATTR_NAME, entry);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(inputAttribute);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule tr = rulesets.iterator().next();
+
+        final IdPAttribute decodedAttribute = TranscoderSupport.<JSONObject>getTranscoder(tr)
+                .decode(null, inputAttribute, tr);
+        
+        Assert.assertNotNull(decodedAttribute);
+        Assert.assertTrue(decodedAttribute instanceof IdPRequestedAttribute);
+        Assert.assertEquals(decodedAttribute.getId(), ATTR_ID);
+        Assert.assertEquals(decodedAttribute.getValues().size(), 0);
+
+    }
+    
+    @Test
+    public void testDecodingStringArray() throws ComponentInitializationException, AttributeDecodingException {
+        
+        registry.setTranscoderRegistry(Collections.singletonList(new TranscodingRule(ruleset)));
+        registry.setApplicationContext(new MockApplicationContext());        
+        registry.initialize();        
+       
+        // Map from oidc claim.name 'bar' to shib attribute id 'foo'. 
+        final JSONObject inputAttribute = new JSONObject();        
+        final JSONArray array = new JSONArray();
+        array.add("First");
+        array.add("Second");               
+        inputAttribute.put(ATTR_NAME, array);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(inputAttribute);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule tr = rulesets.iterator().next();
+
+        final IdPAttribute decodedAttribute = TranscoderSupport.<JSONObject>getTranscoder(tr)
+                .decode(null, inputAttribute, tr);
+
+        Assert.assertNotNull(decodedAttribute);
+        Assert.assertEquals(decodedAttribute.getId(), ATTR_ID);
+        Assert.assertEquals(decodedAttribute.getValues().size(), 2);
+        Assert.assertEquals(decodedAttribute.getValues().get(0).getNativeValue(), "First");
+        Assert.assertEquals(decodedAttribute.getValues().get(1).getNativeValue(), "Second");
+    }
+    
+    @Test
+    public void testDecodingStringArrayAsString() throws ComponentInitializationException, AttributeDecodingException {
+        
+        ruleset.put(OIDCStringAttributeTranscoder.PROP_ARRAY_AS_STRING, true);
+        
+        registry.setTranscoderRegistry(Collections.singletonList(new TranscodingRule(ruleset)));
+        registry.setApplicationContext(new MockApplicationContext());        
+        registry.initialize();        
+       
+        // Map from oidc claim.name 'bar' to shib attribute id 'foo'. 
+        final JSONObject inputAttribute = new JSONObject();        
+        final JSONArray array = new JSONArray();
+        array.add("First");
+        array.add("Second");               
+        inputAttribute.put(ATTR_NAME, array);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(inputAttribute);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule tr = rulesets.iterator().next();
+
+        final IdPAttribute decodedAttribute = TranscoderSupport.<JSONObject>getTranscoder(tr)
+                .decode(null, inputAttribute, tr);
+
+        Assert.assertNotNull(decodedAttribute);
+        Assert.assertEquals(decodedAttribute.getId(), ATTR_ID);
+        Assert.assertEquals(decodedAttribute.getValues().size(), 1);
+        Assert.assertEquals(decodedAttribute.getValues().get(0).getNativeValue(), "[\"First\",\"Second\"]");
+    }
+    
+    @Test
+    public void testDecodingComplexArray() throws ComponentInitializationException, AttributeDecodingException {
+        
+        // Convert all types below to strings
+        ruleset.put(OIDCStringAttributeTranscoder.PROP_ASOBJECT, true);
+        ruleset.put(OIDCStringAttributeTranscoder.PROP_NUMBER_AS_STRING, true);
+        ruleset.put(OIDCStringAttributeTranscoder.PROP_BOOLEAN_AS_STRING, true);
+        
+        registry.setTranscoderRegistry(Collections.singletonList(new TranscodingRule(ruleset)));
+        registry.setApplicationContext(new MockApplicationContext());        
+        registry.initialize();        
+       
+        // Map from oidc claim.name 'bar' to shib attribute id 'foo'. 
+        final JSONObject inputAttribute = new JSONObject();        
+        final JSONArray firstArray = new JSONArray();
+        firstArray.add("First");
+        
+        final JSONArray secondArray = new JSONArray();
+        secondArray.add("Second"); 
+        secondArray.add(true); 
+        secondArray.add(1); 
+        
+        firstArray.add(secondArray);
+        
+        final JSONArray thirdArray = new JSONArray();
+        thirdArray.add(3.14);
+        
+        final JSONObject nestedObject = new JSONObject();
+        nestedObject.put("name","John");
+        nestedObject.put("age","30");
+        
+        thirdArray.add(nestedObject);        
+        secondArray.add(thirdArray);
+        
+        inputAttribute.put(ATTR_NAME, firstArray);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(inputAttribute);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule tr = rulesets.iterator().next();
+
+        final IdPAttribute decodedAttribute = TranscoderSupport.<JSONObject>getTranscoder(tr)
+                .decode(null, inputAttribute, tr);
+
+        Assert.assertNotNull(decodedAttribute);
+        Assert.assertEquals(decodedAttribute.getId(), ATTR_ID);
+        Assert.assertEquals(decodedAttribute.getValues().size(), 6);
+        Assert.assertEquals(decodedAttribute.getValues().get(0).getNativeValue(), "First");
+        Assert.assertEquals(decodedAttribute.getValues().get(1).getNativeValue(), "Second");
+        Assert.assertEquals(decodedAttribute.getValues().get(2).getNativeValue(), "true");
+        Assert.assertEquals(decodedAttribute.getValues().get(3).getNativeValue(), "1");
+        Assert.assertEquals(decodedAttribute.getValues().get(4).getNativeValue(), "3.14");
+        Assert.assertEquals(decodedAttribute.getValues().get(5).getNativeValue(), "{\"name\":\"John\",\"age\":\"30\"}");
+    }
+    
+    @Test
+    public void testDecodingComplexArray_NumberNotSupported() 
+            throws ComponentInitializationException, AttributeDecodingException {
+        
+        // Do not convert numbers to string, any in the input will trigger an exception
+        ruleset.put(OIDCStringAttributeTranscoder.PROP_ASOBJECT, true);
+        // Do not convert numbers
+        ruleset.put(OIDCStringAttributeTranscoder.PROP_NUMBER_AS_STRING, false);
+        // convert boolean
+        ruleset.put(OIDCStringAttributeTranscoder.PROP_BOOLEAN_AS_STRING, true);
+        
+        registry.setTranscoderRegistry(Collections.singletonList(new TranscodingRule(ruleset)));
+        registry.setApplicationContext(new MockApplicationContext());        
+        registry.initialize();        
+       
+        // Map from oidc claim.name 'bar' to shib attribute id 'foo'. 
+        final JSONObject inputAttribute = new JSONObject();        
+        final JSONArray firstArray = new JSONArray();
+        firstArray.add("First");
+        
+        final JSONArray secondArray = new JSONArray();
+        secondArray.add("Second"); 
+        secondArray.add(true); 
+        secondArray.add(1); 
+        
+        firstArray.add(secondArray);
+        
+        final JSONArray thirdArray = new JSONArray();
+        thirdArray.add(3.14);
+        
+        final JSONObject nestedObject = new JSONObject();
+        nestedObject.put("name","John");
+        nestedObject.put("age","30");
+        
+        thirdArray.add(nestedObject);        
+        secondArray.add(thirdArray);
+        
+        inputAttribute.put(ATTR_NAME, firstArray);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(inputAttribute);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule tr = rulesets.iterator().next();
+
+        final IdPAttribute decodedAttribute = TranscoderSupport.<JSONObject>getTranscoder(tr)
+                .decode(null, inputAttribute, tr);
+        
+        Assert.assertNotNull(decodedAttribute);
+        Assert.assertEquals(decodedAttribute.getId(), ATTR_ID);
+        Assert.assertEquals(decodedAttribute.getValues().size(), 4);
+        Assert.assertEquals(decodedAttribute.getValues().get(0).getNativeValue(), "First");
+        Assert.assertEquals(decodedAttribute.getValues().get(1).getNativeValue(), "Second");
+        Assert.assertEquals(decodedAttribute.getValues().get(2).getNativeValue(), "true");
+        Assert.assertEquals(decodedAttribute.getValues().get(3).getNativeValue(), "{\"name\":\"John\",\"age\":\"30\"}");
+   }
+    
+    @Test
+    public void testDecodingInteger() throws ComponentInitializationException, AttributeDecodingException {
+        
+        ruleset.put(OIDCStringAttributeTranscoder.PROP_NUMBER_AS_STRING, true);
+
+        registry.setTranscoderRegistry(Collections.singletonList(new TranscodingRule(ruleset)));
+        registry.setApplicationContext(new MockApplicationContext());        
+        registry.initialize();        
+       
+        // Map from oidc claim.name 'bar' to shib attribute id 'foo'. 
+        final JSONObject inputAttribute = new JSONObject();                      
+        inputAttribute.put(ATTR_NAME, 1);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(inputAttribute);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule tr = rulesets.iterator().next();
+
+        final IdPAttribute decodedAttribute = TranscoderSupport.<JSONObject>getTranscoder(tr)
+                .decode(null, inputAttribute, tr);
+        
+        Assert.assertNotNull(decodedAttribute);
+        Assert.assertEquals(decodedAttribute.getId(), ATTR_ID);
+        Assert.assertEquals(decodedAttribute.getValues().size(), 1);
+        Assert.assertNotNull(decodedAttribute.getValues().get(0).getNativeValue());
+        Assert.assertEquals(decodedAttribute.getValues().get(0).getNativeValue(), "1");
+    }
+    
+    @Test
+    public void testDecodingInteger_NotSupported() throws ComponentInitializationException, AttributeDecodingException {
+        
+        ruleset.put(OIDCStringAttributeTranscoder.PROP_NUMBER_AS_STRING, false);
+
+        registry.setTranscoderRegistry(Collections.singletonList(new TranscodingRule(ruleset)));
+        registry.setApplicationContext(new MockApplicationContext());        
+        registry.initialize();        
+       
+        // Map from oidc claim.name 'bar' to shib attribute id 'foo'. 
+        final JSONObject inputAttribute = new JSONObject();                      
+        inputAttribute.put(ATTR_NAME, 1);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(inputAttribute);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule tr = rulesets.iterator().next();
+
+        final IdPAttribute decodedAttribute = TranscoderSupport.<JSONObject>getTranscoder(tr)
+                .decode(null, inputAttribute, tr);
+        
+        Assert.assertNotNull(decodedAttribute);
+        Assert.assertEquals(decodedAttribute.getId(), ATTR_ID);
+        Assert.assertEquals(decodedAttribute.getValues().size(), 0);
+    }
+    
+    @Test
+    public void testDecodingDouble() throws ComponentInitializationException, AttributeDecodingException {
+
+        ruleset.put(OIDCStringAttributeTranscoder.PROP_NUMBER_AS_STRING, true);
+        
+        registry.setTranscoderRegistry(Collections.singletonList(new TranscodingRule(ruleset)));
+        registry.setApplicationContext(new MockApplicationContext());        
+        registry.initialize();        
+       
+        // Map from oidc claim.name 'bar' to shib attribute id 'foo'. 
+        final JSONObject inputAttribute = new JSONObject();                      
+        inputAttribute.put(ATTR_NAME, 3.14);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(inputAttribute);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule tr = rulesets.iterator().next();
+
+        final IdPAttribute decodedAttribute = TranscoderSupport.<JSONObject>getTranscoder(tr)
+                .decode(null, inputAttribute, tr);
+
+        Assert.assertNotNull(decodedAttribute);
+        Assert.assertEquals(decodedAttribute.getId(), ATTR_ID);
+        Assert.assertEquals(decodedAttribute.getValues().size(), 1);
+        Assert.assertNotNull(decodedAttribute.getValues().get(0).getNativeValue());
+        Assert.assertEquals(decodedAttribute.getValues().get(0).getNativeValue(), "3.14");
+    }
+    
+    
+    @Test
+    public void testDecodingBoolean() throws ComponentInitializationException, AttributeDecodingException {
+        
+        ruleset.put(OIDCStringAttributeTranscoder.PROP_BOOLEAN_AS_STRING, true);
+        
+        registry.setTranscoderRegistry(Collections.singletonList(new TranscodingRule(ruleset)));
+        registry.setApplicationContext(new MockApplicationContext());        
+        registry.initialize();        
+       
+        // Map from oidc claim.name 'bar' to shib attribute id 'foo'. 
+        final JSONObject inputAttribute = new JSONObject();                      
+        inputAttribute.put(ATTR_NAME, true);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(inputAttribute);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule tr = rulesets.iterator().next();
+
+        final IdPAttribute decodedAttribute = TranscoderSupport.<JSONObject>getTranscoder(tr)
+                .decode(null, inputAttribute, tr);
+
+        Assert.assertNotNull(decodedAttribute);
+        Assert.assertEquals(decodedAttribute.getId(), ATTR_ID);
+        Assert.assertEquals(decodedAttribute.getValues().size(), 1);
+        Assert.assertNotNull(decodedAttribute.getValues().get(0).getNativeValue());
+        Assert.assertEquals(decodedAttribute.getValues().get(0).getNativeValue(), "true");
+    }
+    
+    @Test
+    public void testDecodingBoolean_NotSupported() 
+            throws ComponentInitializationException, AttributeDecodingException {
+        
+        ruleset.put(OIDCStringAttributeTranscoder.PROP_BOOLEAN_AS_STRING, false);
+        
+        registry.setTranscoderRegistry(Collections.singletonList(new TranscodingRule(ruleset)));
+        registry.setApplicationContext(new MockApplicationContext());        
+        registry.initialize();        
+       
+        // Map from oidc claim.name 'bar' to shib attribute id 'foo'. 
+        final JSONObject inputAttribute = new JSONObject();                      
+        inputAttribute.put(ATTR_NAME, true);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(inputAttribute);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule tr = rulesets.iterator().next();
+
+        final IdPAttribute decodedAttribute = TranscoderSupport.<JSONObject>getTranscoder(tr)
+                .decode(null, inputAttribute, tr);
+        
+        Assert.assertNotNull(decodedAttribute);
+        Assert.assertEquals(decodedAttribute.getId(), ATTR_ID);
+        Assert.assertEquals(decodedAttribute.getValues().size(), 0);
+    }
+    
+    @Test
+    public void testDecodingJSONObject() throws ComponentInitializationException, AttributeDecodingException {
+        
+        ruleset.put(OIDCStringAttributeTranscoder.PROP_ASOBJECT, true);
+        
+        registry.setTranscoderRegistry(Collections.singletonList(new TranscodingRule(ruleset)));
+        registry.setApplicationContext(new MockApplicationContext());        
+        registry.initialize();        
+       
+        // Map from oidc claim.name 'bar' to shib attribute id 'foo'. 
+        final JSONObject inputAttribute = new JSONObject();    
+        final JSONObject nested = new JSONObject();
+        nested.put("name","John");
+        nested.put("age","30");
+        inputAttribute.put(ATTR_NAME, nested);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(inputAttribute);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule tr = rulesets.iterator().next();
+
+        final IdPAttribute decodedAttribute = TranscoderSupport.<JSONObject>getTranscoder(tr)
+                .decode(null, inputAttribute, tr);
+
+        Assert.assertNotNull(decodedAttribute);
+        Assert.assertEquals(decodedAttribute.getId(), ATTR_ID);
+        Assert.assertEquals(decodedAttribute.getValues().size(), 1);
+        Assert.assertNotNull(decodedAttribute.getValues().get(0).getNativeValue());
+        Assert.assertEquals(decodedAttribute.getValues().get(0)
+                .getNativeValue(), "{\"name\":\"John\",\"age\":\"30\"}");
+    }
+    
+    @Test
+    public void testDecodingJSONObjectDisabledByRule() throws ComponentInitializationException, AttributeDecodingException {
+        
+        
+        ruleset.put(OIDCStringAttributeTranscoder.PROP_ASOBJECT, false);
+        
+        registry.setTranscoderRegistry(Collections.singletonList(new TranscodingRule(ruleset)));
+        registry.setApplicationContext(new MockApplicationContext());        
+        registry.initialize();        
+       
+        // Map from oidc claim.name 'bar' to shib attribute id 'foo'. 
+        final JSONObject inputAttribute = new JSONObject();    
+        final JSONObject nested = new JSONObject();
+        nested.put("name","John");
+        nested.put("age","30");
+        inputAttribute.put(ATTR_NAME, nested);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(inputAttribute);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule tr = rulesets.iterator().next();
+
+        final IdPAttribute decodedAttribute = TranscoderSupport.<JSONObject>getTranscoder(tr)
+                .decode(null, inputAttribute, tr);
+        
+        Assert.assertNotNull(decodedAttribute);
+        Assert.assertEquals(decodedAttribute.getId(), ATTR_ID);
+        Assert.assertEquals(decodedAttribute.getValues().size(), 0);
+
+    }
+    
+    @Test
+    public void testDecodingWrongType() throws ComponentInitializationException, AttributeDecodingException {
+        
+        registry.setTranscoderRegistry(Collections.singletonList(new TranscodingRule(ruleset)));
+        registry.setApplicationContext(new MockApplicationContext());        
+        registry.initialize();        
+       
+        // Map from oidc claim.name 'bar' to shib attribute id 'foo'. 
+        final JSONObject inputAttribute = new JSONObject();    
+        inputAttribute.put(ATTR_NAME, new byte[1]);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(inputAttribute);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule tr = rulesets.iterator().next();
+        
+        final IdPAttribute decodedAttribute = TranscoderSupport.<JSONObject>getTranscoder(tr)
+                .decode(null, inputAttribute, tr);
+        
+        Assert.assertNotNull(decodedAttribute);
+        Assert.assertEquals(decodedAttribute.getId(), ATTR_ID);
+        Assert.assertEquals(decodedAttribute.getValues().size(), 0);
+
+    }
 
 }
\ 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