[java-oidc-common] 02/02: JCOMOIDC-28 A fetching strategy for fetching policies from file/endpoint

Henri Mikkonen henri.mikkonen at iki.fi
Fri Feb 4 12:54:41 UTC 2022


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

hjmikkon pushed a commit to branch main
in repository java-oidc-common.

View the commit online:
http://git.shibboleth.net/view/?p=java-oidc-common.git;a=commit;h=e72a17326eadb3d323be06a814ce94c93c15a8d6

commit e72a17326eadb3d323be06a814ce94c93c15a8d6
Author: Henri Mikkonen <henri.mikkonen at iki.fi>
AuthorDate: Fri Feb 4 14:46:29 2022 +0200

    JCOMOIDC-28 A fetching strategy for fetching policies from file/endpoint
    
    https://shibboleth.atlassian.net/browse/JCOMOIDC-28
    
    The criterion contains the location for the (policy) resource to be fetched via
    dynamic metadata cache. First use case for this is that the location is
    given via HTTP request parameter to the registration access token issuance
    flow (admin flow in the OP plugin).
---
 .../criterion/ResourceLocationCriterion.java       |  78 ++++++++++++
 .../impl/DefaultMetadataPolicyResponseHandler.java | 113 +++++++++++++++++
 .../MetadataPolicyViaLocationFetchingStrategy.java | 136 +++++++++++++++++++++
 3 files changed, 327 insertions(+)

diff --git a/oidc-common-metadata-api/src/main/java/net/shibboleth/oidc/metadata/criterion/ResourceLocationCriterion.java b/oidc-common-metadata-api/src/main/java/net/shibboleth/oidc/metadata/criterion/ResourceLocationCriterion.java
new file mode 100644
index 0000000..3056417
--- /dev/null
+++ b/oidc-common-metadata-api/src/main/java/net/shibboleth/oidc/metadata/criterion/ResourceLocationCriterion.java
@@ -0,0 +1,78 @@
+/*
+ * 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.oidc.metadata.criterion;
+
+import java.util.Objects;
+
+import javax.annotation.Nonnull;
+
+import net.shibboleth.utilities.java.support.logic.Constraint;
+import net.shibboleth.utilities.java.support.resolver.Criterion;
+
+/**
+ * A {@link Criterion} representing a resource location.
+ */
+public class ResourceLocationCriterion implements Criterion {
+
+    /** The resource location. */
+    @Nonnull private final String location;
+    
+    /**
+     * Constructor.
+     *
+     * @param resourceLocation The resource location.
+     */
+    public ResourceLocationCriterion(@Nonnull final String resourceLocation) {
+        location = Constraint.isNotNull(resourceLocation, "Resource location cannot be null");
+    }
+    
+    /**
+     * Get the resource location.
+
+     * @return The resource location.
+     */
+    @Nonnull public String getResourceLocation() {
+        return location;
+    }
+    
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        return "ResourceLocationCriterion [location=" + location + "]";
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        return Objects.hash(location);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        final ResourceLocationCriterion other = (ResourceLocationCriterion) obj;
+        return location.equals(other.location);
+    }
+
+}
diff --git a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/policy/impl/DefaultMetadataPolicyResponseHandler.java b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/policy/impl/DefaultMetadataPolicyResponseHandler.java
new file mode 100644
index 0000000..cb7525e
--- /dev/null
+++ b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/policy/impl/DefaultMetadataPolicyResponseHandler.java
@@ -0,0 +1,113 @@
+/*
+ * 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.oidc.metadata.policy.impl;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.client.ResponseHandler;
+import org.apache.http.util.EntityUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.slf4j.MDC;
+
+import net.shibboleth.oidc.metadata.policy.MetadataPolicy;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.utilities.java.support.component.AbstractIdentifiableInitializableComponent;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+import net.shibboleth.utilities.java.support.component.ComponentSupport;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+
+/** The response handler for parsing the metadata policy document into a map. */
+public class DefaultMetadataPolicyResponseHandler extends AbstractIdentifiableInitializableComponent
+        implements ResponseHandler<Map<String, MetadataPolicy>> {        
+    
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(DefaultMetadataPolicyResponseHandler.class);
+    
+    /** The parsing strategy used for parsing metadata policies. */
+    @NonnullAfterInit private Function<byte[], List<Map<String, MetadataPolicy>>> parsingStrategy;
+    
+    /**
+     * Set the parsing strategy used for parsing metadata policies.
+     * 
+     * @param strategy What to set.
+     */
+    public void setParsingStrategy(@Nonnull final Function<byte[], List<Map<String, MetadataPolicy>>> strategy) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
+        
+        parsingStrategy = Constraint.isNotNull(strategy, "Parsing strategy cannot be null");
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void doInitialize() throws ComponentInitializationException {
+        super.doInitialize();
+        if (parsingStrategy == null) {
+            throw new ComponentInitializationException("Parsing strategy cannot be null");
+        }
+    }
+    
+    /** {@inheritDoc} */
+    @Override
+    @Nullable public Map<String, MetadataPolicy> handleResponse(final HttpResponse response) throws IOException {
+        
+        final int httpStatusCode = response.getStatusLine().getStatusCode();
+        
+        final String currentRequestURI = 
+                MDC.get(MetadataPolicyViaLocationFetchingStrategy.MDC_ATTRIB_CURRENT_REQUEST_URI);
+        
+        if (httpStatusCode != HttpStatus.SC_OK) {
+            log.warn("Non-ok status code '{}' returned from remote metadata source: {}",
+                    httpStatusCode, currentRequestURI);
+            return null;
+        }
+        
+        final List<Map<String, MetadataPolicy>> parsedResponse;
+        
+        try {
+            // this should convert the entity with the character set from the entity.
+            parsedResponse = parsingStrategy.apply(EntityUtils.toByteArray(response.getEntity()));
+            
+        } catch (final Exception e) {
+            // catch any of the many exceptions
+            log.error("Error parsing HTTP response stream", e);
+            return null;
+        }
+        
+        if (parsedResponse == null || parsedResponse.isEmpty()) {
+            log.error("No parsed values found from the response.");
+            return null;
+        }
+        
+        if (parsedResponse.size() > 1) {
+            log.warn("More than one metadata policies found, returning first of the list.");            
+        }
+        return parsedResponse.get(0);
+        
+    }
+    
+}
diff --git a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/policy/impl/MetadataPolicyViaLocationFetchingStrategy.java b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/policy/impl/MetadataPolicyViaLocationFetchingStrategy.java
new file mode 100644
index 0000000..f15a7b5
--- /dev/null
+++ b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/policy/impl/MetadataPolicyViaLocationFetchingStrategy.java
@@ -0,0 +1,136 @@
+/*
+ * 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.oidc.metadata.policy.impl;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.ResponseHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.core.io.FileSystemResource;
+
+import net.shibboleth.oidc.metadata.cache.CacheLoadingContext;
+import net.shibboleth.oidc.metadata.cache.LoadingStrategy;
+import net.shibboleth.oidc.metadata.cache.impl.DefaultFileLoadingStrategy;
+import net.shibboleth.oidc.metadata.criterion.ResourceLocationCriterion;
+import net.shibboleth.oidc.metadata.impl.AbstractDynamicHTTPFetchingStrategy;
+import net.shibboleth.oidc.metadata.policy.MetadataPolicy;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+import net.shibboleth.utilities.java.support.component.ComponentSupport;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
+
+/**
+ * A fetching strategy that exploits {@link ResourceLocationCriterion} from the given {@link CriteriaSet} to resolve
+ * a location for metadata policy. If the value starts with <code>http://</code> or <code>https://</code>, the policy
+ * is fetched via {@link HttpClient}. In other cases the location is expected to be a file path. Possibly existing
+ * <code>file:</code> prefix is removed before loading the file.
+ */
+public class MetadataPolicyViaLocationFetchingStrategy
+        extends AbstractDynamicHTTPFetchingStrategy<Map<String, MetadataPolicy>>
+        implements Function<CriteriaSet, Map<String, MetadataPolicy>> {
+
+    /**
+     * Constructor.
+     *
+     * @param client the instance of {@link HttpClient} used to fetch remote metadata policy.
+     * @param handler the response handler used to convert the HTTP response to the metadata policy.
+     */
+    public MetadataPolicyViaLocationFetchingStrategy(HttpClient client,
+            ResponseHandler<Map<String, MetadataPolicy>> handler) {
+        super(client, handler);
+    }
+
+    /** Class logger. */
+    @Nonnull private Logger log = LoggerFactory.getLogger(MetadataPolicyViaLocationFetchingStrategy.class);
+
+    /** The parsing strategy used for parsing metadata policies. */
+    @NonnullAfterInit private Function<byte[], List<Map<String, MetadataPolicy>>> parsingStrategy;
+    
+    /**
+     * Set the parsing strategy used for parsing metadata policies.
+     * 
+     * @param strategy What to set.
+     */
+    public void setParsingStrategy(@Nonnull final Function<byte[], List<Map<String, MetadataPolicy>>> strategy) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
+        
+        parsingStrategy = Constraint.isNotNull(strategy, "Parsing strategy cannot be null");
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void doInitialize() throws ComponentInitializationException {
+        super.doInitialize();
+        
+        if (parsingStrategy == null) {
+            throw new ComponentInitializationException("Parsing strategy cannot be null");
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    @Nullable public Map<String, MetadataPolicy> apply(final @Nonnull CriteriaSet criteriaSet) {
+        final String requestURL = buildRequestURL(criteriaSet);
+        if (requestURL == null) {
+            return null;
+        }
+        if (requestURL.startsWith("http://") || requestURL.startsWith("https://")) {
+            return super.apply(criteriaSet);
+        }
+        final String fileLocation = requestURL.startsWith("file:") ? requestURL.substring(5) : requestURL;
+        final CacheLoadingContext context = new CacheLoadingContext(null, null);
+        final FileSystemResource resource = new FileSystemResource(fileLocation);
+        final LoadingStrategy loadingStrategy;
+        try {
+            loadingStrategy = new DefaultFileLoadingStrategy(resource);
+        } catch (final IOException e) {
+            log.error("Could not load a metadata policy from file {}", fileLocation, e);
+            return null;
+        }
+        final List<Map<String, MetadataPolicy>> result = parsingStrategy.apply(loadingStrategy.apply(context));
+        if (result == null || result.isEmpty()) {
+            log.warn("Could not find any entries via parsing strategy");
+            return null;
+        }
+        if (result.size() > 1) {
+            log.warn("More than one metadata policies found, returning first of the list.");
+        }
+        return result.get(0);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected String buildRequestURL(final CriteriaSet criteria) {
+        if (criteria == null || criteria.isEmpty()) {
+            return null;
+        }
+        final ResourceLocationCriterion criterion = criteria.get(ResourceLocationCriterion.class);
+        return criterion == null ? null : criterion.getResourceLocation();
+    }
+
+}

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


More information about the commits mailing list