[java-shib-attribute] branch main updated: IDP-1992 - Add a DateAttributeValue type to avoid formatting conversions

Scott Cantor cantor.2 at osu.edu
Mon Aug 8 19:42:42 UTC 2022


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=9409b915d138182ee44f535ac837426cdc973aa2

The following commit(s) were added to refs/heads/main by this push:
     new 9409b915d IDP-1992 - Add a DateAttributeValue type to avoid formatting conversions
9409b915d is described below

commit 9409b915d138182ee44f535ac837426cdc973aa2
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Mon Aug 8 15:42:39 2022 -0400

    IDP-1992 - Add a DateAttributeValue type to avoid formatting conversions
    
    https://shibboleth.atlassian.net/browse/IDP-1992
---
 .../idp/attribute/DateTimeAttributeValue.java      | 108 +++++++
 .../attribute/transcoding/SAMLEncoderSupport.java  |  47 +++
 .../impl/SAML2DateTimeAttributeTranscoder.java     | 165 +++++++++++
 .../impl/SAML2DateTimeAttributeTranscoderTest.java | 315 +++++++++++++++++++++
 4 files changed, 635 insertions(+)

diff --git a/shib-attribute-api/src/main/java/net/shibboleth/idp/attribute/DateTimeAttributeValue.java b/shib-attribute-api/src/main/java/net/shibboleth/idp/attribute/DateTimeAttributeValue.java
new file mode 100644
index 000000000..d0c509eae
--- /dev/null
+++ b/shib-attribute-api/src/main/java/net/shibboleth/idp/attribute/DateTimeAttributeValue.java
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.attribute;
+
+import java.time.Instant;
+import java.time.ZonedDateTime;
+import java.util.Objects;
+
+import javax.annotation.Nonnull;
+
+import net.shibboleth.utilities.java.support.annotation.ParameterName;
+import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+
+import com.google.common.base.MoreObjects;
+
+/**
+ * Base class for {@link IdPAttribute} values that are date/time values.
+ * 
+ * @since 4.3.0
+ */
+public class DateTimeAttributeValue implements IdPAttributeValue {
+
+    /** The attribute value. */
+    @Nonnull @NotEmpty private final Instant value;
+
+    /**
+     * Constructor.
+     * 
+     * @param attributeValue the attribute value
+     */
+    public DateTimeAttributeValue(
+            @Nonnull @NotEmpty @ParameterName(name="attributeValue") final Instant attributeValue) {
+        value = Constraint.isNotNull(attributeValue, "Attribute value cannot be null or empty");
+    }
+
+    /**
+     * Constructor.
+     * 
+     * @param attributeValue the attribute value
+     */
+    public DateTimeAttributeValue(
+            @Nonnull @NotEmpty @ParameterName(name="attributeValue") final ZonedDateTime attributeValue) {
+        value = Constraint.isNotNull(attributeValue, "Attribute value cannot be null or empty").toInstant();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Object getNativeValue() {
+        return value;
+    }
+
+    /** Return the value.
+     * @return the value
+     */
+    @Nonnull @NotEmpty public final Instant getValue() {
+        return value;
+    }
+
+    /** {@inheritDoc} */
+    @Override @Nonnull @NotEmpty public String getDisplayValue() {
+        return value.toString();
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean equals(final Object obj) {
+        if (obj == null) {
+            return false;
+        }
+
+        if (obj == this) {
+            return true;
+        }
+
+        if (!(obj instanceof DateTimeAttributeValue)) {
+            return false;
+        }
+
+        final DateTimeAttributeValue other = (DateTimeAttributeValue) obj;
+        return Objects.equals(value, other.value);
+    }
+
+    /** {@inheritDoc} */
+    @Override public int hashCode() {
+        return value.hashCode();
+    }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return MoreObjects.toStringHelper(this).add("value", value).toString();
+    }
+
+}
\ No newline at end of file
diff --git a/shib-saml-attribute-api/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/SAMLEncoderSupport.java b/shib-saml-attribute-api/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/SAMLEncoderSupport.java
index eb7ab99d4..2f421b60e 100644
--- a/shib-saml-attribute-api/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/SAMLEncoderSupport.java
+++ b/shib-saml-attribute-api/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/SAMLEncoderSupport.java
@@ -17,6 +17,8 @@
 
 package net.shibboleth.idp.saml.attribute.transcoding;
 
+import java.time.Instant;
+
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 import javax.xml.namespace.QName;
@@ -28,12 +30,14 @@ import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
 import net.shibboleth.utilities.java.support.codec.Base64Support;
 import net.shibboleth.utilities.java.support.codec.EncodingException;
 import net.shibboleth.utilities.java.support.logic.Constraint;
+import net.shibboleth.utilities.java.support.xml.DOMTypeSupport;
 
 import org.opensaml.core.xml.XMLObject;
 import org.opensaml.core.xml.XMLObjectBuilder;
 import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
 import org.opensaml.core.xml.schema.XSAny;
 import org.opensaml.core.xml.schema.XSBase64Binary;
+import org.opensaml.core.xml.schema.XSDateTime;
 import org.opensaml.core.xml.schema.XSString;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -221,4 +225,47 @@ public final class SAMLEncoderSupport {
 
         return encodeStringValue(attribute, attributeValueElementName, builder.toString(), withType);
     }
+
+
+    /**
+     * Encodes a date/time value into a SAML attribute value element.
+     * 
+     * @param attribute attribute to be encoded
+     * @param attributeValueElementName the element name to create
+     * @param value value to encoded
+     * @param withType whether to include xsi:type
+     * 
+     * @return the attribute value element or null if the given value was null or empty
+     * 
+     * @since 4.3.0
+     */
+    @Nullable public static XMLObject encodeDateTimeValue(@Nonnull final IdPAttribute attribute,
+            @Nonnull final QName attributeValueElementName, @Nullable final Instant value, final boolean withType) {
+        Constraint.isNotNull(attribute, "Attribute cannot be null");
+        Constraint.isNotNull(attributeValueElementName, "Attribute Element Name cannot be null");
+
+        if (value == null) {
+            LOG.debug("Skipping null value for attribute {}", attribute.getId());
+            return null;
+        }
+
+        LOG.debug("Encoding value {} of attribute {}", value, attribute.getId());
+        
+        if (withType) {
+            final XMLObjectBuilder<XSDateTime> dateTimeBuilder =
+                    XMLObjectProviderRegistrySupport.getBuilderFactory().<XSDateTime>getBuilderOrThrow(
+                            XSDateTime.TYPE_NAME);
+            final XSDateTime samlAttributeValue =
+                    dateTimeBuilder.buildObject(attributeValueElementName, XSDateTime.TYPE_NAME);
+            samlAttributeValue.setValue(value);
+            return samlAttributeValue;
+        }
+        
+        final XMLObjectBuilder<XSAny> anyBuilder =
+                XMLObjectProviderRegistrySupport.getBuilderFactory().<XSAny>getBuilderOrThrow(XSAny.TYPE_NAME);
+        final XSAny samlAttributeValue = anyBuilder.buildObject(attributeValueElementName);
+        samlAttributeValue.setTextContent(DOMTypeSupport.instantToString(value));
+        return samlAttributeValue;
+    }
+
 }
\ No newline at end of file
diff --git a/shib-saml-attribute-impl/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML2DateTimeAttributeTranscoder.java b/shib-saml-attribute-impl/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML2DateTimeAttributeTranscoder.java
new file mode 100644
index 000000000..e85cbf2b1
--- /dev/null
+++ b/shib-saml-attribute-impl/src/main/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML2DateTimeAttributeTranscoder.java
@@ -0,0 +1,165 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.saml.attribute.transcoding.impl;
+
+import java.time.Instant;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.core.xml.XMLObject;
+import org.opensaml.core.xml.schema.XSAny;
+import org.opensaml.core.xml.schema.XSDateTime;
+import org.opensaml.core.xml.schema.XSInteger;
+import org.opensaml.core.xml.schema.XSString;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.saml.saml2.core.Attribute;
+import org.opensaml.saml.saml2.core.AttributeValue;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import net.shibboleth.idp.attribute.AttributeEncodingException;
+import net.shibboleth.idp.attribute.DateTimeAttributeValue;
+import net.shibboleth.idp.attribute.IdPAttribute;
+import net.shibboleth.idp.attribute.IdPAttributeValue;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoder;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry;
+import net.shibboleth.idp.attribute.transcoding.TranscodingRule;
+import net.shibboleth.idp.saml.attribute.transcoding.AbstractSAML2AttributeTranscoder;
+import net.shibboleth.idp.saml.attribute.transcoding.SAMLEncoderSupport;
+import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
+import net.shibboleth.utilities.java.support.xml.DOMTypeSupport;
+
+/**
+ * {@link AttributeTranscoder} that supports {@link Attribute} and {@link DateTimeAttributeValue} objects.
+ * 
+ * @since 4.3.0
+ */
+public class SAML2DateTimeAttributeTranscoder extends AbstractSAML2AttributeTranscoder<DateTimeAttributeValue> {
+
+    /** One of "ms" or "s", controlling the unit to use when converting to an epoch. */
+    @Nonnull @NotEmpty public static final String PROP_EPOCH_UNITS = "saml2.epochUnits";
+
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(SAML2DateTimeAttributeTranscoder.class);
+    
+    /** {@inheritDoc} */
+    @Override protected boolean canEncodeValue(@Nonnull final IdPAttribute attribute,
+            @Nonnull final IdPAttributeValue value) {
+        return value instanceof DateTimeAttributeValue;
+    }
+
+    /** {@inheritDoc} */
+    @Override @Nullable protected XMLObject encodeValue(@Nullable final ProfileRequestContext profileRequestContext,
+            @Nonnull final IdPAttribute attribute, @Nonnull final TranscodingRule rule,
+            @Nonnull final DateTimeAttributeValue value) throws AttributeEncodingException {
+        
+        final Boolean encodeType = rule.getOrDefault(PROP_ENCODE_TYPE, Boolean.class, Boolean.TRUE);
+        
+        return SAMLEncoderSupport.encodeDateTimeValue(attribute, AttributeValue.DEFAULT_ELEMENT_NAME, value.getValue(),
+                encodeType);
+    }
+
+    /** {@inheritDoc} */
+    @Override @Nullable protected IdPAttributeValue decodeValue(
+            @Nullable final ProfileRequestContext profileRequestContext, @Nonnull final Attribute attribute,
+            @Nonnull final TranscodingRule rule, @Nullable final XMLObject value) {
+        
+        return value != null ? new DateTimeAttributeValue(getDateTimeValue(rule, value)) : null;
+    }
+    
+    /**
+     * Function to return an XML object in date/time form.
+     * 
+     * @param rule transcoding rule
+     * @param object object to decode
+     * 
+     * @return decoded date/time, or null
+     */
+    @Nullable protected Instant getDateTimeValue(@Nonnull final TranscodingRule rule, @Nonnull final XMLObject object) {
+        Instant retVal = null;
+
+        if (object instanceof XSString) {
+            
+            return getDateTimeValue(rule, ((XSString) object).getValue());
+            
+        } else if (object instanceof XSInteger) {
+
+            return getDateTimeValue(rule, ((XSInteger) object).getValue().longValue());
+
+        } else if (object instanceof XSDateTime) {
+
+            retVal = ((XSDateTime) object).getValue();
+
+        } else if (object instanceof XSAny) {
+
+            final XSAny wc = (XSAny) object;
+            if (wc.getUnknownAttributes().isEmpty() && wc.getUnknownXMLObjects().isEmpty()) {
+                return getDateTimeValue(rule, wc.getTextContent());
+            }
+        }
+
+        if (null == retVal) {
+            log.info("Value of type {} could not be converted", object.getClass().getSimpleName());
+        }
+        return retVal;
+    }
+
+    /**
+     * Convert a string value into an {@link Instant}.
+     * 
+     * @param rule transcoding rule
+     * @param value input value
+     * 
+     * @return converted result or null
+     */
+    @Nullable protected Instant getDateTimeValue(@Nonnull final TranscodingRule rule, @Nullable final String value) {
+        try {
+            final Long longVal = Long.valueOf(value);
+            if (longVal != null) {
+                return getDateTimeValue(rule, longVal);
+            }
+        } catch (final NumberFormatException e) {
+            return DOMTypeSupport.stringToInstant(value);
+        }
+        
+        return null;
+    }
+    
+    /**
+     * Convert a long value into an {@link Instant}.
+     * 
+     * @param rule transcoding rule
+     * @param value input value
+     * 
+     * @return converted result
+     */
+    @Nullable protected Instant getDateTimeValue(@Nonnull final TranscodingRule rule, @Nonnull final Long value) {
+        final String units = rule.getOrDefault(PROP_EPOCH_UNITS, String.class, "s");
+        if ("s".equals(units)) {
+            return Instant.ofEpochSecond(value);
+        } else if ("ms".equals(units)) {
+            return Instant.ofEpochMilli(value);
+        }
+        
+        log.error("{} rule property {} must be 's' or 'ms'",
+                rule.getOrDefault(AttributeTranscoderRegistry.PROP_ID, String.class, "(none)"), PROP_EPOCH_UNITS);
+        return null;
+    }
+
+}
\ No newline at end of file
diff --git a/shib-saml-attribute-impl/src/test/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML2DateTimeAttributeTranscoderTest.java b/shib-saml-attribute-impl/src/test/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML2DateTimeAttributeTranscoderTest.java
new file mode 100644
index 000000000..da03a2b00
--- /dev/null
+++ b/shib-saml-attribute-impl/src/test/java/net/shibboleth/idp/saml/attribute/transcoding/impl/SAML2DateTimeAttributeTranscoderTest.java
@@ -0,0 +1,315 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.saml.attribute.transcoding.impl;
+
+import java.time.Instant;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import net.shibboleth.ext.spring.testing.MockApplicationContext;
+import net.shibboleth.idp.attribute.AttributeEncodingException;
+import net.shibboleth.idp.attribute.ByteAttributeValue;
+import net.shibboleth.idp.attribute.DateTimeAttributeValue;
+import net.shibboleth.idp.attribute.IdPAttribute;
+import net.shibboleth.idp.attribute.IdPAttributeValue;
+import net.shibboleth.idp.attribute.IdPRequestedAttribute;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry;
+import net.shibboleth.idp.attribute.transcoding.BasicNamingFunction;
+import net.shibboleth.idp.attribute.transcoding.TranscoderSupport;
+import net.shibboleth.idp.attribute.transcoding.TranscodingRule;
+import net.shibboleth.idp.attribute.transcoding.impl.AttributeTranscoderRegistryImpl;
+import net.shibboleth.idp.saml.attribute.transcoding.AbstractSAML2AttributeTranscoder;
+import net.shibboleth.idp.saml.attribute.transcoding.SAML2AttributeTranscoder;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+
+import org.opensaml.core.testing.OpenSAMLInitBaseTestCase;
+import org.opensaml.core.xml.XMLObject;
+import org.opensaml.core.xml.XMLObjectBuilder;
+import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
+import org.opensaml.core.xml.schema.XSDateTime;
+import org.opensaml.core.xml.schema.XSString;
+import org.opensaml.saml.common.SAMLObjectBuilder;
+import org.opensaml.saml.saml2.core.Attribute;
+import org.opensaml.saml.saml2.core.AttributeValue;
+import org.opensaml.saml.saml2.metadata.RequestedAttribute;
+import org.testng.Assert;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+/** {@link SAML2DateTimeAttributeTranscoder} unit test. */
+public class SAML2DateTimeAttributeTranscoderTest extends OpenSAMLInitBaseTestCase {
+
+    private AttributeTranscoderRegistryImpl registry;
+    
+    private XMLObjectBuilder<XSString> stringBuilder;
+
+    private XMLObjectBuilder<XSDateTime> dateTimeBuilder;
+
+    private SAMLObjectBuilder<Attribute> attributeBuilder;
+
+    private SAMLObjectBuilder<RequestedAttribute> reqAttributeBuilder;
+
+    private final static String ATTR_NAME = "foo";
+    private final static String ATTR_NAMEFORMAT = "Namespace";
+    private final static String ATTR_FRIENDLYNAME = "friendly";
+    private final static String STRING_SECS = "1659979872";
+    private final static String STRING_MSECS = "1659979872969";
+    private final static String STRING_ISO = "2022-08-08T17:31:12.969Z";
+    private final static String STRING_INVALID = "invalid";
+        
+    @BeforeClass public void setUp() throws ComponentInitializationException {
+        
+        stringBuilder = XMLObjectProviderRegistrySupport.getBuilderFactory().<XSString>getBuilderOrThrow(XSString.TYPE_NAME);
+        dateTimeBuilder = XMLObjectProviderRegistrySupport.getBuilderFactory().<XSDateTime>getBuilderOrThrow(XSDateTime.TYPE_NAME);
+        
+        attributeBuilder = (SAMLObjectBuilder<Attribute>)
+                XMLObjectProviderRegistrySupport.getBuilderFactory().<Attribute>getBuilderOrThrow(
+                        Attribute.TYPE_NAME);
+        reqAttributeBuilder = (SAMLObjectBuilder<RequestedAttribute>)
+                XMLObjectProviderRegistrySupport.getBuilderFactory().<RequestedAttribute>getBuilderOrThrow(
+                        RequestedAttribute.TYPE_NAME);
+        
+        registry = new AttributeTranscoderRegistryImpl();
+        registry.setId("test");
+
+        final SAML2DateTimeAttributeTranscoder transcoder = new SAML2DateTimeAttributeTranscoder();
+        transcoder.initialize();
+        
+        registry.setNamingRegistry(Collections.singletonList(
+                new BasicNamingFunction<>(transcoder.getEncodedType(), new AbstractSAML2AttributeTranscoder.NamingFunction())));
+        
+        final Map<String,Object> ruleset1 = new HashMap<>();
+        ruleset1.put(AttributeTranscoderRegistry.PROP_ID, ATTR_NAME);
+        ruleset1.put(AttributeTranscoderRegistry.PROP_TRANSCODER, transcoder);
+        ruleset1.put(SAML2AttributeTranscoder.PROP_ENCODE_TYPE, true);
+        ruleset1.put(SAML2AttributeTranscoder.PROP_NAME, ATTR_NAME);
+        ruleset1.put(SAML2AttributeTranscoder.PROP_NAME_FORMAT, ATTR_NAMEFORMAT);
+        ruleset1.put(SAML2AttributeTranscoder.PROP_FRIENDLY_NAME, ATTR_FRIENDLYNAME);
+        
+        registry.setTranscoderRegistry(Collections.singletonList(new TranscodingRule(ruleset1)));
+        registry.setApplicationContext(new MockApplicationContext());
+        registry.initialize();
+    }
+    
+    @AfterClass public void tearDown() {
+        registry.destroy();
+        registry = null;
+    }
+
+    @Test public void emptyEncode() throws Exception {
+        final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(inputAttribute, Attribute.class);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        final Attribute attr = TranscoderSupport.<Attribute>getTranscoder(ruleset).encode(
+                null, inputAttribute, Attribute.class, ruleset);
+        
+        Assert.assertNotNull(attr);
+        Assert.assertEquals(attr.getName(), ATTR_NAME);
+        Assert.assertEquals(attr.getNameFormat(), ATTR_NAMEFORMAT);
+        Assert.assertEquals(attr.getFriendlyName(), ATTR_FRIENDLYNAME);
+        Assert.assertTrue(attr.getAttributeValues().isEmpty());
+    }
+
+    @Test public void emptyDecode() throws Exception {
+        
+        final Attribute samlAttribute = attributeBuilder.buildObject();
+        samlAttribute.setName(ATTR_NAME);
+        samlAttribute.setNameFormat(ATTR_NAMEFORMAT);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(samlAttribute);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        final IdPAttribute attr = TranscoderSupport.<Attribute>getTranscoder(ruleset).decode(null, samlAttribute, ruleset);
+        
+        Assert.assertNotNull(attr);
+        Assert.assertEquals(attr.getId(), ATTR_NAME);
+        Assert.assertTrue(attr.getValues().isEmpty());
+    }
+
+    @Test public void emptyRequestedDecode() throws Exception {
+        
+        final RequestedAttribute samlAttribute = reqAttributeBuilder.buildObject();
+        samlAttribute.setName(ATTR_NAME);
+        samlAttribute.setNameFormat(ATTR_NAMEFORMAT);
+        samlAttribute.setIsRequired(true);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(samlAttribute);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        final IdPAttribute attr = TranscoderSupport.<Attribute>getTranscoder(ruleset).decode(null, samlAttribute, ruleset);
+        
+        Assert.assertTrue(attr instanceof IdPRequestedAttribute);
+        Assert.assertEquals(attr.getId(), ATTR_NAME);
+        Assert.assertTrue(((IdPRequestedAttribute) attr).isRequired());
+        Assert.assertTrue(attr.getValues().isEmpty());
+    }
+    
+    @Test(expectedExceptions = {AttributeEncodingException.class,}) public void inappropriate() throws Exception {
+        final int[] intArray = {1, 2, 3, 4};
+        final List<IdPAttributeValue> values =
+                List.of(new ByteAttributeValue(new byte[] {1, 2, 3,}), new IdPAttributeValue() {
+                    @Override
+                    public Object getNativeValue() {
+                        return intArray;
+                    }
+                    @Override
+                    public String getDisplayValue() {
+                        return intArray.toString();
+                    }
+                });
+
+        final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
+        inputAttribute.setValues(values);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(inputAttribute, Attribute.class);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        TranscoderSupport.<Attribute>getTranscoder(ruleset).encode(null, inputAttribute, Attribute.class, ruleset);
+    }
+    
+    @Test public void single() throws Exception {
+        final List<IdPAttributeValue> values =
+                List.of(new ByteAttributeValue(new byte[] {1, 2, 3,}), new DateTimeAttributeValue(Instant.parse(STRING_ISO)));
+
+        final IdPAttribute inputAttribute = new IdPAttribute(ATTR_NAME);
+        inputAttribute.setValues(values);
+        
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(inputAttribute, Attribute.class);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        final Attribute attr = TranscoderSupport.<Attribute>getTranscoder(ruleset).encode(
+                null, inputAttribute, Attribute.class, ruleset);
+
+        Assert.assertNotNull(attr);
+        Assert.assertEquals(attr.getName(), ATTR_NAME);
+        Assert.assertEquals(attr.getNameFormat(), ATTR_NAMEFORMAT);
+        Assert.assertEquals(attr.getFriendlyName(), ATTR_FRIENDLYNAME);
+
+        final List<XMLObject> children = attr.getOrderedChildren();
+
+        Assert.assertEquals(children.size(), 1, "Encoding one entry");
+
+        final XMLObject child = children.get(0);
+
+        Assert.assertEquals(child.getElementQName(), AttributeValue.DEFAULT_ELEMENT_NAME,
+                "Attribute Value not inside <AttributeValue/>");
+
+        Assert.assertTrue(child instanceof XSDateTime, "Child of result attribute should be a string");
+
+        final XSDateTime childAsString = (XSDateTime) child;
+
+        Assert.assertEquals(childAsString.getValue(), Instant.parse(STRING_ISO));
+    }
+
+    @Test public void singleRequested() throws Exception {
+        final List<IdPAttributeValue> values =
+                List.of(new ByteAttributeValue(new byte[] {1, 2, 3,}),
+                        new DateTimeAttributeValue(Instant.ofEpochSecond(Long.valueOf(STRING_SECS))));
+
+        final IdPRequestedAttribute inputAttribute = new IdPRequestedAttribute(ATTR_NAME);
+        inputAttribute.setRequired(true);
+        inputAttribute.setValues(values);
+        
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(inputAttribute, Attribute.class);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+
+        final RequestedAttribute attr = TranscoderSupport.<RequestedAttribute>getTranscoder(ruleset).encode(
+                null, inputAttribute, RequestedAttribute.class, ruleset);
+
+        Assert.assertNotNull(attr);
+        Assert.assertEquals(attr.getName(), ATTR_NAME);
+        Assert.assertEquals(attr.getNameFormat(), ATTR_NAMEFORMAT);
+        Assert.assertEquals(attr.getFriendlyName(), ATTR_FRIENDLYNAME);
+        Assert.assertTrue(attr.isRequired());
+
+        final List<XMLObject> children = attr.getOrderedChildren();
+
+        Assert.assertEquals(children.size(), 1, "Encoding one entry");
+
+        final XMLObject child = children.get(0);
+
+        Assert.assertEquals(child.getElementQName(), AttributeValue.DEFAULT_ELEMENT_NAME,
+                "Attribute Value not inside <AttributeValue/>");
+
+        Assert.assertTrue(child instanceof XSDateTime, "Child of result attribute should be a string");
+
+        final XSDateTime childAsString = (XSDateTime) child;
+
+        Assert.assertEquals(childAsString.getValue().getEpochSecond(), Long.valueOf(STRING_SECS));
+    }
+    
+    @Test public void singleDecodeString() throws Exception {
+                
+        final XSString stringValue = stringBuilder.buildObject(AttributeValue.DEFAULT_ELEMENT_NAME);
+        stringValue.setValue(STRING_ISO);
+        
+        final Attribute samlAttribute = attributeBuilder.buildObject();
+        samlAttribute.setName(ATTR_NAME);
+        samlAttribute.setNameFormat(ATTR_NAMEFORMAT);
+        samlAttribute.getAttributeValues().add(stringValue);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(samlAttribute);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        final IdPAttribute attr = TranscoderSupport.<Attribute>getTranscoder(ruleset).decode(null, samlAttribute, ruleset);
+        
+        Assert.assertNotNull(attr);
+        Assert.assertEquals(attr.getId(), ATTR_NAME);
+        Assert.assertEquals(attr.getValues().size(), 1);
+        Assert.assertEquals(((DateTimeAttributeValue)attr.getValues().get(0)).getValue().toString(), STRING_ISO);
+    }
+    
+    
+    @Test public void singleRequestedDecode() throws Exception {
+        
+        final XSDateTime dateTimeValue = dateTimeBuilder.buildObject(AttributeValue.DEFAULT_ELEMENT_NAME);
+        dateTimeValue.setValue(Instant.ofEpochMilli(Long.valueOf(STRING_MSECS)));
+        
+        final RequestedAttribute samlAttribute = reqAttributeBuilder.buildObject();
+        samlAttribute.setName(ATTR_NAME);
+        samlAttribute.setNameFormat(ATTR_NAMEFORMAT);
+        samlAttribute.setIsRequired(true);
+        samlAttribute.getAttributeValues().add(dateTimeValue);
+
+        final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(samlAttribute);
+        Assert.assertEquals(rulesets.size(), 1);
+        final TranscodingRule ruleset = rulesets.iterator().next();
+        
+        final IdPAttribute attr = TranscoderSupport.<Attribute>getTranscoder(ruleset).decode(null, samlAttribute, ruleset);
+        
+        Assert.assertTrue(attr instanceof IdPRequestedAttribute);
+        Assert.assertEquals(attr.getId(), ATTR_NAME);
+        Assert.assertTrue(((IdPRequestedAttribute) attr).isRequired());
+        Assert.assertEquals(attr.getValues().size(), 1);
+        Assert.assertEquals(((DateTimeAttributeValue)attr.getValues().get(0)).getValue().toString(), STRING_ISO);
+    }
+    
+}
\ 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