[java-idp-oidc] 03/44: JOIDC-5 Initial versions of the node processor + related files. WIP.

Henri Mikkonen henri.mikkonen at iki.fi
Thu Oct 22 13:08:15 UTC 2020


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

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

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

commit 7b922743f2faa675fccce01b26ad7d7e18a288c6
Author: Henri Mikkonen <henri.mikkonen at iki.fi>
AuthorDate: Wed Apr 15 16:16:14 2020 +0300

    JOIDC-5 Initial versions of the node processor + related files. WIP.
    
    https://issues.shibboleth.net/jira/browse/JOIDC-5
---
 .../oidc/filter/impl/ClientInformationParser.java  |  56 +++
 .../oidc/impl/MetadataNamespaceHandler.java        |  39 +++
 .../impl/ClientInformationNodeProcessor.java       | 382 +++++++++++++++++++++
 .../src/main/resources/META-INF/spring.handlers    |   1 +
 .../src/main/resources/META-INF/spring.schemas     |   3 +-
 .../schema/idp-oidc-extension-metadata-ext.xsd     |  20 ++
 .../impl/ClientInformationNodeProcessorTest.java   | 104 ++++++
 .../impl/EntitiesDescriptor-with-oidcmd.xml        |  85 +++++
 8 files changed, 689 insertions(+), 1 deletion(-)

diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/profile/spring/relyingparty/metadata/oidc/filter/impl/ClientInformationParser.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/profile/spring/relyingparty/metadata/oidc/filter/impl/ClientInformationParser.java
new file mode 100644
index 00000000..6150f0eb
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/profile/spring/relyingparty/metadata/oidc/filter/impl/ClientInformationParser.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2017 - 2020, GÉANT
+ *
+ * Licensed 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.profile.spring.relyingparty.metadata.oidc.filter.impl;
+
+import javax.annotation.Nonnull;
+import javax.xml.namespace.QName;
+
+import org.geant.idpextension.oidc.metadata.impl.ClientInformationNodeProcessor;
+import org.springframework.beans.factory.support.BeanDefinitionBuilder;
+import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
+import org.springframework.beans.factory.xml.ParserContext;
+import org.w3c.dom.Element;
+
+import net.shibboleth.idp.profile.spring.relyingparty.metadata.oidc.impl.MetadataNamespaceHandler;
+
+/**
+ * Parser for a <ClientInformation> node processor.
+ */
+public class ClientInformationParser extends AbstractSingleBeanDefinitionParser {
+    
+    /** Element name. */
+    @Nonnull public static final QName TYPE_NAME =
+            new QName(MetadataNamespaceHandler.NAMESPACE, "ClientInformation");
+
+    /** {@inheritDoc} */
+    @Override protected Class<?> getBeanClass(final Element element) {
+        return ClientInformationNodeProcessor.class;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void doParse(final Element element, final ParserContext parserContext,
+            final BeanDefinitionBuilder builder) {
+        
+    }
+
+    /** {@inheritDoc} */
+    @Override protected boolean shouldGenerateId() {
+        return true;
+    }
+    
+}
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/profile/spring/relyingparty/metadata/oidc/impl/MetadataNamespaceHandler.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/profile/spring/relyingparty/metadata/oidc/impl/MetadataNamespaceHandler.java
new file mode 100644
index 00000000..caa9b0d7
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/profile/spring/relyingparty/metadata/oidc/impl/MetadataNamespaceHandler.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2017 - 2020, GÉANT
+ *
+ * Licensed 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.profile.spring.relyingparty.metadata.oidc.impl;
+
+import javax.annotation.Nonnull;
+
+import net.shibboleth.ext.spring.util.BaseSpringNamespaceHandler;
+import net.shibboleth.idp.profile.spring.relyingparty.metadata.oidc.filter.impl.ClientInformationParser;
+import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
+
+/** Namespace handler for <code>urn:mace:shibboleth:2.0:metadata:oidc</code>. */
+public class MetadataNamespaceHandler extends BaseSpringNamespaceHandler {
+
+    /** Namespace for this handler. */
+    @Nonnull
+    @NotEmpty
+    public static final String NAMESPACE = "urn:mace:shibboleth:2.0:metadata:oidc";
+
+    /** {@inheritDoc} */
+    @Override
+    public void init() {
+        registerBeanDefinitionParser(ClientInformationParser.TYPE_NAME, new ClientInformationParser());
+    }
+
+}
diff --git a/idp-oidc-extension-impl/src/main/java/org/geant/idpextension/oidc/metadata/impl/ClientInformationNodeProcessor.java b/idp-oidc-extension-impl/src/main/java/org/geant/idpextension/oidc/metadata/impl/ClientInformationNodeProcessor.java
new file mode 100644
index 00000000..1816ab02
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/java/org/geant/idpextension/oidc/metadata/impl/ClientInformationNodeProcessor.java
@@ -0,0 +1,382 @@
+/*
+ * Copyright (c) 2017 - 2020, GÉANT
+ *
+ * Licensed 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 org.geant.idpextension.oidc.metadata.impl;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.security.interfaces.ECPublicKey;
+import java.security.interfaces.RSAPublicKey;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.geant.idpextension.oidc.security.impl.CredentialConversionUtil;
+import org.opensaml.core.xml.XMLObject;
+import org.opensaml.saml.criterion.RoleDescriptorCriterion;
+import org.opensaml.saml.metadata.resolver.RoleDescriptorResolver;
+import org.opensaml.saml.metadata.resolver.filter.FilterException;
+import org.opensaml.saml.metadata.resolver.filter.MetadataNodeProcessor;
+import org.opensaml.saml.saml2.metadata.EntityDescriptor;
+import org.opensaml.saml.saml2.metadata.RoleDescriptor;
+import org.opensaml.saml.security.impl.MetadataCredentialResolver;
+import org.opensaml.security.credential.Credential;
+import org.opensaml.security.credential.UsageType;
+import org.opensaml.xmlsec.keyinfo.KeyInfoCredentialResolver;
+import org.opensaml.xmlsec.keyinfo.impl.BasicProviderKeyInfoCredentialResolver;
+import org.opensaml.xmlsec.keyinfo.impl.KeyInfoProvider;
+import org.opensaml.xmlsec.keyinfo.impl.provider.DSAKeyValueProvider;
+import org.opensaml.xmlsec.keyinfo.impl.provider.InlineX509DataProvider;
+import org.opensaml.xmlsec.keyinfo.impl.provider.RSAKeyValueProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.nimbusds.jose.EncryptionMethod;
+import com.nimbusds.jose.JWEAlgorithm;
+import com.nimbusds.jose.JWSAlgorithm;
+import com.nimbusds.jose.jwk.Curve;
+import com.nimbusds.jose.jwk.ECKey;
+import com.nimbusds.jose.jwk.JWK;
+import com.nimbusds.jose.jwk.KeyUse;
+import com.nimbusds.jose.jwk.RSAKey;
+import com.nimbusds.oauth2.sdk.GrantType;
+import com.nimbusds.oauth2.sdk.ResponseType;
+import com.nimbusds.oauth2.sdk.Scope;
+import com.nimbusds.oauth2.sdk.auth.ClientAuthenticationMethod;
+import com.nimbusds.oauth2.sdk.id.ClientID;
+import com.nimbusds.oauth2.sdk.id.SoftwareID;
+import com.nimbusds.oauth2.sdk.id.SoftwareVersion;
+import com.nimbusds.openid.connect.sdk.SubjectType;
+import com.nimbusds.openid.connect.sdk.claims.ACR;
+import com.nimbusds.openid.connect.sdk.rp.ApplicationType;
+import com.nimbusds.openid.connect.sdk.rp.OIDCClientInformation;
+import com.nimbusds.openid.connect.sdk.rp.OIDCClientMetadata;
+
+import net.shibboleth.idp.saml.oidc.xmlobject.DefaultAcrValue;
+import net.shibboleth.idp.saml.oidc.xmlobject.MetadataValueSAMLObject;
+import net.shibboleth.idp.saml.oidc.xmlobject.OAuthRPRoleDescriptorType;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+import net.shibboleth.utilities.java.support.primitive.StringSupport;
+import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
+import net.shibboleth.utilities.java.support.resolver.ResolverException;
+
+/**
+ * An implementation of {@link MetadataNodeProcessor} which supports adding an instance of
+ * {@link OIDCClientInformation} to the object metadata of {@link OAuthRPRoleDescriptorType}.
+ */
+public class ClientInformationNodeProcessor implements MetadataNodeProcessor {
+    
+    /** Class logger. */
+    private final Logger log = LoggerFactory.getLogger(ClientInformationNodeProcessor.class);
+    
+    private KeyInfoCredentialResolver keyInfoCredentialResolver;
+    
+    public ClientInformationNodeProcessor() {
+        final List<KeyInfoProvider> keyInfoProviders = new ArrayList<>();
+        keyInfoProviders.add(new DSAKeyValueProvider());
+        keyInfoProviders.add(new RSAKeyValueProvider());
+        keyInfoProviders.add(new InlineX509DataProvider());
+        keyInfoCredentialResolver = new BasicProviderKeyInfoCredentialResolver(keyInfoProviders);
+
+    }
+    
+    protected Iterable<Credential> resolveCredentials(final OAuthRPRoleDescriptorType roleDescriptor) {
+        final MetadataCredentialResolver credentialResolver = new MetadataCredentialResolver();
+        credentialResolver.setKeyInfoCredentialResolver(keyInfoCredentialResolver);
+        credentialResolver.setRoleDescriptorResolver(new RoleDescriptorResolver() {
+
+            @Override
+            public Iterable<RoleDescriptor> resolve(CriteriaSet criteria) throws ResolverException {
+                final ArrayList<RoleDescriptor> result = new ArrayList<>();
+                result.add(resolveSingle(criteria));
+                return result;
+            }
+
+            @Override
+            public RoleDescriptor resolveSingle(CriteriaSet criteria) throws ResolverException {
+                return roleDescriptor;
+            }
+
+            @Override
+            public String getId() {
+                return null;
+            }
+
+            @Override
+            public boolean isRequireValidMetadata() {
+                return false;
+            }
+
+            @Override
+            public void setRequireValidMetadata(boolean requireValidMetadata) {
+                // no op
+            }
+            
+        });
+        try {
+            credentialResolver.initialize();
+        } catch (ComponentInitializationException e) {
+            log.error("Could not initialize the metdadata credential resolver", e);
+            return null;
+        }
+        final CriteriaSet criteriaSet = new CriteriaSet();
+        criteriaSet.add(new RoleDescriptorCriterion(roleDescriptor));
+
+        try {
+            return credentialResolver.resolve(criteriaSet);
+        } catch (ResolverException e) {
+            log.error("Could not resolve credentials", e);
+            return null;
+        }
+    }
+
+    @Override
+    public void process(XMLObject metadataNode) throws FilterException {
+        if (metadataNode instanceof OAuthRPRoleDescriptorType) {
+            final OAuthRPRoleDescriptorType roleDescriptor = (OAuthRPRoleDescriptorType) metadataNode;
+            log.info("Keys: {}", roleDescriptor.getKeyDescriptors());
+            final ClientID clientId = parseClientID(roleDescriptor);
+            if (clientId == null) {
+                log.error("Could not find a value for client_id, nothing to do");
+                return;
+            }
+            final OIDCClientMetadata metadata = populateMetadata(roleDescriptor);
+            final OIDCClientInformation clientInformation = new OIDCClientInformation(clientId, null, metadata, null);
+            metadataNode.getObjectMetadata().put(clientInformation);
+
+            final Iterable<Credential> credentials = resolveCredentials(roleDescriptor);
+            if (credentials != null) {
+                for (final Credential credential : credentials) {
+                    log.debug("JWK {}", credentialToKey(credential).toJSONString());
+                }
+            }
+        }
+        
+    }
+    
+    /**
+     * Resolved KeyUse parameter from credential.
+     * 
+     * @param credential credential to resolve KeyUse of
+     * @return KeyUse of credential
+     */
+    private KeyUse resolveKeyUse(Credential credential) {
+        if (credential.getUsageType().equals(UsageType.SIGNING)) {
+            return KeyUse.SIGNATURE;
+        }
+        if (credential.getUsageType().equals(UsageType.ENCRYPTION)) {
+            return KeyUse.ENCRYPTION;
+        }
+        return null;
+    }
+
+    /**
+     * Convert credential to JWK. Only RSA and EC keys supported.
+     * 
+     * @param credential to convert.
+     * @return credential as JWK.
+     */
+    private JWK credentialToKey(Credential credential) {
+        JWK key = null;
+        switch (credential.getPublicKey().getAlgorithm()) {
+            case "RSA":
+                key = new RSAKey.Builder((RSAPublicKey) credential.getPublicKey()).keyUse(resolveKeyUse(credential))
+                        .keyID(CredentialConversionUtil.resolveKid(credential)).build();
+                break;
+
+            case "EC":
+                key = new ECKey.Builder(Curve.forECParameterSpec(((ECPublicKey) credential.getPublicKey()).getParams()),
+                        (ECPublicKey) credential.getPublicKey()).keyUse(resolveKeyUse(credential))
+                                .keyID(CredentialConversionUtil.resolveKid(credential)).build();
+            default:
+                break;
+        }
+        return key;
+    }
+    
+    protected ClientID parseClientID(final OAuthRPRoleDescriptorType roleDescriptor) {
+        if (!roleDescriptor.hasParent() || !(roleDescriptor.getParent() instanceof EntityDescriptor)) {
+            log.warn("Unexpected structure, EntityDescriptor not as a parent for OAuthRPRoleDescriptor");
+            return null;
+        }
+        final EntityDescriptor entityDescriptor = (EntityDescriptor) roleDescriptor.getParent();
+        return new ClientID(entityDescriptor.getEntityID());
+    }
+    
+    protected OIDCClientMetadata populateMetadata(final OAuthRPRoleDescriptorType roleDescriptor) {
+        final OIDCClientMetadata metadata = new OIDCClientMetadata();
+        metadata.setApplicationType(parseApplicationType(roleDescriptor));
+        metadata.setURI(getSingleURIValue(roleDescriptor.getClientUri()));
+        metadata.setDefaultACRs(parseDefaultAcrValues(roleDescriptor));
+        metadata.setGrantTypes(parseGrantTypes(roleDescriptor));
+        metadata.setIDTokenJWEAlg(parseJweAlgorithm(roleDescriptor.getIdTokenEncryptionResponseAlg()));
+        metadata.setIDTokenJWEEnc(parseEncryptionMethod(roleDescriptor.getIdTokenEncryptionResponseEnc()));
+        metadata.setIDTokenJWSAlg(parseJwsAlgorithm(roleDescriptor.getIdTokenSignedResponseAlg()));
+        metadata.setInitiateLoginURI(getSingleURIValue(roleDescriptor.getInitiateLoginUri()));
+        metadata.setJWKSetURI(getSingleURIValue(roleDescriptor.getJwksUri()));
+        metadata.setPostLogoutRedirectionURIs(parseUris(roleDescriptor.getPostLogoutRedirectUris()));
+        metadata.setRedirectionURIs(parseUris(roleDescriptor.getRedirectUris()));
+        metadata.setRequestObjectJWEAlg(parseJweAlgorithm(roleDescriptor.getRequestObjectEncryptionResponseAlg()));
+        metadata.setRequestObjectJWEEnc(parseEncryptionMethod(roleDescriptor.getRequestObjectEncryptionResponseEnc()));
+        metadata.setRequestObjectJWSAlg(parseJwsAlgorithm(roleDescriptor.getRequestObjectSignedResponseAlg()));
+        metadata.setRequestObjectURIs(parseUris(roleDescriptor.getRequestUris()));
+        metadata.setResponseTypes(parseResponseTypes(roleDescriptor));
+        metadata.setScope(parseScope(roleDescriptor));
+        metadata.setSectorIDURI(getSingleURIValue(roleDescriptor.getSectorIdentifierUri()));
+        final String softwareId = getSingleValue(roleDescriptor.getSoftwareId());
+        if (softwareId != null) {
+            metadata.setSoftwareID(new SoftwareID(softwareId));
+        }
+        final String softwareVersion = getSingleValue(roleDescriptor.getSoftwareVersion());
+        if (softwareVersion != null) {
+            metadata.setSoftwareVersion(new SoftwareVersion(softwareVersion));
+        }
+        metadata.setSubjectType(parseSubjectType(roleDescriptor));
+        metadata.setTokenEndpointAuthMethod(parseClientAuthenticationMethod(roleDescriptor));
+        metadata.setTokenEndpointAuthJWSAlg(parseJwsAlgorithm(roleDescriptor.getTokenEndpointAuthSigningAlg()));
+        metadata.setUserInfoJWEAlg(parseJweAlgorithm(roleDescriptor.getUserInfoEncryptionResponseAlg()));
+        metadata.setUserInfoJWEEnc(parseEncryptionMethod(roleDescriptor.getUserInfoEncryptionResponseEnc()));
+        metadata.setUserInfoJWSAlg(parseJwsAlgorithm(roleDescriptor.getUserInfoSignedResponseAlg()));
+        return metadata;
+    }
+    
+    protected ClientAuthenticationMethod parseClientAuthenticationMethod(final OAuthRPRoleDescriptorType roleDescriptor) {
+        final String metadataValue = getSingleValue(roleDescriptor.getTokenEndpointAuthMethod());
+        if (metadataValue == null) {
+            return null;
+        }
+        return ClientAuthenticationMethod.parse(metadataValue);
+    }
+    
+    protected ApplicationType parseApplicationType(final OAuthRPRoleDescriptorType roleDescriptor) {
+        final String metadataValue = getSingleValue(roleDescriptor.getApplicationType());
+        if (ApplicationType.NATIVE.toString().equalsIgnoreCase(metadataValue)) {
+            return ApplicationType.NATIVE;
+        }
+        return ApplicationType.WEB;
+    }
+    
+    protected SubjectType parseSubjectType(final OAuthRPRoleDescriptorType roleDescriptor) {
+        final String metadataValue = getSingleValue(roleDescriptor.getSubjectType());
+        if (SubjectType.PAIRWISE.toString().equalsIgnoreCase(metadataValue)) {
+            return SubjectType.PAIRWISE;
+        }
+        return SubjectType.PUBLIC;
+    }
+    
+    protected List<ACR> parseDefaultAcrValues(final OAuthRPRoleDescriptorType roleDescriptor) {
+        final List<ACR> acrs = new ArrayList<>();
+        for (final DefaultAcrValue acr : roleDescriptor.getDefaultAcrValues()) {
+            final String value = getSingleValue(acr);
+            if (value != null) {
+                acrs.add(new ACR(value));
+            }
+        }
+        return acrs;
+    }
+    
+    protected Set<GrantType> parseGrantTypes(final OAuthRPRoleDescriptorType roleDescriptor) {
+        final Set<GrantType> grantTypes = new HashSet<>();
+        for (final net.shibboleth.idp.saml.oidc.xmlobject.GrantType grantType : roleDescriptor.getGrantTypes()) {
+            final String value = getSingleValue(grantType);
+            if (value != null) {
+                grantTypes.add(new GrantType(value));
+            }
+        }
+        return grantTypes;
+    }
+    
+    protected Set<ResponseType> parseResponseTypes(final OAuthRPRoleDescriptorType roleDescriptor) {
+        final Set<ResponseType> responseTypes = new HashSet<>();
+        for (final net.shibboleth.idp.saml.oidc.xmlobject.ResponseType responseType : roleDescriptor.getResponseTypes()) {
+            final String value = getSingleValue(responseType);
+            if (value != null) {
+                responseTypes.add(new ResponseType(value));
+            }
+        }
+        return responseTypes;
+    }
+    
+    protected Scope parseScope(final OAuthRPRoleDescriptorType roleDescriptor) {
+        final Scope scope = new Scope();
+        for (final net.shibboleth.idp.saml.oidc.xmlobject.Scope singleScope : roleDescriptor.getScopes()) {
+            final String value = getSingleValue(singleScope);
+            if (value != null) {
+                scope.add(value);
+            }
+        }
+        if (!scope.contains("openid")) {
+            scope.add("openid");
+        }
+        return scope;
+    }
+    
+    protected JWEAlgorithm parseJweAlgorithm(final MetadataValueSAMLObject metadataValue) {
+        final String value = getSingleValue(metadataValue);
+        if (value != null) {
+            return new JWEAlgorithm(value);
+        }
+        return null;
+    }
+
+    protected JWSAlgorithm parseJwsAlgorithm(final MetadataValueSAMLObject metadataValue) {
+        final String value = getSingleValue(metadataValue);
+        if (value != null) {
+            return new JWSAlgorithm(value);
+        }
+        return null;
+    }
+
+    protected EncryptionMethod parseEncryptionMethod(final MetadataValueSAMLObject metadataValue) {
+        final String value = getSingleValue(metadataValue);
+        if (value != null) {
+            return new EncryptionMethod(value);
+        }
+        return null;
+    }
+    
+    protected Set<URI> parseUris(final List<? extends MetadataValueSAMLObject> listOfValues) {
+        final Set<URI> uris = new HashSet<>();
+        for (final MetadataValueSAMLObject value : listOfValues) {
+            final URI uri = getSingleURIValue(value);
+            if (uri != null) {
+                uris.add(uri);
+            }
+        }
+        return uris;
+    }
+
+    protected String getSingleValue(final MetadataValueSAMLObject metadataValue) {
+        if (metadataValue != null) {
+            return StringSupport.trimOrNull(metadataValue.getValue());
+        }
+        return null;
+    }
+    
+    protected URI getSingleURIValue(final MetadataValueSAMLObject metadataValue) {
+        final String value = getSingleValue(metadataValue);
+        if (value != null) {
+            try {
+                return new URI(value);
+            } catch (URISyntaxException e) {
+                log.warn("Could not parse {} into an URI", value, e);
+            }
+        }
+        return null;
+    }
+    
+}
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/spring.handlers b/idp-oidc-extension-impl/src/main/resources/META-INF/spring.handlers
index 56325217..94d1859f 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/spring.handlers
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/spring.handlers
@@ -1,2 +1,3 @@
 org.geant.idpextension.oidc.attribute.encoder = org.geant.idpextension.oidc.attribute.resolver.spring.enc.impl.AttributeEncoderNamespaceHandler
 org.geant.idpextension.oidc.attribute.filter = org.geant.idpextension.oidc.attribute.filter.spring.impl.AttributeFilterNamespaceHandler
+urn\:mace\:shibboleth\:2.0\:metadata\:oidc = net.shibboleth.idp.profile.spring.relyingparty.metadata.oidc.impl.MetadataNamespaceHandler
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/spring.schemas b/idp-oidc-extension-impl/src/main/resources/META-INF/spring.schemas
index da0faa73..0bda88dc 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/spring.schemas
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/spring.schemas
@@ -1,2 +1,3 @@
 classpath\:/schema/idp-oidc-extension-afp.xsd = schema/idp-oidc-extension-afp.xsd
-classpath\:/schema/idp-oidc-extension-attribute-encoder.xsd = schema/idp-oidc-extension-attribute-encoder.xsd
\ No newline at end of file
+classpath\:/schema/idp-oidc-extension-attribute-encoder.xsd = schema/idp-oidc-extension-attribute-encoder.xsd
+classpath\:/schema/idp-oidc-extension-metadata-ext.xsd = schema/idp-oidc-extension-metadata-ext.xsd
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/resources/schema/idp-oidc-extension-metadata-ext.xsd b/idp-oidc-extension-impl/src/main/resources/schema/idp-oidc-extension-metadata-ext.xsd
new file mode 100644
index 00000000..5ed3ce0b
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/resources/schema/idp-oidc-extension-metadata-ext.xsd
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<schema xmlns="http://www.w3.org/2001/XMLSchema" xmlns:shibmd="urn:mace:shibboleth:2.0:metadata"
+    targetNamespace="urn:mace:shibboleth:2.0:metadata:oidc" elementFormDefault="qualified">
+
+    <import namespace="urn:mace:shibboleth:2.0:metadata"
+        schemaLocation="http://shibboleth.net/schema/idp/shibboleth-metadata.xsd" />
+
+    <complexType name="ClientInformation">
+        <annotation>
+            <documentation>
+                A node processor that processes OAuthRP and attaches the parsed client
+                information via object metadata.
+            </documentation>
+        </annotation>
+        <complexContent>
+            <extension base="shibmd:MetadataNodeProcessorType"/>
+        </complexContent>
+    </complexType>
+
+</schema>
diff --git a/idp-oidc-extension-impl/src/test/java/org/geant/idpextension/oidc/metadata/impl/ClientInformationNodeProcessorTest.java b/idp-oidc-extension-impl/src/test/java/org/geant/idpextension/oidc/metadata/impl/ClientInformationNodeProcessorTest.java
new file mode 100644
index 00000000..5dfbfc8c
--- /dev/null
+++ b/idp-oidc-extension-impl/src/test/java/org/geant/idpextension/oidc/metadata/impl/ClientInformationNodeProcessorTest.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2017 - 2020, GÉANT
+ *
+ * Licensed 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 org.geant.idpextension.oidc.metadata.impl;
+
+import java.io.File;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.opensaml.core.criterion.EntityIdCriterion;
+import org.opensaml.core.xml.XMLObjectBaseTestCase;
+import org.opensaml.saml.criterion.EntityRoleCriterion;
+import org.opensaml.saml.criterion.ProtocolCriterion;
+import org.opensaml.saml.metadata.resolver.filter.MetadataNodeProcessor;
+import org.opensaml.saml.metadata.resolver.filter.impl.NodeProcessingMetadataFilter;
+import org.opensaml.saml.metadata.resolver.impl.FilesystemMetadataResolver;
+import org.opensaml.saml.metadata.resolver.impl.PredicateRoleDescriptorResolver;
+import org.opensaml.saml.saml2.metadata.RoleDescriptor;
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.nimbusds.openid.connect.sdk.rp.OIDCClientInformation;
+
+import net.shibboleth.idp.saml.oidc.xmlobject.OAuthRPRoleDescriptorType;
+import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
+import net.shibboleth.utilities.java.support.resolver.ResolverException;
+
+public class ClientInformationNodeProcessorTest extends XMLObjectBaseTestCase {
+
+    private String mdFileName;
+    
+    private FilesystemMetadataResolver mdProvider;
+    
+    private PredicateRoleDescriptorResolver roleResolver;
+
+    @BeforeMethod
+    protected void setUp() throws Exception {
+        mdFileName = "/org/geant/idpextension/oidc/metadata/impl/EntitiesDescriptor-with-oidcmd.xml";
+        
+        URL mdURL = ClientInformationNodeProcessorTest.class.getResource(mdFileName);
+        File mdFile = new File(mdURL.toURI());
+        
+        mdProvider = new FilesystemMetadataResolver(mdFile);
+        mdProvider.setParserPool(parserPool);
+        mdProvider.setId("test");
+        NodeProcessingMetadataFilter filter = new NodeProcessingMetadataFilter();
+        List<MetadataNodeProcessor> processors = new ArrayList<>();
+        processors.add(new ClientInformationNodeProcessor());
+        filter.setNodeProcessors(processors);
+        filter.initialize();
+        mdProvider.setMetadataFilter(filter);
+        mdProvider.initialize();
+        
+        roleResolver = new PredicateRoleDescriptorResolver(mdProvider);
+        roleResolver.initialize();
+        
+    }
+    
+    // Success cases
+    
+    /**
+     * Test valid index.
+     * @throws ResolverException
+     */
+    @Test
+    public void testWithValidIndex() throws ResolverException {
+        RoleDescriptor role =  roleResolver.resolveSingle(new CriteriaSet(
+                new EntityIdCriterion("mockSamlClientId"),
+                new EntityRoleCriterion(OAuthRPRoleDescriptorType.DEFAULT_ELEMENT_NAME),
+                new ProtocolCriterion("http://openid.net/specs/openid-connect-core-1_0.html")));
+        Assert.assertNotNull(role);
+        
+        System.out.println(role.getClass());
+        final OAuthRPRoleDescriptorType rp = (OAuthRPRoleDescriptorType) role;
+        System.out.println(rp.getRedirectUris().get(0).getValue());
+        System.out.println(rp.getRedirectUris().get(1).getValue());
+        System.out.println(rp.getResponseTypes().get(0).getValue());
+        System.out.println(rp.getGrantTypes().get(0).getValue());
+        System.out.println(rp.getApplicationType().getValue());
+        System.out.println(rp.getDefaultMaxAge());
+        System.out.println(rp.isRequireAuthTime());
+        
+        final List<OIDCClientInformation> clientInformations = rp.getObjectMetadata().get(OIDCClientInformation.class);
+        if (clientInformations != null && clientInformations.size() > 0) {
+            System.out.println("Katsos: " + clientInformations.get(0).getID());
+        }
+    }
+
+}
diff --git a/idp-oidc-extension-impl/src/test/resources/org/geant/idpextension/oidc/metadata/impl/EntitiesDescriptor-with-oidcmd.xml b/idp-oidc-extension-impl/src/test/resources/org/geant/idpextension/oidc/metadata/impl/EntitiesDescriptor-with-oidcmd.xml
new file mode 100644
index 00000000..c3304734
--- /dev/null
+++ b/idp-oidc-extension-impl/src/test/resources/org/geant/idpextension/oidc/metadata/impl/EntitiesDescriptor-with-oidcmd.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<md:EntitiesDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" Name="RP test">
+
+    <md:EntityDescriptor entityID="mockSamlClientId">
+
+        <md:RoleDescriptor xmlns:oidcmd="urn:mace:shibboleth:metadata:oidc:1.0"
+                           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+                           xsi:type="oidcmd:OAuthRPRoleDescriptorType"
+                           protocolSupportEnumeration="http://openid.net/specs/openid-connect-core-1_0.html"
+                           defaultMaxAge="123"
+                           requireAuthTime="true">
+        <md:KeyDescriptor use="signing">
+            <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
+                    <ds:X509Data>
+                        <ds:X509Certificate>
+MIIEQDCCAqigAwIBAgIVAIarXvdvyS47KJR7U40FlTufyD8vMA0GCSqGSIb3DQEB
+CwUAMCAxHjAcBgNVBAMMFWxvY2FsaG9zdC5sb2NhbGRvbWFpbjAeFw0xOTA2MTcx
+MTI5MTJaFw0zOTA2MTcxMTI5MTJaMCAxHjAcBgNVBAMMFWxvY2FsaG9zdC5sb2Nh
+bGRvbWFpbjCCAaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoCggGBALXysGFnoBFh
+oasd5uMecp9OTBjvztntPUVmHfm4R3AcItEMEZEN/pETcX/wgKdo4qCBq4PrZITa
+T8Salgl0XL6qF1Wia3JNA7Hh/OaoQEUsbsHgsjLMKt6MJh8vIaE1o8loL7Ay4WmZ
+Cr3wc8ZS6CpMsv+qbxkyfl1h7MTydETnQhg/X83bj+BjJSh7QeFU0d0SWK1dN2/D
+nFoGOfuTfVqeDRIwMxKlR5G//8N202sLaG28NljaHhLn3jHXeiGpCQ+Q2X90dkFb
+EKb6sQ6SlDUAzm9MwLYjglDyOhXpUqOnvD67nggLb4Gn/4k+g5wtdfr7unOJYcHK
+w7JGnI8Gd0lJMd6B3SpkhUOWgKv/D6HIBArhqSEmXuTyy8FewyYuo1XkIw/Lu3bB
+9qoBojM1tygoGlKi7R7e719J+DSkhyGbMyQ59leoN97iGGgqjUWS5mew8zSNviyz
+4uGqvxmLWU9UTH1YhlARsBF1bMiMnwLz7dF74AaAkC4pN3BYzDMyHQIDAQABo3Ew
+bzAdBgNVHQ4EFgQUwKUd9D1Qymu2oBEVTscrAhP+sIUwTgYDVR0RBEcwRYIVbG9j
+YWxob3N0LmxvY2FsZG9tYWluhixodHRwczovL2xvY2FsaG9zdC5sb2NhbGRvbWFp
+bi9pZHAvc2hpYmJvbGV0aDANBgkqhkiG9w0BAQsFAAOCAYEAEYqh54a+j5OuR1UB
+/AT9k2xXVwHiqQXAC/2un8O5BWAOeOq9+0gLJO5yaJp5c9GjPXRJmnDfGP9HFF6R
+CjngtRCm1gV/fpj97IRQS5oroaeTWPQ9ZD5+ogs5DNt6UZeJ2GqpfA5mOytNg3cM
+OP1B5QnA1apOaG4FHTegJR7WOIXkkAjEJUy6R+5Q6At7DdK/SRrP5onVPFv2HgGF
+E9v9iX/uQepDizS5F2oi6LZCl1/b38gxA8BFL7VZu53JQguaA7SrnP+dBOErT/yh
+Qcx3e9wE2ms8H1qISIdl3e7gvLi5jEyDWC9Agde6EjjvVVJAF7jR0puQ39mBfoxP
+moVdHJQmCt3V7Ew9tYZUpG3rjp4YNXOiM+QhtwhHWT94q9uJKUQ6JvbxgLNDs5KM
+3PENx2C60TPFne9nRRIMVDavU4wwY7GdCgeo8PiZ5zxI0ZCkxh38ODePtKQrxJ7i
+E0J1BE2LIxa1T7KY0XKpsH0iI2dNfZfNpNp4v/HiDb4svYgq
+                        </ds:X509Certificate>
+                    </ds:X509Data>
+            </ds:KeyInfo>
+        </md:KeyDescriptor>
+        <md:KeyDescriptor use="signing">
+            <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
+                    <ds:X509Data>
+                        <ds:X509Certificate>
+MIIBKDCBzgIJAOYlspXlaqguMAoGCCqGSM49BAMCMBwxCzAJBgNVBAYTAkZJMQ0w
+CwYDVQQDDAR0ZXN0MB4XDTE5MTEwMTA4Mjg0OVoXDTIwMTAzMTA4Mjg0OVowHDEL
+MAkGA1UEBhMCRkkxDTALBgNVBAMMBHRlc3QwWTATBgcqhkjOPQIBBggqhkjOPQMB
+BwNCAARCUOlFMtRj3MIbdCzXmoGz4giDwjzPoX4AxMehhlXmPOodQhLDdvDqx3KE
+hqadzIIsKHRQPDycscpHWpPbaQ2VMAoGCCqGSM49BAMCA0kAMEYCIQCVykSuUjlX
+j4lxI6YqgYVuuhL2rG4hIrXw/pCey7eF2gIhAOSSaS025lQWy09W4NlnO28OkHoI
++Hbap7+DQlhbbr2d
+                        </ds:X509Certificate>
+                    </ds:X509Data>
+            </ds:KeyInfo>
+        </md:KeyDescriptor>
+        <md:KeyDescriptor use="encryption">
+            <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
+                    <ds:X509Data>
+                        <ds:X509Certificate>
+MIIBKDCBzgIJAOYlspXlaqguMAoGCCqGSM49BAMCMBwxCzAJBgNVBAYTAkZJMQ0w
+CwYDVQQDDAR0ZXN0MB4XDTE5MTEwMTA4Mjg0OVoXDTIwMTAzMTA4Mjg0OVowHDEL
+MAkGA1UEBhMCRkkxDTALBgNVBAMMBHRlc3QwWTATBgcqhkjOPQIBBggqhkjOPQMB
+BwNCAARCUOlFMtRj3MIbdCzXmoGz4giDwjzPoX4AxMehhlXmPOodQhLDdvDqx3KE
+hqadzIIsKHRQPDycscpHWpPbaQ2VMAoGCCqGSM49BAMCA0kAMEYCIQCVykSuUjlX
+j4lxI6YqgYVuuhL2rG4hIrXw/pCey7eF2gIhAOSSaS025lQWy09W4NlnO28OkHoI
++Hbap7+DQlhbbr2d
+                        </ds:X509Certificate>
+                    </ds:X509Data>
+            </ds:KeyInfo>
+        </md:KeyDescriptor>
+            <oidcmd:RedirectUri>https://example.org/cb</oidcmd:RedirectUri>
+            <oidcmd:RedirectUri>https://example.org/cb2</oidcmd:RedirectUri>
+            <oidcmd:GrantType>authorization_code</oidcmd:GrantType>
+            <oidcmd:ResponseType>code</oidcmd:ResponseType>
+            <oidcmd:ApplicationType>web</oidcmd:ApplicationType>
+            <oidcmd:TokenEndpointAuthMethod>client_secret_basic</oidcmd:TokenEndpointAuthMethod>
+            <oidcmd:Scope>openid</oidcmd:Scope>
+            <oidcmd:Scope>profile</oidcmd:Scope>
+        </md:RoleDescriptor>
+
+    </md:EntityDescriptor>
+
+</md:EntitiesDescriptor>
\ No newline at end of file

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


More information about the commits mailing list