[java-identity-provider] branch main updated: IDP-1992 - Add a DateTimeAttributeValue type
Scott Cantor
cantor.2 at osu.edu
Tue Aug 9 18:25:09 UTC 2022
This is an automated email from the git hooks/post-receive script.
scantor pushed a commit to branch main
in repository java-identity-provider.
View the commit online:
http://git.shibboleth.net/view/?p=java-identity-provider.git;a=commit;h=42252c612681837ba7cf9d58da8007394867556c
The following commit(s) were added to refs/heads/main by this push:
new 42252c612 IDP-1992 - Add a DateTimeAttributeValue type
42252c612 is described below
commit 42252c612681837ba7cf9d58da8007394867556c
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Tue Aug 9 14:25:06 2022 -0400
IDP-1992 - Add a DateTimeAttributeValue type
CAS transcoder.
---
.../impl/CASDateTimeAttributeTranscoder.java | 134 +++++++++++++++
.../impl/CASDateTimeAttributeTranscoderTest.java | 183 +++++++++++++++++++++
2 files changed, 317 insertions(+)
diff --git a/idp-cas-impl/src/main/java/net/shibboleth/idp/cas/attribute/transcoding/impl/CASDateTimeAttributeTranscoder.java b/idp-cas-impl/src/main/java/net/shibboleth/idp/cas/attribute/transcoding/impl/CASDateTimeAttributeTranscoder.java
new file mode 100644
index 000000000..dd8e38576
--- /dev/null
+++ b/idp-cas-impl/src/main/java/net/shibboleth/idp/cas/attribute/transcoding/impl/CASDateTimeAttributeTranscoder.java
@@ -0,0 +1,134 @@
+/*
+ * 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.cas.attribute.transcoding.impl;
+
+import java.time.Instant;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+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.cas.attribute.AbstractCASAttributeTranscoder;
+import net.shibboleth.idp.cas.attribute.Attribute;
+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.
+ */
+public class CASDateTimeAttributeTranscoder extends AbstractCASAttributeTranscoder<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 = "cas.epochUnits";
+
+ /** Class logger. */
+ @Nonnull private final Logger log = LoggerFactory.getLogger(CASDateTimeAttributeTranscoder.class);
+
+ /** {@inheritDoc} */
+ @Override protected boolean canEncodeValue(@Nonnull final IdPAttribute attribute,
+ @Nonnull final IdPAttributeValue value) {
+ return value instanceof DateTimeAttributeValue;
+ }
+
+ /** {@inheritDoc} */
+ @Override @Nullable protected String encodeValue(@Nullable final ProfileRequestContext profileRequestContext,
+ @Nonnull final IdPAttribute attribute, @Nonnull final TranscodingRule rule,
+ @Nonnull final DateTimeAttributeValue value) throws AttributeEncodingException {
+
+ return DOMTypeSupport.instantToString(value.getValue());
+ }
+
+ /** {@inheritDoc} */
+ @Override @Nullable protected IdPAttributeValue decodeValue(
+ @Nullable final ProfileRequestContext profileRequestContext, @Nonnull final Attribute attribute,
+ @Nonnull final TranscodingRule rule, @Nullable final String value) {
+
+ if (value != null) {
+ final Instant retVal = getDateTimeValue(rule, value);
+ if (retVal != null) {
+ return new DateTimeAttributeValue(retVal);
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * 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) {
+ if (value == null) {
+ return null;
+ }
+
+ try {
+ final Long longVal = Long.valueOf(value);
+ if (longVal != null) {
+ return getDateTimeValue(rule, longVal);
+ }
+ } catch (final NumberFormatException e) {
+ }
+
+ try {
+ return DOMTypeSupport.stringToInstant(value);
+ } catch (final IllegalArgumentException e) {
+ }
+
+ log.warn("{} rule unable to process string value as numeric or ISO format",
+ rule.getOrDefault(AttributeTranscoderRegistry.PROP_ID, String.class, "(none)"));
+ 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/idp-cas-impl/src/test/java/net/shibboleth/idp/cas/attribute/transcoding/impl/CASDateTimeAttributeTranscoderTest.java b/idp-cas-impl/src/test/java/net/shibboleth/idp/cas/attribute/transcoding/impl/CASDateTimeAttributeTranscoderTest.java
new file mode 100644
index 000000000..9813e287d
--- /dev/null
+++ b/idp-cas-impl/src/test/java/net/shibboleth/idp/cas/attribute/transcoding/impl/CASDateTimeAttributeTranscoderTest.java
@@ -0,0 +1,183 @@
+/*
+ * 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.cas.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 org.testng.Assert;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+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.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.cas.attribute.AbstractCASAttributeTranscoder;
+import net.shibboleth.idp.cas.attribute.Attribute;
+import net.shibboleth.idp.cas.attribute.CASAttributeTranscoder;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+
+/** {@link CASDateTimeAttributeTranscoder} unit test. */
+public class CASDateTimeAttributeTranscoderTest {
+
+ private AttributeTranscoderRegistryImpl registry;
+
+ private final static String ATTR_ID = "foo";
+ private final static String ATTR_NAME = "bar";
+ 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 {
+
+ registry = new AttributeTranscoderRegistryImpl();
+ registry.setId("test");
+
+ final CASDateTimeAttributeTranscoder transcoder = new CASDateTimeAttributeTranscoder();
+ transcoder.initialize();
+
+ registry.setNamingRegistry(Collections.singletonList(
+ new BasicNamingFunction<>(transcoder.getEncodedType(), new AbstractCASAttributeTranscoder.NamingFunction())));
+
+ final Map<String,Object> ruleset1 = new HashMap<>();
+ ruleset1.put(AttributeTranscoderRegistry.PROP_ID, ATTR_ID);
+ ruleset1.put(AttributeTranscoderRegistry.PROP_TRANSCODER, transcoder);
+ ruleset1.put(CASAttributeTranscoder.PROP_NAME, ATTR_NAME);
+
+ 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_ID);
+
+ 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.assertTrue(attr.getValues().isEmpty());
+ }
+
+ @Test public void emptyDecode() throws Exception {
+
+ final Attribute casAttribute = new Attribute(ATTR_NAME);
+
+ final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(casAttribute);
+ Assert.assertEquals(rulesets.size(), 1);
+ final TranscodingRule ruleset = rulesets.iterator().next();
+
+ final IdPAttribute attr = TranscoderSupport.getTranscoder(ruleset).decode(null, casAttribute, ruleset);
+
+ Assert.assertNotNull(attr);
+ Assert.assertEquals(attr.getId(), ATTR_ID);
+ 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_ID);
+ inputAttribute.setValues(values);
+
+ final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(inputAttribute, Attribute.class);
+ Assert.assertEquals(rulesets.size(), 1);
+ final TranscodingRule ruleset = rulesets.iterator().next();
+
+ TranscoderSupport.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_ID);
+ 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);
+
+ final Collection<String> children = attr.getValues();
+
+ Assert.assertEquals(children.size(), 1, "Encoding one entry");
+
+ final String child = children.iterator().next();
+
+ Assert.assertEquals(child, STRING_ISO);
+ }
+
+ @Test public void singleDecode() throws Exception {
+
+ final Attribute casAttribute = new Attribute(ATTR_NAME);
+ casAttribute.getValues().add(STRING_SECS);
+
+ final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(casAttribute);
+ Assert.assertEquals(rulesets.size(), 1);
+ final TranscodingRule ruleset = rulesets.iterator().next();
+
+ final IdPAttribute attr = TranscoderSupport.getTranscoder(ruleset).decode(null, casAttribute, ruleset);
+
+ Assert.assertNotNull(attr);
+ Assert.assertEquals(attr.getId(), ATTR_ID);
+ Assert.assertEquals(attr.getValues().size(), 1);
+ Assert.assertEquals(((DateTimeAttributeValue)attr.getValues().get(0)).getValue(),
+ Instant.ofEpochSecond(Long.valueOf(STRING_SECS)));
+ }
+
+}
\ 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