[java-oidc-common] branch main updated: JCOMOIDC-40 - Complete OIDCAttributeTranscoder decode values function
Phil Smart
philip.smart at jisc.ac.uk
Thu Jun 8 13:07:41 UTC 2023
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=e859ededa6252e013e4e2dcb7512da7990a6bad3
The following commit(s) were added to refs/heads/main by this push:
new e859ede JCOMOIDC-40 - Complete OIDCAttributeTranscoder decode values function
e859ede is described below
commit e859ededa6252e013e4e2dcb7512da7990a6bad3
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Thu Jun 8 14:07:37 2023 +0100
JCOMOIDC-40 - Complete OIDCAttributeTranscoder decode values function
- Completed #decodeValues of the OIDCScopedStringAttributeTranscoder.
Supports JSON Strings and Strings nested in JSON Arrays.
https://shibboleth.atlassian.net/browse/JCOMOIDC-40
---
.../impl/OIDCScopedStringAttributeTranscoder.java | 43 +++-
.../OIDCScopedStringAttributeTranscoderTest.java | 246 ++++++++++++++++++++-
2 files changed, 279 insertions(+), 10 deletions(-)
diff --git a/oidc-common-attribute-impl/src/main/java/net/shibboleth/oidc/attribute/transcoding/impl/OIDCScopedStringAttributeTranscoder.java b/oidc-common-attribute-impl/src/main/java/net/shibboleth/oidc/attribute/transcoding/impl/OIDCScopedStringAttributeTranscoder.java
index fca7618..c478d76 100644
--- a/oidc-common-attribute-impl/src/main/java/net/shibboleth/oidc/attribute/transcoding/impl/OIDCScopedStringAttributeTranscoder.java
+++ b/oidc-common-attribute-impl/src/main/java/net/shibboleth/oidc/attribute/transcoding/impl/OIDCScopedStringAttributeTranscoder.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;
@@ -25,6 +26,8 @@ import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.opensaml.profile.context.ProfileRequestContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import net.minidev.json.JSONArray;
import net.shibboleth.idp.attribute.AttributeDecodingException;
@@ -32,6 +35,7 @@ import net.shibboleth.idp.attribute.AttributeEncodingException;
import net.shibboleth.idp.attribute.IdPAttribute;
import net.shibboleth.idp.attribute.IdPAttributeValue;
import net.shibboleth.idp.attribute.ScopedStringAttributeValue;
+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;
@@ -39,13 +43,20 @@ import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
/**
* Class encoding scoped string attributes to string json object. Name of the attribute will be set as the key. The
- * string contains attribute value, delimiter and scope catenated. If there are several attribute values they are
+ * string contains attribute value, delimiter and scope concatenated. If there are several attribute values they are
* delimited with delimiter(space is default) or placed to array.
+ *
+ * <p>For decoding, JSON Strings are split by the delimiter into a {@link ScopedStringAttributeValue}. For JSON Arrays,
+ * the elements are recessively visited and any JSON Strings are split by their delimiter into a list of
+ * {@link ScopedStringAttributeValue}s. </p>
*/
public class OIDCScopedStringAttributeTranscoder extends AbstractOIDCAttributeTranscoder {
/** Scope delimiter for combining the data. */
@Nonnull @NotEmpty public static final String PROP_SCOPE_DELIMITER = "oidc.scopeDelimiter";
+
+ /** Class logger. */
+ @Nonnull private final Logger log = LoggerFactory.getLogger(OIDCScopedStringAttributeTranscoder.class);
/** {@inheritDoc} */
@Override
@@ -97,8 +108,32 @@ public class OIDCScopedStringAttributeTranscoder extends AbstractOIDCAttributeTr
@Nullable final ProfileRequestContext profileRequestContext, @Nonnull final Object input,
@Nonnull final TranscodingRule rule) throws AttributeDecodingException {
- // TODO: implement value decoding
- return Collections.emptyList();
+ if (input instanceof String) {
+ final String stringValue = (String)input;
+ final String scopeDelimiter = rule.getOrDefault(PROP_SCOPE_DELIMITER, String.class, "@");
+ final int offset = stringValue.indexOf(scopeDelimiter);
+ if (offset < 0) {
+ log.warn("Ignoring value with no scope delimiter ({})", scopeDelimiter);
+ return Collections.emptyList();
+ }
+ return List.of(ScopedStringAttributeValue.valueOf(stringValue.substring(0, offset),
+ stringValue.substring(offset + scopeDelimiter.length())));
+ }
+ else if (input instanceof JSONArray) {
+ // Deal with array recursively
+ 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 {
+ log.trace("Could not convert values of type {} for the claim '{}' to a scoped 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();
+ }
}
-
}
\ No newline at end of file
diff --git a/oidc-common-attribute-impl/src/test/java/net/shibboleth/oidc/attribute/transcoding/impl/OIDCScopedStringAttributeTranscoderTest.java b/oidc-common-attribute-impl/src/test/java/net/shibboleth/oidc/attribute/transcoding/impl/OIDCScopedStringAttributeTranscoderTest.java
index 2027271..f0927f3 100644
--- a/oidc-common-attribute-impl/src/test/java/net/shibboleth/oidc/attribute/transcoding/impl/OIDCScopedStringAttributeTranscoderTest.java
+++ b/oidc-common-attribute-impl/src/test/java/net/shibboleth/oidc/attribute/transcoding/impl/OIDCScopedStringAttributeTranscoderTest.java
@@ -24,9 +24,15 @@ 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 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;
@@ -39,14 +45,8 @@ 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.OIDCScopedStringAttributeTranscoder;
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;
-
public class OIDCScopedStringAttributeTranscoderTest {
private AttributeTranscoderRegistryImpl registry;
@@ -59,6 +59,7 @@ public class OIDCScopedStringAttributeTranscoderTest {
private final static String STRING_2 = "value2";
private final static String SCOPE_1 = "scope1";
private final static String SCOPE_2 = "scope2";
+ private final static String DELIMITER = "@";
@BeforeMethod
protected void setUp() throws Exception {
@@ -182,4 +183,237 @@ public class OIDCScopedStringAttributeTranscoderTest {
TranscoderSupport.<JSONObject>getTranscoder(tr).encode(null, inputAttribute, JSONObject.class, tr);
}
+
+ @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);
+ }
+
+ @Test
+ public void testDecodingScopedString() 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, STRING_1+ DELIMITER + SCOPE_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.assertTrue(decodedAttribute.getValues().get(0) instanceof ScopedStringAttributeValue);
+ final var scopedString = (ScopedStringAttributeValue) decodedAttribute.getValues().get(0);
+ Assert.assertEquals(scopedString.getScope(), SCOPE_1);
+ Assert.assertEquals(scopedString.getValue(), STRING_1);
+ }
+
+ @Test
+ public void testDecodingScopedStringWithNewDelimiter()
+ throws ComponentInitializationException, AttributeDecodingException {
+
+ ruleset.put(OIDCScopedStringAttributeTranscoder.PROP_SCOPE_DELIMITER, "#");
+
+ 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, STRING_1+ "#" + SCOPE_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.assertTrue(decodedAttribute.getValues().get(0) instanceof ScopedStringAttributeValue);
+ final var scopedString = (ScopedStringAttributeValue) decodedAttribute.getValues().get(0);
+ Assert.assertEquals(scopedString.getScope(), SCOPE_1);
+ Assert.assertEquals(scopedString.getValue(), STRING_1);
+ }
+
+ @Test
+ public void testDecodingUnScopedString() 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();
+ // No delimeter so should not decode
+ inputAttribute.put(ATTR_NAME, STRING_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 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'.
+ // Byte is not supported, so should not be decoded
+ 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);
+
+ }
+
+ @Test
+ public void testDecodingStringArrayAsScopedStrings() 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(STRING_1+ DELIMITER + SCOPE_1);
+ array.add(STRING_2+ DELIMITER + SCOPE_2);
+ 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(((ScopedStringAttributeValue)decodedAttribute.getValues().get(0)).getScope(), SCOPE_1);
+ Assert.assertEquals(((ScopedStringAttributeValue)decodedAttribute.getValues().get(0)).getValue(), STRING_1);
+ Assert.assertEquals(((ScopedStringAttributeValue)decodedAttribute.getValues().get(1)).getScope(), SCOPE_2);
+ Assert.assertEquals(((ScopedStringAttributeValue)decodedAttribute.getValues().get(1)).getValue(), STRING_2);
+ }
+
+ @Test
+ public void testDecodingComplexArray() 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 firstArray = new JSONArray();
+ firstArray.add(STRING_1+ DELIMITER + SCOPE_1);
+
+ final JSONArray secondArray = new JSONArray();
+ secondArray.add(STRING_2+ DELIMITER + SCOPE_2);
+
+ firstArray.add(secondArray);
+
+ 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(), 2);
+ Assert.assertEquals(((ScopedStringAttributeValue)decodedAttribute.getValues().get(0)).getScope(), SCOPE_1);
+ Assert.assertEquals(((ScopedStringAttributeValue)decodedAttribute.getValues().get(0)).getValue(), STRING_1);
+ Assert.assertEquals(((ScopedStringAttributeValue)decodedAttribute.getValues().get(1)).getScope(), SCOPE_2);
+ Assert.assertEquals(((ScopedStringAttributeValue)decodedAttribute.getValues().get(1)).getValue(), STRING_2);
+ }
+
+ @Test
+ public void testDecodingComplexArray_NumberNotSupported()
+ 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 firstArray = new JSONArray();
+ firstArray.add(STRING_1+ DELIMITER + SCOPE_1);
+
+ // This should be ignored in the result
+ final JSONArray secondArray = new JSONArray();
+ secondArray.add(1);
+
+ firstArray.add(secondArray);
+
+ 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(), 1);
+ Assert.assertEquals(((ScopedStringAttributeValue)decodedAttribute.getValues().get(0)).getScope(), SCOPE_1);
+ Assert.assertEquals(((ScopedStringAttributeValue)decodedAttribute.getValues().get(0)).getValue(), STRING_1);
+
+ }
+
}
\ 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