[java-identity-provider] branch master updated: IDP-1522 - Support for encrypted attributes in the resolver

Scott Cantor cantor.2 at osu.edu
Tue Jun 30 17:29:26 UTC 2020


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

scantor pushed a commit to branch master
in repository java-identity-provider.

View the commit online:
http://git.shibboleth.net/view/?p=java-identity-provider.git;a=commit;h=9c8e6d6bc1ad682abed92c95960a734399d92e08

The following commit(s) were added to refs/heads/master by this push:
       new  9c8e6d6bc IDP-1522 - Support for encrypted attributes in the resolver
9c8e6d6bc is described below

commit 9c8e6d6bc1ad682abed92c95960a734399d92e08
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Tue Jun 30 13:29:18 2020 -0400

    IDP-1522 - Support for encrypted attributes in the resolver
    
    https://issues.shibboleth.net/jira/browse/IDP-1522
---
 .../ad/impl/DecryptedAttributeDefinition.java      | 124 ++++++++++
 .../resolver/ad/impl/DecryptedAttributeTest.java   | 249 +++++++++++++++++++++
 .../attribute/resolver/impl/ad/SealerKeyStore.jks  | Bin 0 -> 984 bytes
 .../attribute/resolver/impl/ad/SealerKeyStore.kver |   1 +
 .../impl/DecryptedAttributeDefinitionParser.java   |  54 +++++
 .../impl/AttributeResolverNamespaceHandler.java    |   5 +-
 .../resolver/spring/AttributeResolverTest.java     |  42 +++-
 .../resolver/spring/attribute-resolver.xml         |  21 +-
 .../attribute/resolver/spring/externalBeans.xml    |  21 +-
 .../idp/attribute/resolver/spring/sealer.xml       |  18 --
 .../idp/attribute/resolver/spring/service.xml      |  12 +-
 .../attribute/resolver/spring/storageService.xml   |  11 +-
 .../net/shibboleth/idp/saml/impl/TestSources.java  |  69 ++++--
 .../schema/shibboleth-attribute-resolver.xsd       |  22 ++
 14 files changed, 587 insertions(+), 62 deletions(-)

diff --git a/idp-attribute-resolver-impl/src/main/java/net/shibboleth/idp/attribute/resolver/ad/impl/DecryptedAttributeDefinition.java b/idp-attribute-resolver-impl/src/main/java/net/shibboleth/idp/attribute/resolver/ad/impl/DecryptedAttributeDefinition.java
new file mode 100644
index 000000000..ff8023940
--- /dev/null
+++ b/idp-attribute-resolver-impl/src/main/java/net/shibboleth/idp/attribute/resolver/ad/impl/DecryptedAttributeDefinition.java
@@ -0,0 +1,124 @@
+/*
+ * 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.resolver.ad.impl;
+
+import javax.annotation.Nonnull;
+import javax.annotation.concurrent.ThreadSafe;
+import java.util.Collection;
+import java.util.List;
+import java.util.ArrayList;
+
+import net.shibboleth.idp.attribute.EmptyAttributeValue;
+import net.shibboleth.idp.attribute.IdPAttribute;
+import net.shibboleth.idp.attribute.StringAttributeValue;
+import net.shibboleth.idp.attribute.IdPAttributeValue;
+import net.shibboleth.idp.attribute.resolver.AbstractAttributeDefinition;
+import net.shibboleth.idp.attribute.resolver.AttributeDefinition;
+import net.shibboleth.idp.attribute.resolver.PluginDependencySupport;
+import net.shibboleth.idp.attribute.resolver.ResolutionException;
+import net.shibboleth.idp.attribute.resolver.context.AttributeResolutionContext;
+import net.shibboleth.idp.attribute.resolver.context.AttributeResolverWorkContext;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.utilities.java.support.component.ComponentSupport;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+import net.shibboleth.utilities.java.support.security.DataSealer;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * An {@link AttributeDefinition} that creates an attribute whose values are the
+ * values of all its dependencies.
+ */
+ at ThreadSafe
+public class DecryptedAttributeDefinition extends AbstractAttributeDefinition {
+
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(DecryptedAttributeDefinition.class);
+
+    /** The DataSealer that we'll use to decrypt the attribute. */
+    @NonnullAfterInit private DataSealer sealer;
+
+    /**
+     * Set the DataSealer (sealer) for this Definition.
+     * 
+     * @param newSealer what to set
+     */
+    public void setDataSealer(@Nonnull final DataSealer newSealer) {
+        ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+        sealer = Constraint.isNotNull(newSealer, "DataSealer cannot be null");
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void doInitialize() throws ComponentInitializationException {
+        super.doInitialize();
+    
+        if (getDataConnectorDependencies().isEmpty() && getAttributeDependencies().isEmpty()) {
+            throw new ComponentInitializationException(getLogPrefix() + " no dependencies were configured");
+        } else if (sealer == null) {
+            throw new ComponentInitializationException("DataSealer cannot be null");
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override @Nonnull protected IdPAttribute doAttributeDefinitionResolve(
+            @Nonnull final AttributeResolutionContext resolutionContext,
+            @Nonnull final AttributeResolverWorkContext workContext) throws ResolutionException {
+        Constraint.isNotNull(workContext, "AttributeResolverWorkContext cannot be null");
+
+        final List<IdPAttributeValue> results =
+                PluginDependencySupport.getMergedAttributeValues(workContext, getAttributeDependencies(), 
+                        getDataConnectorDependencies(), getId());
+
+        final Collection<IdPAttributeValue> decryptedValues = new ArrayList<>(results.size());
+
+        for (final IdPAttributeValue value : results) {
+            
+            if (value instanceof EmptyAttributeValue) {
+                log.trace("{} Passing through EmptyAttributeValue", getLogPrefix());
+                decryptedValues.add(value);
+                continue;
+            } else if (!(value instanceof StringAttributeValue)) {
+                log.warn("{} Ignoring non-string-valued IdPAttributeValue type {}", getLogPrefix(),
+                        value.getClass().getSimpleName());
+                continue;
+            }
+            
+            log.trace("{} Encrypted attribute value: {}", getLogPrefix(), ((StringAttributeValue) value).getValue());
+        
+            try{
+                final String decrypted = sealer.unwrap(((StringAttributeValue) value).getValue());
+                log.trace("{}: Decrypted attribute value: {}", getLogPrefix(), decrypted);
+                decryptedValues.add(new StringAttributeValue(decrypted));
+                
+            } catch(final Exception e){
+                log.warn("{}: Error decrypting attribute: {}", getLogPrefix(), e);
+            }
+        }
+    
+        final IdPAttribute decryptedAttribute = new IdPAttribute(getId());
+        decryptedAttribute.setValues(decryptedValues);
+
+        return decryptedAttribute;
+    }
+  
+}
\ No newline at end of file
diff --git a/idp-attribute-resolver-impl/src/test/java/net/shibboleth/idp/attribute/resolver/ad/impl/DecryptedAttributeTest.java b/idp-attribute-resolver-impl/src/test/java/net/shibboleth/idp/attribute/resolver/ad/impl/DecryptedAttributeTest.java
new file mode 100644
index 000000000..0bdb05419
--- /dev/null
+++ b/idp-attribute-resolver-impl/src/test/java/net/shibboleth/idp/attribute/resolver/ad/impl/DecryptedAttributeTest.java
@@ -0,0 +1,249 @@
+/*
+ * 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.resolver.ad.impl;
+
+import static org.testng.Assert.*;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+import org.springframework.core.io.ClassPathResource;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import net.shibboleth.idp.attribute.EmptyAttributeValue;
+import net.shibboleth.idp.attribute.EmptyAttributeValue.EmptyType;
+import net.shibboleth.idp.attribute.IdPAttribute;
+import net.shibboleth.idp.attribute.IdPAttributeValue;
+import net.shibboleth.idp.attribute.StringAttributeValue;
+import net.shibboleth.idp.attribute.resolver.AttributeDefinition;
+import net.shibboleth.idp.attribute.resolver.DataConnector;
+import net.shibboleth.idp.attribute.resolver.ResolutionException;
+import net.shibboleth.idp.attribute.resolver.ResolverAttributeDefinitionDependency;
+import net.shibboleth.idp.attribute.resolver.ResolverDataConnectorDependency;
+import net.shibboleth.idp.attribute.resolver.ResolverTestSupport;
+import net.shibboleth.idp.attribute.resolver.context.AttributeResolutionContext;
+import net.shibboleth.idp.attribute.resolver.context.AttributeResolverWorkContext;
+import net.shibboleth.idp.attribute.resolver.impl.AttributeResolverImpl;
+import net.shibboleth.idp.attribute.resolver.impl.AttributeResolverImplTest;
+import net.shibboleth.idp.saml.impl.TestSources;
+import net.shibboleth.utilities.java.support.collection.LazySet;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+import net.shibboleth.utilities.java.support.resource.Resource;
+import net.shibboleth.utilities.java.support.resource.TestResourceConverter;
+import net.shibboleth.utilities.java.support.security.DataSealer;
+import net.shibboleth.utilities.java.support.security.DataSealerException;
+import net.shibboleth.utilities.java.support.security.impl.BasicKeystoreKeyStrategy;
+
+/** Test for {@link DecryptedAttributeDefinition}. */
+public class DecryptedAttributeTest {
+
+    private static final String TEST_ATTRIBUTE_NAME = "decrypted";
+
+    private DataSealer dataSealer;
+
+    @BeforeClass public void setUp() throws ComponentInitializationException {
+        ClassPathResource resource =
+                new ClassPathResource("net/shibboleth/idp/attribute/resolver/impl/ad/SealerKeyStore.jks");
+        final Resource keystoreResource = TestResourceConverter.of(resource);
+
+        resource = new ClassPathResource("net/shibboleth/idp/attribute/resolver/impl/ad/SealerKeyStore.kver");
+        final Resource versionResource = TestResourceConverter.of(resource);
+
+        final BasicKeystoreKeyStrategy strategy = new BasicKeystoreKeyStrategy();
+        
+        strategy.setKeyAlias("secret");
+        strategy.setKeyPassword("kpassword");
+
+        strategy.setKeystorePassword("password");
+        strategy.setKeystoreResource(keystoreResource);
+        
+        strategy.setKeyVersionResource(versionResource);
+
+        strategy.initialize();
+        
+        dataSealer = new DataSealer();
+        dataSealer.setKeyStrategy(strategy);
+        dataSealer.initialize();
+    }
+    
+    /**
+     * Test resolution of an empty definition to nothing.
+     * 
+     * @throws ResolutionException if resolution failed.
+     * @throws ComponentInitializationException if initialization fails (which it shouldn't).
+     */
+    @Test public void empty() throws ResolutionException, ComponentInitializationException {
+        final DecryptedAttributeDefinition decrypted = new DecryptedAttributeDefinition();
+        decrypted.setId(TEST_ATTRIBUTE_NAME);
+        try {
+            decrypted.initialize();
+            fail("no dependencies");
+        } catch (final ComponentInitializationException e) {
+            
+        }
+        decrypted.setDataConnectorDependencies(Collections.singleton(TestSources.makeDataConnectorDependency("foo", "bar")));
+
+        try {
+            decrypted.initialize();
+            fail("no DataSealer");
+        } catch (final ComponentInitializationException e) {
+            
+        }
+        
+        decrypted.setDataSealer(dataSealer);
+        decrypted.initialize();
+
+        final AttributeResolutionContext context = new AttributeResolutionContext();
+        context.getSubcontext(AttributeResolverWorkContext.class, true);
+        final IdPAttribute result = decrypted.resolve(context);
+
+        assertTrue(result.getValues().isEmpty());
+    }
+
+    /**
+     * Test when dependent on a data connector.
+     * 
+     * @throws ComponentInitializationException if initialization fails (which it shouldn't).
+     * @throws DataSealerException 
+     */
+    @Test public void dataConnector() throws ComponentInitializationException, DataSealerException {
+
+        // Set the dependency on the data connector
+        final DecryptedAttributeDefinition decrypted = new DecryptedAttributeDefinition();
+        decrypted.setId(TEST_ATTRIBUTE_NAME);
+
+        final Set<ResolverDataConnectorDependency> dependencySet = new LazySet<>();
+        dependencySet.add(TestSources.makeDataConnectorDependency(TestSources.STATIC_CONNECTOR_NAME,
+                TestSources.DEPENDS_ON_ATTRIBUTE_NAME_CONNECTOR));
+        decrypted.setDataConnectorDependencies(dependencySet);
+        decrypted.setDataSealer(dataSealer);
+        decrypted.initialize();
+
+        // Generate encrypted data.
+        final IdPAttribute attr1 = new IdPAttribute(TestSources.DEPENDS_ON_ATTRIBUTE_NAME_CONNECTOR);
+        attr1.setValues(List.of(new StringAttributeValue(dataSealer.wrap(TestSources.COMMON_ATTRIBUTE_VALUE_STRING)),
+                new StringAttributeValue(dataSealer.wrap(TestSources.CONNECTOR_ATTRIBUTE_VALUE_STRING))));
+
+        final IdPAttribute attr2 = new IdPAttribute(TestSources.DEPENDS_ON_SECOND_ATTRIBUTE_NAME);
+        attr2.setValues(List.of(new StringAttributeValue(dataSealer.wrap(TestSources.SECOND_ATTRIBUTE_VALUE_STRINGS[0])),
+                new StringAttributeValue(dataSealer.wrap(TestSources.SECOND_ATTRIBUTE_VALUE_STRINGS[1]))));
+        
+        // And resolve
+        final Set<DataConnector> connectorSet = new LazySet<>();
+        connectorSet.add(TestSources.populatedStaticConnector(List.of(attr1,attr2)));
+
+        final Set<AttributeDefinition> attributeSet = new LazySet<>();
+        attributeSet.add(decrypted);
+
+        final AttributeResolverImpl resolver = AttributeResolverImplTest.newAttributeResolverImpl("foo", attributeSet, connectorSet);
+        resolver.initialize();
+
+        final AttributeResolutionContext context = new AttributeResolutionContext();
+        try {
+            resolver.resolveAttributes(context);
+        } catch (final ResolutionException e) {
+            fail("resolution failed", e);
+        }
+
+        final Collection<?> values = context.getResolvedIdPAttributes().get(TEST_ATTRIBUTE_NAME).getValues();
+        assertEquals(values.size(), 2);
+        assertTrue(values.contains(TestSources.COMMON_ATTRIBUTE_VALUE_RESULT), "looking for " + TestSources.COMMON_ATTRIBUTE_VALUE_STRING);
+        assertTrue(values.contains(TestSources.CONNECTOR_ATTRIBUTE_VALUE_RESULT),
+                "looking for " + TestSources.CONNECTOR_ATTRIBUTE_VALUE_STRING);
+    }
+
+    /**
+     * Test when dependent on another attribute.
+     * 
+     * @throws ComponentInitializationException if initialization fails (which it shouldn't).
+     * @throws DataSealerException 
+     */
+    @Test public void attribute() throws ComponentInitializationException, DataSealerException {
+
+        final DecryptedAttributeDefinition decrypted = new DecryptedAttributeDefinition();
+        decrypted.setId(TEST_ATTRIBUTE_NAME);
+
+        final Set<ResolverAttributeDefinitionDependency> dependencySet = new LazySet<>();
+        dependencySet.add(TestSources.makeAttributeDefinitionDependency(TestSources.DEPENDS_ON_ATTRIBUTE_NAME_ATTR));
+        decrypted.setAttributeDependencies(dependencySet);
+        decrypted.setDataSealer(dataSealer);
+        decrypted.initialize();
+
+        final Set<AttributeDefinition> am = new LazySet<>();
+        am.add(decrypted);
+        
+        final IdPAttribute dependency = new IdPAttribute(TestSources.DEPENDS_ON_ATTRIBUTE_NAME_ATTR);
+        dependency.setValues(List.of(new StringAttributeValue(dataSealer.wrap(TestSources.COMMON_ATTRIBUTE_VALUE_STRING)),
+                new StringAttributeValue(dataSealer.wrap(TestSources.ATTRIBUTE_ATTRIBUTE_VALUE_STRING))));
+        am.add(TestSources.populatedStaticAttribute(dependency));
+
+        final AttributeResolverImpl resolver = AttributeResolverImplTest.newAttributeResolverImpl("foo", am, null);
+        resolver.initialize();
+
+        final AttributeResolutionContext context = new AttributeResolutionContext();
+        try {
+            resolver.resolveAttributes(context);
+        } catch (final ResolutionException e) {
+            fail("resolution failed", e);
+        }
+        final Collection<IdPAttributeValue> values = context.getResolvedIdPAttributes().get(TEST_ATTRIBUTE_NAME).getValues();
+
+        assertEquals(values.size(), 2);
+        assertTrue(values.contains(TestSources.COMMON_ATTRIBUTE_VALUE_RESULT),
+                "looking for value " + TestSources.COMMON_ATTRIBUTE_VALUE_STRING);
+        assertTrue(values.contains(TestSources.ATTRIBUTE_ATTRIBUTE_VALUE_RESULT),
+                "looking for value " + TestSources.ATTRIBUTE_ATTRIBUTE_VALUE_STRING);
+    }
+    
+    /**
+     * Test resolution of an empty definition to nothing.
+     * 
+     * @throws ResolutionException if resolution failed.
+     * @throws ComponentInitializationException if initialization fails (which it shouldn't).
+     */
+    @Test public void nullValue() throws ResolutionException, ComponentInitializationException {
+        final List<IdPAttributeValue> values = new ArrayList<>(3);
+        values.add(TestSources.COMMON_ATTRIBUTE_VALUE_RESULT);
+        values.add(new EmptyAttributeValue(EmptyType.NULL_VALUE));
+        final IdPAttribute attr = new IdPAttribute(TestSources.DEPENDS_ON_ATTRIBUTE_NAME_ATTR);
+
+        attr.setValues(values);
+
+        final AttributeResolutionContext resolutionContext =
+                ResolverTestSupport.buildResolutionContext(ResolverTestSupport.buildDataConnector("connector1", attr));
+        final ResolverDataConnectorDependency depend = TestSources.makeDataConnectorDependency("connector1", TestSources.DEPENDS_ON_ATTRIBUTE_NAME_ATTR);
+
+        final DecryptedAttributeDefinition decrypted = new DecryptedAttributeDefinition();
+        decrypted.setId(TEST_ATTRIBUTE_NAME);
+        decrypted.setDataConnectorDependencies(Collections.singleton(depend));
+        decrypted.setDataSealer(dataSealer);
+        decrypted.initialize();
+
+        final IdPAttribute result = decrypted.resolve(resolutionContext);
+
+        final List<IdPAttributeValue> outValues = result.getValues();
+        assertEquals(outValues.size(), 1);
+        assertFalse(outValues.contains(TestSources.COMMON_ATTRIBUTE_VALUE_RESULT));
+        assertTrue(outValues.contains(new EmptyAttributeValue(EmptyType.NULL_VALUE)));
+    }
+    
+}
\ No newline at end of file
diff --git a/idp-attribute-resolver-impl/src/test/resources/net/shibboleth/idp/attribute/resolver/impl/ad/SealerKeyStore.jks b/idp-attribute-resolver-impl/src/test/resources/net/shibboleth/idp/attribute/resolver/impl/ad/SealerKeyStore.jks
new file mode 100644
index 000000000..147d92bb0
Binary files /dev/null and b/idp-attribute-resolver-impl/src/test/resources/net/shibboleth/idp/attribute/resolver/impl/ad/SealerKeyStore.jks differ
diff --git a/idp-attribute-resolver-impl/src/test/resources/net/shibboleth/idp/attribute/resolver/impl/ad/SealerKeyStore.kver b/idp-attribute-resolver-impl/src/test/resources/net/shibboleth/idp/attribute/resolver/impl/ad/SealerKeyStore.kver
new file mode 100644
index 000000000..2cd48df38
--- /dev/null
+++ b/idp-attribute-resolver-impl/src/test/resources/net/shibboleth/idp/attribute/resolver/impl/ad/SealerKeyStore.kver
@@ -0,0 +1 @@
+CurrentVersion = 1
diff --git a/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/ad/impl/DecryptedAttributeDefinitionParser.java b/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/ad/impl/DecryptedAttributeDefinitionParser.java
new file mode 100644
index 000000000..cbdec83ac
--- /dev/null
+++ b/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/ad/impl/DecryptedAttributeDefinitionParser.java
@@ -0,0 +1,54 @@
+/*
+ * 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.resolver.spring.ad.impl;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.xml.namespace.QName;
+
+import org.springframework.beans.factory.support.BeanDefinitionBuilder;
+import org.springframework.beans.factory.xml.ParserContext;
+import org.w3c.dom.Element;
+
+import net.shibboleth.idp.attribute.resolver.ad.impl.DecryptedAttributeDefinition;
+import net.shibboleth.idp.attribute.resolver.spring.ad.BaseAttributeDefinitionParser;
+import net.shibboleth.idp.attribute.resolver.spring.impl.AttributeResolverNamespaceHandler;
+import net.shibboleth.utilities.java.support.primitive.StringSupport;
+
+/** Bean definition parser for a {@link DecryptedAttributeDefinition}. */
+public class DecryptedAttributeDefinitionParser extends BaseAttributeDefinitionParser {
+
+    /** Schema type name. */
+    @Nonnull public static final QName TYPE_NAME_RESOLVER =
+            new QName(AttributeResolverNamespaceHandler.NAMESPACE, "Decrypted");
+
+    /** {@inheritDoc} */
+    @Override protected Class<DecryptedAttributeDefinition> getBeanClass(@Nullable final Element element) {
+        return DecryptedAttributeDefinition.class;
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void doParse(@Nonnull final Element config, @Nonnull final ParserContext parserContext,
+            @Nonnull final BeanDefinitionBuilder builder) {
+        super.doParse(config, parserContext, builder);
+    
+        builder.addPropertyReference("dataSealer",
+                StringSupport.trimOrNull(config.getAttributeNS(null, "dataSealerRef")));
+    }
+
+}
\ No newline at end of file
diff --git a/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/impl/AttributeResolverNamespaceHandler.java b/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/impl/AttributeResolverNamespaceHandler.java
index a5eeeb7d4..4991dbc1e 100644
--- a/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/impl/AttributeResolverNamespaceHandler.java
+++ b/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/impl/AttributeResolverNamespaceHandler.java
@@ -31,6 +31,7 @@ import net.shibboleth.idp.attribute.resolver.spring.ad.impl.SAML2NameIDAttribute
 import net.shibboleth.idp.attribute.resolver.spring.ad.impl.ScopedAttributeDefinitionParser;
 import net.shibboleth.idp.attribute.resolver.spring.ad.impl.ScriptedAttributeDefinitionParser;
 import net.shibboleth.idp.attribute.resolver.spring.ad.impl.SimpleAttributeDefinitionParser;
+import net.shibboleth.idp.attribute.resolver.spring.ad.impl.DecryptedAttributeDefinitionParser;
 import net.shibboleth.idp.attribute.resolver.spring.ad.impl.SubjectDerivedAttributeDefinitionParser;
 import net.shibboleth.idp.attribute.resolver.spring.ad.impl.TemplateAttributeDefinitionParser;
 import net.shibboleth.idp.attribute.resolver.spring.ad.mapped.impl.MappedAttributeDefinitionParser;
@@ -93,6 +94,8 @@ public class AttributeResolverNamespaceHandler extends BaseSpringNamespaceHandle
                 new ScriptedAttributeDefinitionParser());
         registerBeanDefinitionParser(SimpleAttributeDefinitionParser.TYPE_NAME_RESOLVER,
                 new SimpleAttributeDefinitionParser());
+        registerBeanDefinitionParser(DecryptedAttributeDefinitionParser.TYPE_NAME_RESOLVER,
+                new DecryptedAttributeDefinitionParser());        
         registerBeanDefinitionParser(TemplateAttributeDefinitionParser.TYPE_NAME_RESOLVER,
                 new TemplateAttributeDefinitionParser());
         registerBeanDefinitionParser(SourceValueParser.TYPE_NAME_RESOLVER, new SourceValueParser());
@@ -140,4 +143,4 @@ public class AttributeResolverNamespaceHandler extends BaseSpringNamespaceHandle
     }
     // Checkstyle: MethodLength ON
 
-}
\ No newline at end of file
+}
diff --git a/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/AttributeResolverTest.java b/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/AttributeResolverTest.java
index 60a22e47e..87243f406 100644
--- a/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/AttributeResolverTest.java
+++ b/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/AttributeResolverTest.java
@@ -25,6 +25,7 @@ import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.function.Function;
 import java.util.function.Predicate;
 
@@ -53,6 +54,7 @@ import com.unboundid.ldap.sdk.LDAPException;
 
 import net.shibboleth.ext.spring.config.IdentifiableBeanPostProcessor;
 import net.shibboleth.ext.spring.config.StringToDurationConverter;
+import net.shibboleth.ext.spring.config.StringToResourceConverter;
 import net.shibboleth.ext.spring.util.SchemaTypeAwareXMLBeanDefinitionReader;
 import net.shibboleth.idp.attribute.IdPAttribute;
 import net.shibboleth.idp.attribute.IdPAttributeValue;
@@ -66,6 +68,8 @@ import net.shibboleth.idp.saml.impl.TestSources;
 import net.shibboleth.idp.testing.DatabaseTestingSupport;
 import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
 import net.shibboleth.utilities.java.support.logic.Constraint;
+import net.shibboleth.utilities.java.support.security.DataSealer;
+import net.shibboleth.utilities.java.support.security.DataSealerException;
 import net.shibboleth.utilities.java.support.service.ReloadableService;
 import net.shibboleth.utilities.java.support.service.ServiceException;
 import net.shibboleth.utilities.java.support.service.ServiceableComponent;
@@ -160,6 +164,37 @@ public class AttributeResolverTest extends OpenSAMLInitBaseTestCase {
         helper(false);
     }
 
+    /**
+     * Not actually a test, just a convenience method for encrypting a value to put into the test data.
+     * @throws DataSealerException 
+     * @throws ComponentInitializationException 
+     */
+    @Test(enabled=false)
+    private void dumpEncryptedString() throws DataSealerException {
+        final String inputFile ="net/shibboleth/idp/attribute/resolver/spring/externalBeans.xml";
+
+        final GenericApplicationContext context = new GenericApplicationContext();
+        context.getBeanFactory().addBeanPostProcessor(new IdentifiableBeanPostProcessor());
+        setTestContext(context);
+        context.setDisplayName("ApplicationContext: " + AttributeResolverTest.class);
+
+        final ConversionServiceFactoryBean service = new ConversionServiceFactoryBean();
+        context.setDisplayName("ApplicationContext: ");
+        service.setConverters(Set.of(new StringToDurationConverter(), new StringToResourceConverter()));
+        service.afterPropertiesSet();
+
+        context.getBeanFactory().setConversionService(service.getObject());
+        
+        final SchemaTypeAwareXMLBeanDefinitionReader beanDefinitionReader =
+                new SchemaTypeAwareXMLBeanDefinitionReader(context);
+
+        beanDefinitionReader.loadBeanDefinitions(inputFile);
+        context.refresh();
+
+        final DataSealer sealer = context.getBean("encryptedAttribute.DataSealer", DataSealer.class);
+        log.info("Encrypted string is {}", sealer.wrap("Hello World"));
+    }
+    
     private void helper(final boolean stripNulls) throws ComponentInitializationException, ServiceException, ResolutionException {
 
         final String inputFile;
@@ -195,7 +230,7 @@ public class AttributeResolverTest extends OpenSAMLInitBaseTestCase {
         final Map<String, IdPAttribute> resolvedAttributes = resolutionContext.getResolvedIdPAttributes();
         log.debug("resolved attributes '{}'", resolvedAttributes);
 
-        assertEquals(resolvedAttributes.size(), 14);
+        assertEquals(resolvedAttributes.size(), 15);
 
         // Static
         IdPAttribute attribute = resolvedAttributes.get("eduPersonAffiliation");
@@ -204,6 +239,11 @@ public class AttributeResolverTest extends OpenSAMLInitBaseTestCase {
         
         assertEquals(values.size(), expectedEPAValues);
         assertTrue(values.contains(new StringAttributeValue("member")));
+
+        attribute = resolvedAttributes.get("decryptedOne");
+        assertNotNull(attribute);
+        values = attribute.getValues();
+        assertTrue(values.contains(new StringAttributeValue("Hello World")));
         
         // Broken (case 665)
         attribute =  resolvedAttributes.get("broken");
diff --git a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/attribute-resolver.xml b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/attribute-resolver.xml
index d4d774fdd..fba76d169 100644
--- a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/attribute-resolver.xml
+++ b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/attribute-resolver.xml
@@ -1,4 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
+
 <AttributeResolver xmlns="urn:mace:shibboleth:2.0:resolver" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                    xsi:schemaLocation="urn:mace:shibboleth:2.0:resolver http://shibboleth.net/schema/idp/shibboleth-attribute-resolver.xsd">
 
@@ -29,6 +30,11 @@
         <InputAttributeDefinition ref="eduPersonAffiliation" />
     </AttributeDefinition>
 
+    <AttributeDefinition xsi:type="Decrypted" id="decryptedOne" dataSealerRef="encryptedAttribute.DataSealer">
+    <InputDataConnector ref="staticEncrypted" attributeNames="encryptedOne"/>
+    </AttributeDefinition>
+
+
     <!-- Schema: Core schema attributes-->
     
     <AttributeDefinition xsi:type="Simple" id="uid">
@@ -202,14 +208,21 @@
     <!--      Data Connectors                       -->
     <!-- ========================================== -->
 
+    <DataConnector id="staticEncrypted" xsi:type="Static">
+        <Attribute id="encryptedOne">
+        <!-- plaintext: Hello World -->
+            <Value>AAdzZWNyZXQxfrZRV23BOWmQMLc4mNEbYRxwgcY+z2Y8gm+0duysPsTsMJwa66gqhwM18uFw/PsGtcBYClwLmfs8fLIFP0lNAG7jpQ==</Value>
+        </Attribute>
+    </DataConnector>
+
     <!-- Example Relational Database Connector -->
 
     <DataConnector id="myDB" xsi:type="RelationalDatabase">
         <SimpleManagedConnection
-	        jdbcDriver="org.hsqldb.jdbc.JDBCDriver"
-	        jdbcURL="jdbc:hsqldb:mem:myTestDB"
-	        jdbcUserName="SA"
-	        jdbcPassword="" />
+            jdbcDriver="org.hsqldb.jdbc.JDBCDriver"
+            jdbcURL="jdbc:hsqldb:mem:myTestDB"
+            jdbcUserName="SA"
+            jdbcPassword="" />
         <QueryTemplate>
             <![CDATA[ 
                 SELECT * FROM student WHERE userid = '$resolutionContext.principal'
diff --git a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/externalBeans.xml b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/externalBeans.xml
index 3c9ddfe18..90240899d 100644
--- a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/externalBeans.xml
+++ b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/externalBeans.xml
@@ -6,7 +6,10 @@
         xmlns:context="http://www.springframework.org/schema/context"
         xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                             http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
-                            http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
+                            http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
+                            
+        default-init-method="initialize"
+        default-destroy-method="destroy">
     
     <bean id="shibboleth.VelocityEngine" class="net.shibboleth.ext.spring.velocity.VelocityEngineFactoryBean">
         <property name="velocityProperties">
@@ -21,7 +24,19 @@
             </props>
         </property>
     </bean>
-    
+   
+     <bean id="encryptedAttribute.DataSealer" class="net.shibboleth.utilities.java.support.security.DataSealer">
+        <property name="keyStrategy">
+            <bean class="net.shibboleth.utilities.java.support.security.impl.BasicKeystoreKeyStrategy"
+                p:keyAlias="secret"
+                p:keystoreResource="classpath:/net/shibboleth/idp/attribute/resolver/spring/ad/SealerKeyStore.jks"
+                p:keyVersionResource="classpath:/net/shibboleth/idp/attribute/resolver/spring/ad/SealerKeyStore.kver"
+                p:keystorePassword="password"
+                p:keyPassword="kpassword"
+                p:updateInterval="PT0S" />
+        </property>
+    </bean>
+   
     <bean id="shibboleth.PropertySourcesPlaceholderConfigurer"
         class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"
         p:placeholderPrefix="%{" p:placeholderSuffix="}" />
@@ -37,4 +52,4 @@
                 c:path="net/shibboleth/idp/attribute/resolver/spring/dc/rdbms/rdbms-attribute-resolver-spring-props-context.xml"/>
     </util:list>
     
-</beans>
\ No newline at end of file
+</beans>
diff --git a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/sealer.xml b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/sealer.xml
deleted file mode 100644
index 893f1a7d0..000000000
--- a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/sealer.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
-       xmlns:util="http://www.springframework.org/schema/util"
-       xmlns:p="http://www.springframework.org/schema/p"
-       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
-                           http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
-
-    <bean id="shibboleth.TransientIDDataSealer" class="net.shibboleth.utilities.java.support.security.DataSealer">
-        <property name="keyStrategy">
-            <bean class="net.shibboleth.utilities.java.support.security.impl.BasicKeystoreKeyStrategy"
-                p:keystoreResource="/net/shibboleth/idp/attribute/resolver/spring/ad/SealerKeyStore.jks"
-                p:keyVersionResource="/net/shibboleth/idp/attribute/resolver/spring/ad/SealerKeyStore.kver"
-                p:keystorePassword="kpassword"
-                p:keyPassword="password"
-                p:updateInterval="PT0S" />
-        </property>
-    </bean>
-</beans>
\ No newline at end of file
diff --git a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/service.xml b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/service.xml
index 0a5ed25d4..2c594187b 100644
--- a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/service.xml
+++ b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/service.xml
@@ -6,13 +6,13 @@
         xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                             http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
         default-init-method="initialize"
-        default-destroy-method="destroy"
-                            >
+        default-destroy-method="destroy">
                             
     <!-- This BeanPostProcessor auto-sets identifiable beans with the bean name (if not already set). -->
     <bean id="shibboleth.IdentifiableBeanPostProcessor"
         class="net.shibboleth.ext.spring.config.IdentifiableBeanPostProcessor" />     
-                            
+ 
+                           
     <bean id="shibboleth.VelocityEngine" class="net.shibboleth.ext.spring.velocity.VelocityEngineFactoryBean">
         <property name="velocityProperties">
             <props>
@@ -26,7 +26,7 @@
             </props>
         </property>
     </bean>
-        <bean id="shibboleth.AttributeResolverService" class="net.shibboleth.ext.spring.service.ReloadableSpringService"
+    <bean id="shibboleth.AttributeResolverService" class="net.shibboleth.ext.spring.service.ReloadableSpringService"
         depends-on="shibboleth.VelocityEngine"
         p:beanPostProcessors-ref="shibboleth.IdentifiableBeanPostProcessor"
         p:failFast="false" p:reloadCheckDelay="0">
@@ -40,11 +40,11 @@
         </constructor-arg>
         <property name="serviceConfigurations">
             <util:list>
-            	<value>net/shibboleth/idp/attribute/resolver/spring/externalBeans.xml</value>
+                <value>net/shibboleth/idp/attribute/resolver/spring/externalBeans.xml</value>
                 <value>net/shibboleth/idp/attribute/resolver/spring/storageService.xml</value>
                 <value>net/shibboleth/idp/attribute/resolver/spring/attribute-resolver.xml</value>
                 <value>net/shibboleth/idp/attribute/resolver/spring/dc/staticAttributesNative.xml</value>
             </util:list>
         </property>
     </bean>
-</beans>
\ No newline at end of file
+</beans>
diff --git a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/storageService.xml b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/storageService.xml
index eec8099dc..e2fd196f5 100644
--- a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/storageService.xml
+++ b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/storageService.xml
@@ -1,8 +1,8 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:p="http://www.springframework.org/schema/p"
-	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"
-	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"
+    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                             http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
                             
        default-init-method="initialize"
@@ -14,9 +14,10 @@
             <list>
                 <bean class="net.shibboleth.ext.spring.config.StringToIPRangeConverter"/>
                 <bean class="net.shibboleth.ext.spring.config.StringToDurationConverter"/>
+        <bean class="net.shibboleth.ext.spring.config.StringToResourceConverter" />
             </list>
         </property>
     </bean>
-	<bean id="shibboleth.StorageService" p:id="test" class="org.opensaml.storage.impl.MemoryStorageService"
-		p:cleanupInterval="PT10M" />
-</beans>
\ No newline at end of file
+    <bean id="shibboleth.StorageService" p:id="test" class="org.opensaml.storage.impl.MemoryStorageService"
+        p:cleanupInterval="PT10M" />
+</beans>
diff --git a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/impl/TestSources.java b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/impl/TestSources.java
index 56785e4e7..21dd91881 100644
--- a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/impl/TestSources.java
+++ b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/impl/TestSources.java
@@ -18,13 +18,11 @@
 package net.shibboleth.idp.saml.impl;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 import java.util.regex.Pattern;
 
 import javax.annotation.Nonnull;
@@ -47,8 +45,8 @@ import net.shibboleth.idp.attribute.resolver.context.AttributeResolverWorkContex
 import net.shibboleth.idp.saml.attribute.resolver.impl.SAML2NameIDAttributeDefinition;
 import net.shibboleth.idp.testing.DatabaseTestingSupport;
 import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
 import net.shibboleth.utilities.java.support.annotation.constraint.NullableElements;
-import net.shibboleth.utilities.java.support.collection.LazySet;
 import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
 import net.shibboleth.utilities.java.support.component.ComponentSupport;
 
@@ -120,6 +118,24 @@ public final class TestSources {
     private TestSources() {
     }
 
+    /**
+     * Create a static connector with provided attributes and values.
+     * 
+     * @param attributes the objects to populate it with
+     * @return The connector
+     * @throws ComponentInitializationException if we cannot initialized (unlikely)
+     */
+    public static DataConnector populatedStaticConnector(@Nonnull @NonnullElements final List<IdPAttribute> attributes)
+            throws ComponentInitializationException {
+
+        final StaticDataConnector connector = new StaticDataConnector();
+        connector.setId(STATIC_CONNECTOR_NAME);
+        connector.setValues(attributes);
+        connector.initialize();
+
+        return connector;
+    }
+
     /**
      * Create a static connector with known attributes and values.
      * 
@@ -127,27 +143,19 @@ public final class TestSources {
      * @throws ComponentInitializationException if we cannot initialized (unlikely)
      */
     public static DataConnector populatedStaticConnector() throws ComponentInitializationException {
-        IdPAttribute attr;
-        Set<IdPAttribute> attributeSet;
-
-        attributeSet = new LazySet<>();
+        List<IdPAttribute> attributeSet = new ArrayList<>(2);
 
-        attr = new IdPAttribute(DEPENDS_ON_ATTRIBUTE_NAME_CONNECTOR);
-        attr.setValues(Arrays.asList(new StringAttributeValue(COMMON_ATTRIBUTE_VALUE_STRING),
+        IdPAttribute attr = new IdPAttribute(DEPENDS_ON_ATTRIBUTE_NAME_CONNECTOR);
+        attr.setValues(List.of(new StringAttributeValue(COMMON_ATTRIBUTE_VALUE_STRING),
                 new StringAttributeValue(CONNECTOR_ATTRIBUTE_VALUE_STRING)));
         attributeSet.add(attr);
 
         attr = new IdPAttribute(DEPENDS_ON_SECOND_ATTRIBUTE_NAME);
-        attr.setValues(Arrays.asList(new StringAttributeValue(SECOND_ATTRIBUTE_VALUE_STRINGS[0]),
+        attr.setValues(List.of(new StringAttributeValue(SECOND_ATTRIBUTE_VALUE_STRINGS[0]),
                 new StringAttributeValue(SECOND_ATTRIBUTE_VALUE_STRINGS[1])));
         attributeSet.add(attr);
-
-        StaticDataConnector connector = new StaticDataConnector();
-        connector.setId(STATIC_CONNECTOR_NAME);
-        connector.setValues(attributeSet);
-        connector.initialize();
-
-        return connector;
+        
+        return populatedStaticConnector(attributeSet);
     }
 
     /**
@@ -159,11 +167,11 @@ public final class TestSources {
     public static AttributeDefinition populatedStaticAttribute() throws ComponentInitializationException {
         return populatedStaticAttribute(DEPENDS_ON_ATTRIBUTE_NAME_ATTR, 2);
     }
-
+    
     public static AttributeDefinition populatedStaticAttribute(String attributeName,
             int attributeValuesCount) throws ComponentInitializationException {
-        IdPAttribute attr;
-        List<IdPAttributeValue> valuesList = new ArrayList<>();
+        
+        final List<IdPAttributeValue> valuesList = new ArrayList<>();
 
         if (attributeValuesCount > 0) {
             valuesList.add(new StringAttributeValue(COMMON_ATTRIBUTE_VALUE_STRING));
@@ -174,12 +182,25 @@ public final class TestSources {
         for (int i = 2; i < attributeValuesCount; i++) {
             valuesList.add(new StringAttributeValue(ATTRIBUTE_ATTRIBUTE_VALUE_STRING + i));
         }
-        attr = new IdPAttribute(attributeName);
+        final IdPAttribute attr = new IdPAttribute(attributeName);
         attr.setValues(valuesList);
 
-        StaticAttributeDefinition definition = new StaticAttributeDefinition();
-        definition.setId(attributeName);
-        definition.setValue(attr);
+        return populatedStaticAttribute(attr);
+    }
+
+    /**
+     * Create a static attribute with known attribute.
+     * 
+     * @param attribute the input attribute
+     * @return the attribute definition
+     * @throws ComponentInitializationException if we cannot initialized (unlikely)
+     */
+    public static AttributeDefinition populatedStaticAttribute(@Nonnull final IdPAttribute attribute)
+            throws ComponentInitializationException {
+        
+        final StaticAttributeDefinition definition = new StaticAttributeDefinition();
+        definition.setId(attribute.getId());
+        definition.setValue(attribute);
         definition.initialize();
         return definition;
     }
diff --git a/idp-schema/src/main/resources/schema/shibboleth-attribute-resolver.xsd b/idp-schema/src/main/resources/schema/shibboleth-attribute-resolver.xsd
index 1bec5e739..769ac5df4 100644
--- a/idp-schema/src/main/resources/schema/shibboleth-attribute-resolver.xsd
+++ b/idp-schema/src/main/resources/schema/shibboleth-attribute-resolver.xsd
@@ -638,6 +638,28 @@
             </extension>
         </complexContent>
     </complexType>
+    
+    <complexType name="Decrypted">
+        <annotation>
+            <documentation>An attribute definition involving an encrypted (DataSealed) attribute</documentation>
+        </annotation>
+        <complexContent>
+            <extension base="resolver:BaseAttributeDefinitionType">
+                <choice maxOccurs="unbounded" minOccurs="0">
+                    <element ref="resolver:InputAttributeDefinition"/>
+                    <element ref="resolver:InputDataConnector"/>
+                    <element name="DisplayName" type="resolver:LocalizedStringType"/>
+                    <element name="DisplayDescription" type="resolver:LocalizedStringType"/>
+                    <element ref="resolver:AttributeEncoder"/>
+                </choice>
+                <attribute name="dataSealerRef" type="resolver:string">
+                    <annotation>
+                        <documentation>Value to use for the decryption key</documentation>
+                    </annotation>
+                </attribute>
+            </extension>
+        </complexContent>
+    </complexType>
 
     <complexType name="SubjectDerivedAttribute">
         <annotation>

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the commits mailing list