[java-identity-provider] branch master updated: IDP-1170 - REST DataConnector

Scott Cantor cantor.2 at osu.edu
Tue Jun 13 15:46:34 EDT 2017


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=4e4df06fcd1abbdd2aa66e0b74cb6d4a7de166ee

The following commit(s) were added to refs/heads/master by this push:
       new  4e4df06   IDP-1170 - REST DataConnector
4e4df06 is described below

commit 4e4df06fcd1abbdd2aa66e0b74cb6d4a7de166ee
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Tue Jun 13 15:46:04 2017 -0400

    IDP-1170 - REST DataConnector
    
    https://issues.shibboleth.net/jira/browse/IDP-1170
    
    Spring parser and schema for configuration.
---
 .../dc/http/impl/HTTPDataConnectorParser.java      | 280 +++++++++++++++++++++
 .../resolver/spring/dc/http/impl/package-info.java |  22 ++
 .../impl/AttributeResolverNamespaceHandler.java    |   2 +
 .../dc/http/HTTPDataConnectorParserTest.java       | 239 ++++++++++++++++++
 .../http-attribute-resolver-spring-context.xml     |  25 ++
 .../http-attribute-resolver-v2-badprotocol.xml     |  18 ++
 .../dc/http/http-attribute-resolver-v2-hybrid.xml  |  15 ++
 .../http/http-attribute-resolver-v2-missingok.xml  |  20 ++
 .../spring/dc/http/http-attribute-resolver-v2.xml  |  21 ++
 .../resolver/spring/dc/http/spring-beans.xml       |  33 +++
 .../schema/shibboleth-attribute-resolver.xsd       | 102 ++++++++
 11 files changed, 777 insertions(+)

diff --git a/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/dc/http/impl/HTTPDataConnectorParser.java b/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/dc/http/impl/HTTPDataConnectorParser.java
new file mode 100644
index 0000000..b3f40d7
--- /dev/null
+++ b/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/dc/http/impl/HTTPDataConnectorParser.java
@@ -0,0 +1,280 @@
+/*
+ * 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.dc.http.impl;
+
+import java.util.List;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.xml.namespace.QName;
+
+import net.shibboleth.ext.spring.util.SpringSupport;
+import net.shibboleth.idp.attribute.resolver.dc.http.impl.HTTPDataConnector;
+import net.shibboleth.idp.attribute.resolver.dc.http.impl.ScriptedResponseMappingStrategy;
+import net.shibboleth.idp.attribute.resolver.dc.http.impl.TemplatedURLBuilder;
+import net.shibboleth.idp.attribute.resolver.spring.dc.AbstractDataConnectorParser;
+import net.shibboleth.idp.attribute.resolver.spring.dc.impl.CacheConfigParser;
+import net.shibboleth.idp.attribute.resolver.spring.impl.AttributeResolverNamespaceHandler;
+import net.shibboleth.idp.profile.spring.relyingparty.metadata.ScriptTypeBeanParser;
+import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+import net.shibboleth.utilities.java.support.primitive.StringSupport;
+import net.shibboleth.utilities.java.support.xml.AttributeSupport;
+import net.shibboleth.utilities.java.support.xml.ElementSupport;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.support.BeanDefinitionBuilder;
+import org.springframework.beans.factory.xml.ParserContext;
+import org.w3c.dom.Element;
+
+/** Bean definition Parser for a {@link HTTPDataConnector}. */
+public class HTTPDataConnectorParser extends AbstractDataConnectorParser {
+
+    /** Schema type name. */
+    @Nonnull public static final QName TYPE_NAME =
+            new QName(AttributeResolverNamespaceHandler.NAMESPACE, "HTTP");
+
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(HTTPDataConnectorParser.class);
+
+    /** {@inheritDoc} */
+    @Override protected Class<HTTPDataConnector> getNativeBeanClass() {
+        return HTTPDataConnector.class;
+    }
+    
+    /** {@inheritDoc} */
+    @Override protected void doV2Parse(@Nonnull final Element config, @Nonnull final ParserContext parserContext,
+            @Nonnull final BeanDefinitionBuilder builder) {
+
+        log.debug("{} Parsing custom configuration {}", getLogPrefix(), config);
+
+        final V2Parser v2Parser = new V2Parser(config, getLogPrefix());
+
+        final String httpClientID = v2Parser.getBeanHttpClientID();
+        if (httpClientID != null) {
+            builder.addPropertyReference("httpClient", httpClientID);
+        }
+
+        final String searchBuilderID = v2Parser.getBeanSearchBuilderID();
+        if (searchBuilderID != null) {
+            builder.addPropertyReference("executableSearchBuilder", searchBuilderID);
+        } else {
+            final BeanDefinition def = v2Parser.createTemplateBuilder();
+            if (def != null) {
+                builder.addPropertyValue("executableSearchBuilder", def);
+            }
+        }
+
+        final String mappingStrategyID = v2Parser.getBeanMappingStrategyID();
+        if (mappingStrategyID != null) {
+            builder.addPropertyReference("mappingStrategy", mappingStrategyID);
+        } else {
+            final BeanDefinition def = v2Parser.createMappingStrategy(config.getAttributeNS(null, "id"));
+            if (def != null) {
+                builder.addPropertyValue("mappingStrategy", def);
+            }
+        }
+        
+        final String validatorID = v2Parser.getBeanValidatorID();
+        if (validatorID != null) {
+            builder.addPropertyReference("validator", validatorID);
+        }
+
+        final String resultCacheBeanID = CacheConfigParser.getBeanResultCacheID(config);
+        
+        if (null != resultCacheBeanID) {
+           builder.addPropertyReference("resultsCache", resultCacheBeanID);
+        } else {
+            builder.addPropertyValue("resultsCache", v2Parser.createCache(parserContext));
+        }
+
+        builder.setInitMethodName("initialize");
+        builder.setDestroyMethodName("destroy");
+    }
+
+    /**
+     * Utility class for parsing v2 schema configuration.
+     * 
+     */
+    protected static class V2Parser {
+
+        /** Base XML element. */
+        @Nonnull private final Element configElement;
+
+        /** Class logger. */
+        @Nonnull private final Logger log = LoggerFactory.getLogger(V2Parser.class);
+
+        /** Parent parser's log prefix.*/
+        @Nonnull @NotEmpty private final String logPrefix;
+
+        /**
+         * Creates a new V2Parser with the supplied element.
+         * 
+         * @param config HTTP DataConnector element
+         * @param prefix the parent parser's log prefix.
+         */
+        public V2Parser(@Nonnull final Element config,  @Nonnull @NotEmpty final String prefix) {
+            Constraint.isNotNull(config, "HTTP DataConnector element cannot be null");
+            configElement = config;
+            logPrefix = prefix;
+        }
+
+        /**
+         * Get the bean ID of an externally defined HttpClient.
+         * 
+         * @return HttpClient bean ID
+         */
+        @Nullable public String getBeanHttpClientID() {
+            return AttributeSupport.getAttributeValue(configElement, new QName("httpClientRef"));
+        }
+
+        /**
+         * Get the bean ID of an externally defined search builder.
+         * 
+         * @return search builder bean ID
+         */
+        @Nullable public String getBeanSearchBuilderID() {
+            return AttributeSupport.getAttributeValue(configElement, null, "executableSearchBuilderRef");
+        }
+        
+        /**
+         * Create the definition of the template driven search builder.
+         * 
+         * @return the bean definition for the template search builder.
+         */
+        @Nonnull public BeanDefinition createTemplateBuilder() {
+            final BeanDefinitionBuilder templateBuilder =
+                    BeanDefinitionBuilder.genericBeanDefinition(TemplatedURLBuilder.class);
+            templateBuilder.setInitMethodName("initialize");
+            templateBuilder.setDestroyMethodName("destroy");
+
+            String velocityEngineRef = StringSupport.trimOrNull(configElement.getAttributeNS(null, "templateEngine"));
+            if (null == velocityEngineRef) {
+                velocityEngineRef = "shibboleth.VelocityEngine";
+            }
+            templateBuilder.addPropertyReference("velocityEngine", velocityEngineRef);
+
+            final String securityParams =
+                    StringSupport.trimOrNull(configElement.getAttributeNS(null, "httpClientSecurityParametersRef"));
+            if (securityParams != null) {
+                templateBuilder.addPropertyReference("httpClientSecurityParameters", securityParams);
+            }
+            
+            final List<Element> urlTemplates = ElementSupport.getChildElements(configElement, 
+                            new QName(AttributeResolverNamespaceHandler.NAMESPACE, "URLTemplate"));
+            
+            if (urlTemplates.size() > 1) {
+                log.warn("{} A maximum of 1 <URLTemplate> should be specified; the first one has been used",
+                        getLogPrefix());
+            }
+            
+            String url = null;
+            if (!urlTemplates.isEmpty()) {
+                url = urlTemplates.get(0).getTextContent();
+            }
+            templateBuilder.addPropertyValue("templateText", url);
+
+            templateBuilder.setInitMethodName("initialize");
+            templateBuilder.setDestroyMethodName("destroy");
+            return templateBuilder.getBeanDefinition();
+        }
+        
+        /**
+         * Get the bean ID of an externally defined mapping strategy.
+         * 
+         * @return mapping strategy bean ID
+         */
+        @Nullable public String getBeanMappingStrategyID() {
+            return AttributeSupport.getAttributeValue(configElement, null, "mappingStrategyRef");
+        }
+
+        /**
+         * Create the scripted result mapping strategy.
+         * 
+         * @param id the ID of the 
+         * 
+         * @return mapping strategy
+         */
+        @Nullable public BeanDefinition createMappingStrategy(@Nullable final String id) {
+
+            final List<Element> mappings = ElementSupport.getChildElements(configElement, 
+                    new QName(AttributeResolverNamespaceHandler.NAMESPACE, "ResponseMapping"));
+    
+            if (mappings.size() > 1) {
+                log.warn("{} A maximum of 1 <ResponseMapping> should be specified; the first one has been used",
+                        getLogPrefix());
+            }
+            
+            final BeanDefinitionBuilder mapper =
+                    ScriptTypeBeanParser.parseScriptType(ScriptedResponseMappingStrategy.class, mappings.get(0));
+            if (id != null) {
+                mapper.addPropertyValue("logPrefix", id + ':');
+            }
+            
+            final String maxLength = StringSupport.trimOrNull(configElement.getAttributeNS(null, "maxLength"));
+            if (maxLength != null) {
+                mapper.addPropertyValue("maxLength", maxLength);
+            }
+            
+            if (configElement.hasAttributeNS(null, "acceptStatuses")) {
+                mapper.addPropertyValue("acceptStatuses",
+                        SpringSupport.getAttributeValueAsManagedList(
+                                configElement.getAttributeNodeNS(null, "acceptStatuses")));
+            }
+
+            if (configElement.hasAttributeNS(null, "acceptTypes")) {
+                mapper.addPropertyValue("acceptTypes",
+                        SpringSupport.getAttributeValueAsManagedList(
+                                configElement.getAttributeNodeNS(null, "acceptTypes")));
+            }
+
+            return mapper.getBeanDefinition();
+        }
+        
+        /**
+         * Get the bean ID of an externally defined validator.
+         * 
+         * @return validator bean ID
+         */
+        @Nullable public String getBeanValidatorID() {
+            return AttributeSupport.getAttributeValue(configElement, null, "validatorRef");
+        }
+        
+        /**
+         * Create the results cache. See {@link CacheConfigParser}.
+         * 
+         * @param parserContext bean parser context
+         * 
+         * @return results cache
+         */
+        @Nullable public BeanDefinition createCache(@Nonnull final ParserContext parserContext) {
+            final CacheConfigParser parser = new CacheConfigParser(configElement);
+            return parser.createCache(parserContext);
+        }
+        
+        /** The parent parser's log prefix.
+         * @return the log prefix.
+         */
+        @Nonnull @NotEmpty private String getLogPrefix() {
+            return logPrefix;
+        }
+    }
+    
+}
\ No newline at end of file
diff --git a/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/dc/http/impl/package-info.java b/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/dc/http/impl/package-info.java
new file mode 100644
index 0000000..fca4d36
--- /dev/null
+++ b/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/dc/http/impl/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+
+/**
+ * The parser for the HTTP Data connector.
+ */
+
+package net.shibboleth.idp.attribute.resolver.spring.dc.http.impl;
\ 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 7aca8bb..4b2e60b 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
@@ -38,6 +38,7 @@ import net.shibboleth.idp.attribute.resolver.spring.ad.impl.TransientIdAttribute
 import net.shibboleth.idp.attribute.resolver.spring.ad.mapped.impl.MappedAttributeDefinitionParser;
 import net.shibboleth.idp.attribute.resolver.spring.ad.mapped.impl.SourceValueParser;
 import net.shibboleth.idp.attribute.resolver.spring.ad.mapped.impl.ValueMapParser;
+import net.shibboleth.idp.attribute.resolver.spring.dc.http.impl.HTTPDataConnectorParser;
 import net.shibboleth.idp.attribute.resolver.spring.dc.impl.ComputedIDDataConnectorParser;
 import net.shibboleth.idp.attribute.resolver.spring.dc.impl.ScriptDataConnectorParser;
 import net.shibboleth.idp.attribute.resolver.spring.dc.impl.StaticDataConnectorParser;
@@ -118,6 +119,7 @@ public class AttributeResolverNamespaceHandler extends BaseSpringNamespaceHandle
                 new ComputedIDDataConnectorParser());
         registerBeanDefinitionParser(RDBMSDataConnectorParser.TYPE_NAME_RESOLVER, new RDBMSDataConnectorParser());
         registerBeanDefinitionParser(LDAPDataConnectorParser.TYPE_NAME_RESOLVER, new LDAPDataConnectorParser());
+        registerBeanDefinitionParser(HTTPDataConnectorParser.TYPE_NAME, new HTTPDataConnectorParser());
         registerBeanDefinitionParser(ScriptDataConnectorParser.TYPE_NAME_RESOLVER, new ScriptDataConnectorParser());
         registerBeanDefinitionParser(StaticDataConnectorParser.TYPE_NAME_RESOLVER, new StaticDataConnectorParser());
         registerBeanDefinitionParser(StoredIDDataConnectorParser.TYPE_NAME_RESOLVER, new StoredIDDataConnectorParser());
diff --git a/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/dc/http/HTTPDataConnectorParserTest.java b/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/dc/http/HTTPDataConnectorParserTest.java
new file mode 100644
index 0000000..c7de646
--- /dev/null
+++ b/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/dc/http/HTTPDataConnectorParserTest.java
@@ -0,0 +1,239 @@
+/*
+ * 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.dc.http;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Map;
+
+import net.shibboleth.ext.spring.config.DurationToLongConverter;
+import net.shibboleth.ext.spring.config.StringToIPRangeConverter;
+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.resolver.ResolutionException;
+import net.shibboleth.idp.attribute.resolver.context.AttributeResolutionContext;
+import net.shibboleth.idp.attribute.resolver.dc.http.impl.HTTPDataConnector;
+import net.shibboleth.idp.saml.impl.TestSources;
+import net.shibboleth.utilities.java.support.repository.RepositorySupport;
+
+import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
+import org.springframework.context.support.ConversionServiceFactoryBean;
+import org.springframework.context.support.GenericApplicationContext;
+import org.springframework.core.env.PropertySource;
+import org.springframework.mock.env.MockPropertySource;
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.Test;
+
+/** Test for {@link HTTPDataConnectorParser}. */
+public class HTTPDataConnectorParserTest {
+
+    private static final String TEST_URL =
+            RepositorySupport.buildHTTPSResourceURL("java-identity-provider",
+                    "idp-attribute-resolver-impl/src/test/resources/net/shibboleth/idp/attribute/resolver/impl/dc/http/test.json");
+
+    private static final String SCRIPT_PATH = "/net/shibboleth/idp/attribute/resolver/impl/dc/http/";
+    
+    private static final String SCRIPT_PATH_V8 = "/net/shibboleth/idp/attribute/resolver/impl/dc/http/v8/";
+
+    private GenericApplicationContext pendingTeardownContext = null;
+    
+    @AfterMethod public void tearDownTestContext() {
+        if (null == pendingTeardownContext ) {
+            return;
+        }
+        pendingTeardownContext.close();
+        pendingTeardownContext = null;
+    }
+    
+    private void setTestContext(final GenericApplicationContext context) {
+        tearDownTestContext();
+        pendingTeardownContext = context;
+    }
+    
+    @Test public void v2Config() throws Exception {
+        
+        final MockPropertySource propSource = singletonPropertySource("serviceURL", TEST_URL);
+        propSource.setProperty("scriptPath", (isV8() ? SCRIPT_PATH_V8 : SCRIPT_PATH) + "test.js");
+        
+        final HTTPDataConnector connector =
+                getDataConnector(propSource,
+                        "net/shibboleth/idp/attribute/resolver/spring/dc/http/http-attribute-resolver-v2.xml");
+        Assert.assertNotNull(connector);
+        
+        final AttributeResolutionContext context =
+                TestSources.createResolutionContext(TestSources.PRINCIPAL_ID, TestSources.IDP_ENTITY_ID,
+                        TestSources.SP_ENTITY_ID);
+        
+        final Map<String,IdPAttribute> attrs = connector.resolve(context);
+
+        Assert.assertEquals(attrs.size(), 2);
+        
+        Assert.assertEquals(attrs.get("foo").getValues().size(), 1);
+        Assert.assertEquals(attrs.get("foo").getValues().get(0).getValue(), "foo1");
+        
+        Assert.assertEquals(attrs.get("bar").getValues().size(), 2);
+        Assert.assertEquals(attrs.get("bar").getValues().get(0).getValue(), "bar1");
+        Assert.assertEquals(attrs.get("bar").getValues().get(1).getValue(), "bar2");
+        
+        Assert.assertTrue(connector.getResultsCache().size() == 1);
+    }
+
+    @Test(expectedExceptions=ResolutionException.class) public void v2BadProtocol() throws Exception {
+        
+        final MockPropertySource propSource = singletonPropertySource("serviceURL", TEST_URL);
+        propSource.setProperty("scriptPath", (isV8() ? SCRIPT_PATH_V8 : SCRIPT_PATH) + "test.js");
+        
+        final HTTPDataConnector connector =
+                getDataConnector(propSource,
+                        "net/shibboleth/idp/attribute/resolver/spring/dc/http/http-attribute-resolver-v2-badprotocol.xml");
+        Assert.assertNotNull(connector);
+        
+        final AttributeResolutionContext context =
+                TestSources.createResolutionContext(TestSources.PRINCIPAL_ID, TestSources.IDP_ENTITY_ID,
+                        TestSources.SP_ENTITY_ID);
+        
+        connector.resolve(context);
+    }
+
+    @Test(expectedExceptions=ResolutionException.class) public void v2Size() throws Exception {
+        
+        final MockPropertySource propSource = singletonPropertySource("serviceURL", TEST_URL);
+        propSource.setProperty("scriptPath", (isV8() ? SCRIPT_PATH_V8 : SCRIPT_PATH) + "testsize.js");
+        
+        final HTTPDataConnector connector =
+                getDataConnector(propSource,
+                        "net/shibboleth/idp/attribute/resolver/spring/dc/http/http-attribute-resolver-v2.xml");
+        Assert.assertNotNull(connector);
+        
+        final AttributeResolutionContext context =
+                TestSources.createResolutionContext(TestSources.PRINCIPAL_ID, TestSources.IDP_ENTITY_ID,
+                        TestSources.SP_ENTITY_ID);
+        
+        connector.resolve(context);
+    }
+
+    @Test(expectedExceptions=ResolutionException.class) public void v2Missing() throws Exception {
+        
+        final MockPropertySource propSource = singletonPropertySource("serviceURL", "http://shibboleth.net/test.json");
+        propSource.setProperty("scriptPath", (isV8() ? SCRIPT_PATH_V8 : SCRIPT_PATH) + "test.js");
+        
+        final HTTPDataConnector connector =
+                getDataConnector(propSource,
+                        "net/shibboleth/idp/attribute/resolver/spring/dc/http/http-attribute-resolver-v2.xml");
+        Assert.assertNotNull(connector);
+        
+        final AttributeResolutionContext context =
+                TestSources.createResolutionContext(TestSources.PRINCIPAL_ID, TestSources.IDP_ENTITY_ID,
+                        TestSources.SP_ENTITY_ID);
+        
+        connector.resolve(context);
+    }
+    
+    @Test public void v2MissingOk() throws Exception {
+        
+        final MockPropertySource propSource = singletonPropertySource("serviceURL", "http://shibboleth.net/test.json");
+        propSource.setProperty("scriptPath", (isV8() ? SCRIPT_PATH_V8 : SCRIPT_PATH) + "test.js");
+        
+        final HTTPDataConnector connector =
+                getDataConnector(propSource,
+                        "net/shibboleth/idp/attribute/resolver/spring/dc/http/http-attribute-resolver-v2-missingok.xml");
+        Assert.assertNotNull(connector);
+        
+        final AttributeResolutionContext context =
+                TestSources.createResolutionContext(TestSources.PRINCIPAL_ID, TestSources.IDP_ENTITY_ID,
+                        TestSources.SP_ENTITY_ID);
+        
+        final Map<String,IdPAttribute> attrs = connector.resolve(context);
+
+        Assert.assertTrue(attrs == null || attrs.isEmpty());
+    }
+
+    @Test public void hybridConfig() throws Exception {
+        final MockPropertySource propSource = singletonPropertySource("serviceURL", TEST_URL);
+        propSource.setProperty("scriptPath", (isV8() ? SCRIPT_PATH_V8 : SCRIPT_PATH) + "test.js");
+        
+        final HTTPDataConnector connector =
+                getDataConnector(propSource,
+                        "net/shibboleth/idp/attribute/resolver/spring/dc/http/http-attribute-resolver-v2-hybrid.xml",
+                        "net/shibboleth/idp/attribute/resolver/spring/dc/http/http-attribute-resolver-spring-context.xml");
+        Assert.assertNotNull(connector);
+        
+        final AttributeResolutionContext context =
+                TestSources.createResolutionContext(TestSources.PRINCIPAL_ID, TestSources.IDP_ENTITY_ID,
+                        TestSources.SP_ENTITY_ID);
+        
+        final Map<String,IdPAttribute> attrs = connector.resolve(context);
+
+        Assert.assertEquals(attrs.size(), 2);
+        
+        Assert.assertEquals(attrs.get("foo").getValues().size(), 1);
+        Assert.assertEquals(attrs.get("foo").getValues().get(0).getValue(), "foo1");
+        
+        Assert.assertEquals(attrs.get("bar").getValues().size(), 2);
+        Assert.assertEquals(attrs.get("bar").getValues().get(0).getValue(), "bar1");
+        Assert.assertEquals(attrs.get("bar").getValues().get(1).getValue(), "bar2");
+        
+        Assert.assertTrue(connector.getResultsCache().size() == 1);    
+    }
+
+    private HTTPDataConnector getDataConnector(final PropertySource propSource, final String... beanDefinitions)
+            throws IOException {
+        final GenericApplicationContext context = new GenericApplicationContext();
+        setTestContext(context);
+        context.setDisplayName("ApplicationContext: " + HTTPDataConnectorParserTest.class);
+
+        final ConversionServiceFactoryBean service = new ConversionServiceFactoryBean();
+        service.setConverters(new HashSet<>(Arrays.asList(new DurationToLongConverter(), new StringToIPRangeConverter(),
+                new StringToResourceConverter())));
+        service.afterPropertiesSet();
+
+        context.getBeanFactory().setConversionService(service.getObject());
+
+        if (propSource != null) {
+            context.getEnvironment().getPropertySources().addFirst(propSource);
+        }
+        
+        final XmlBeanDefinitionReader configReader = new SchemaTypeAwareXMLBeanDefinitionReader(context);
+
+        configReader.loadBeanDefinitions("net/shibboleth/idp/attribute/resolver/spring/dc/http/spring-beans.xml");
+        
+        final SchemaTypeAwareXMLBeanDefinitionReader beanDefinitionReader =
+                new SchemaTypeAwareXMLBeanDefinitionReader(context);
+
+        beanDefinitionReader.setValidating(true);
+        beanDefinitionReader.loadBeanDefinitions(beanDefinitions);
+        context.refresh();
+
+        return (HTTPDataConnector) context.getBean("myHTTP");
+    }
+
+    private boolean isV8() {
+        final String ver = System.getProperty("java.version");
+        return ver.startsWith("1.8");
+    }
+    
+    private MockPropertySource singletonPropertySource(final String name, final String value) {
+        final MockPropertySource propSource = new MockPropertySource("localProperties");
+        propSource.setProperty(name, value);
+        return propSource;
+    }
+
+}
\ No newline at end of file
diff --git a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/dc/http/http-attribute-resolver-spring-context.xml b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/dc/http/http-attribute-resolver-spring-context.xml
new file mode 100644
index 0000000..c8e4358
--- /dev/null
+++ b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/dc/http/http-attribute-resolver-spring-context.xml
@@ -0,0 +1,25 @@
+<?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:c="http://www.springframework.org/schema/c"
+    xmlns:p="http://www.springframework.org/schema/p"
+    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd"
+    default-init-method="initialize"
+    default-destroy-method="destroy">
+    
+    <bean id="cacheBuilder" class="com.google.common.cache.CacheBuilder" factory-method="from">
+        <constructor-arg value="expireAfterAccess=10s,maximumSize=25" />
+    </bean>
+    
+    <bean id="cache" factory-bean="cacheBuilder" factory-method="build" />
+    
+    <bean id="template" class="net.shibboleth.idp.attribute.resolver.dc.http.impl.TemplatedURLBuilder"
+        p:velocityEngine-ref="shibboleth.VelocityEngine"
+        p:templateText="%{serviceURL}" />
+
+    <bean id="mapping" class="net.shibboleth.idp.attribute.resolver.dc.http.impl.ScriptedResponseMappingStrategy"
+        factory-method="resourceScript"
+        c:_0="%{scriptPath}"
+        p:acceptStatuses="200"
+        p:acceptTypes="application/json" />
+    
+</beans>
\ No newline at end of file
diff --git a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/dc/http/http-attribute-resolver-v2-badprotocol.xml b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/dc/http/http-attribute-resolver-v2-badprotocol.xml
new file mode 100644
index 0000000..0043406
--- /dev/null
+++ b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/dc/http/http-attribute-resolver-v2-badprotocol.xml
@@ -0,0 +1,18 @@
+<?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">
+
+    <DataConnector id="myHTTP" xsi:type="HTTP"
+            httpClientRef="NoTrustEngineHttpClient"
+            httpClientSecurityParametersRef="BadProtocolParameters">
+            
+        <URLTemplate>%{serviceURL}</URLTemplate>
+        
+        <ResponseMapping>
+            <ScriptFile>%{scriptPath}</ScriptFile>
+        </ResponseMapping>
+        
+    </DataConnector>
+    
+</AttributeResolver>
diff --git a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/dc/http/http-attribute-resolver-v2-hybrid.xml b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/dc/http/http-attribute-resolver-v2-hybrid.xml
new file mode 100644
index 0000000..e73d4d2
--- /dev/null
+++ b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/dc/http/http-attribute-resolver-v2-hybrid.xml
@@ -0,0 +1,15 @@
+<?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">
+
+    <DataConnector id="myHTTP" xsi:type="HTTP"
+            httpClientRef="NoTrustEngineHttpClient"
+            executableSearchBuilderRef="template"
+            mappingStrategyRef="mapping">
+    
+        <ResultCacheBean>cache</ResultCacheBean>
+            
+    </DataConnector>
+            
+</AttributeResolver>
diff --git a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/dc/http/http-attribute-resolver-v2-missingok.xml b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/dc/http/http-attribute-resolver-v2-missingok.xml
new file mode 100644
index 0000000..6f3a276
--- /dev/null
+++ b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/dc/http/http-attribute-resolver-v2-missingok.xml
@@ -0,0 +1,20 @@
+<?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">
+
+    <DataConnector id="myHTTP" xsi:type="HTTP"
+            httpClientRef="NoTrustEngineHttpClient"
+            acceptStatuses="200 404">
+            
+        <URLTemplate>%{serviceURL}</URLTemplate>
+        
+        <ResponseMapping>
+            <Script>true</Script>
+        </ResponseMapping>
+        
+        <ResultCache expireAfterWrite="PT10S" />
+        
+    </DataConnector>
+    
+</AttributeResolver>
diff --git a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/dc/http/http-attribute-resolver-v2.xml b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/dc/http/http-attribute-resolver-v2.xml
new file mode 100644
index 0000000..aef5052
--- /dev/null
+++ b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/dc/http/http-attribute-resolver-v2.xml
@@ -0,0 +1,21 @@
+<?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">
+
+    <DataConnector id="myHTTP" xsi:type="HTTP"
+            httpClientRef="NoTrustEngineHttpClient"
+            acceptStatuses="200 201"
+            acceptTypes="application/json">
+            
+        <URLTemplate>%{serviceURL}</URLTemplate>
+        
+        <ResponseMapping>
+            <ScriptFile>%{scriptPath}</ScriptFile>
+        </ResponseMapping>
+        
+        <ResultCache expireAfterWrite="PT10S" />
+        
+    </DataConnector>
+    
+</AttributeResolver>
diff --git a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/dc/http/spring-beans.xml b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/dc/http/spring-beans.xml
new file mode 100644
index 0000000..6c2f273
--- /dev/null
+++ b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/dc/http/spring-beans.xml
@@ -0,0 +1,33 @@
+<?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:p="http://www.springframework.org/schema/p"
+    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd"
+    default-init-method="initialize"
+    default-destroy-method="destroy">
+
+    <bean id="NoTrustEngineHttpClient"
+        class="net.shibboleth.idp.profile.spring.relyingparty.metadata.HttpClientFactoryBean" />
+
+    <bean id="BadProtocolParameters"
+        class="org.opensaml.security.httpclient.HttpClientSecurityParameters"
+        p:TLSProtocols="SSLv3" />
+
+    <bean id="shibboleth.VelocityEngine" class="org.springframework.ui.velocity.VelocityEngineFactoryBean">
+        <property name="velocityProperties">
+            <props>
+                <prop key="resource.loader">classpath, string</prop>
+                <prop key="classpath.resource.loader.class">
+                    org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader
+                </prop>
+                <prop key="string.resource.loader.class">
+                    org.apache.velocity.runtime.resource.loader.StringResourceLoader
+                </prop>
+            </props>
+        </property>
+    </bean>
+    
+    <bean id="shibboleth.PropertySourcesPlaceholderConfigurer"
+        class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"
+        p:placeholderPrefix="%{" p:placeholderSuffix="}" />
+
+</beans>
\ No newline at end of file
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 2f46dc1..c6f2102 100644
--- a/idp-schema/src/main/resources/schema/shibboleth-attribute-resolver.xsd
+++ b/idp-schema/src/main/resources/schema/shibboleth-attribute-resolver.xsd
@@ -1252,6 +1252,108 @@
         </complexContent>
     </complexType>
     
+    <complexType name="HTTP">
+        <annotation>
+            <documentation>
+                A data connector definition that issues requests and parses responses using HTTP, typically
+                via a form of web service. REST and scripted handling of responses, typically in JSON,
+                is the primary use case.
+            </documentation>
+        </annotation>
+        <complexContent>
+            <extension base="resolver:FlattenedBaseDataConnectorType">
+                <choice maxOccurs="unbounded">
+                    <element ref="resolver:Dependency" />
+                    <element ref="resolver:InputAttributeDefinition" />
+                    <element ref="resolver:InputDataConnector"  />                                        
+                    <element ref="resolver:FailoverDataConnector" />
+                    <element name="URLTemplate" type="string">
+                        <annotation>
+                            <documentation>
+                                A template that will be used to create the absolute URL to request.
+                            </documentation>
+                        </annotation>
+                    </element>
+                    <element name="ResponseMapping" type="resolver:ScriptType">
+                        <annotation>
+                            <documentation>Maps the response into attributes by means of scripting.</documentation>
+                        </annotation>
+                    </element>
+                    <element name="ResultCache" type="resolver:CacheConfigType"/>
+                    <element name="ResultCacheBean" type="string"/>
+                </choice>
+                <attribute name="httpClientRef" type="string">
+                    <annotation>
+                        <documentation>
+                            Reference to a Spring bean providing the HttpClient to use.
+                        </documentation>
+                    </annotation>
+                </attribute>
+                <attribute name="httpClientSecurityParametersRef" type="string">
+                    <annotation>
+                        <documentation>
+                            Reference to a Spring bean providing the HttpClientSecurityParameters to use.
+                        </documentation>
+                    </annotation>
+                </attribute>
+                <attribute name="templateEngine" type="string">
+                    <annotation>
+                        <documentation>
+                            Name of the template engine defined within the application.
+                        </documentation>
+                    </annotation>
+                </attribute>
+                <attribute name="maxLength" type="string">
+                    <annotation>
+                        <documentation>
+                            Maximum size of response body to accept.
+                        </documentation>
+                    </annotation>
+                </attribute>
+                <attribute name="acceptStatuses" >
+                    <annotation>
+                        <documentation>
+                            A space-delimited list of HTTP status codes that should be treated as successful.
+                        </documentation>
+                    </annotation>
+                    <simpleType>
+                        <list itemType="string"/>
+                    </simpleType>
+                </attribute>
+                <attribute name="acceptTypes" >
+                    <annotation>
+                        <documentation>
+                            A space-delimited list of MIME content types that should be accepted. 
+                        </documentation>
+                    </annotation>
+                    <simpleType>
+                        <list itemType="string"/>
+                    </simpleType>
+                </attribute>
+                <attribute name="executableSearchBuilderRef" type="string">
+                    <annotation>
+                        <documentation>
+                            Reference to a Spring bean providing the ExecutableSearchBuilder implementation to use.
+                        </documentation>
+                    </annotation>
+                </attribute>
+                <attribute name="mappingStrategyRef" type="string">
+                    <annotation>
+                        <documentation>
+                            Reference to a Spring bean providing the MappingStrategy implementation to use.
+                        </documentation>
+                    </annotation>
+                </attribute>
+                <attribute name="validatorRef" type="string">
+                    <annotation>
+                        <documentation>
+                            Reference to a Spring bean providing the Validator implementation to use.
+                        </documentation>
+                    </annotation>
+                </attribute>
+            </extension>
+        </complexContent>
+    </complexType>
 
     <complexType name="ScriptedDataConnector">
         <annotation>

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


More information about the commits mailing list