[java-oidc-common] 01/02: Moved PopulateJWTEncryptionParameters from RP to commons.

Henri Mikkonen henri.mikkonen at iki.fi
Wed Jan 4 14:33:13 UTC 2023


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=16d74ab344448a72178512635c1e1d4819f66254

commit 16d74ab344448a72178512635c1e1d4819f66254
Author: Henri Mikkonen <henri.mikkonen at iki.fi>
AuthorDate: Wed Jan 4 16:29:36 2023 +0200

    Moved PopulateJWTEncryptionParameters from RP to commons.
    
    Also added support for encryptionOptional -predicate, as it's needed
    by OP.
---
 .../impl/PopulateJWTEncryptionParameters.java      | 358 +++++++++++++++++++++
 1 file changed, 358 insertions(+)

diff --git a/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/impl/PopulateJWTEncryptionParameters.java b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/impl/PopulateJWTEncryptionParameters.java
new file mode 100644
index 0000000..5b8c7b9
--- /dev/null
+++ b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/impl/PopulateJWTEncryptionParameters.java
@@ -0,0 +1,358 @@
+/*
+ * 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.profile.impl;
+
+import java.util.List;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
+import org.opensaml.profile.action.ActionSupport;
+import org.opensaml.profile.action.EventIds;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.profile.context.navigate.OutboundMessageContextLookup;
+import org.opensaml.security.credential.Credential;
+import org.opensaml.security.credential.UsageType;
+import org.opensaml.security.criteria.UsageCriterion;
+import org.opensaml.xmlsec.criterion.EncryptionOptionalCriterion;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import net.shibboleth.idp.profile.AbstractProfileAction;
+import net.shibboleth.idp.profile.context.RelyingPartyContext;
+import net.shibboleth.oidc.metadata.context.OIDCMetadataContext;
+import net.shibboleth.oidc.metadata.context.OIDCProviderMetadataContext;
+import net.shibboleth.oidc.profile.config.OIDCAuthorizationConfiguration;
+import net.shibboleth.oidc.profile.config.logic.EncryptionOptionalPredicate;
+import net.shibboleth.oidc.security.JWTEncryptionConfiguration;
+import net.shibboleth.oidc.security.JWTEncryptionParameters;
+import net.shibboleth.oidc.security.JWTEncryptionParametersResolver;
+import net.shibboleth.oidc.security.context.JWTSecurityParametersContext;
+import net.shibboleth.oidc.security.criterion.ClientInformationCriterion;
+import net.shibboleth.oidc.security.criterion.JWTEncryptionConfigurationCriterion;
+import net.shibboleth.oidc.security.criterion.ProviderMetadataCriterion;
+import net.shibboleth.oidc.security.criterion.StaticCredentialCriterion;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
+import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
+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;
+import net.shibboleth.utilities.java.support.resolver.ResolverException;
+
+/**
+ * Action that resolves and populates {@link JWTEncryptionParameters} on an {@link JWTSecurityParametersContext}
+ * created/accessed via a lookup function, by default on a child of the outbound message context.
+ * 
+ * <p>The resolution process is contingent on the active profile configuration requesting encryption.</p>
+ * 
+ * <p>The default, per-RelyingParty, and default per-profile {@link JWTEncryptionConfiguration}
+ * objects are input to the resolution process, along with the relying party's client metadata, any static
+ * credentials configured on the relying party, and the OpenID  Provider metadata (which in most cases 
+ * will be the source of the eventual encryption key)</p>
+ * 
+ * @event {@link EventIds#PROCEED_EVENT_ID}
+ * @event {@link EventIds#INVALID_PROFILE_CTX}
+ * @event {@link EventIds#INVALID_SEC_CFG}
+ * @post set the encryption parameters onto the security parameters context
+ */
+public class PopulateJWTEncryptionParameters extends AbstractProfileAction {
+
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(PopulateJWTEncryptionParameters.class);
+    
+    /** A friendly name to log as the subject of encryption parameter resolution.*/
+    @Nonnull private String forFriendlyName;
+
+    /** Predicate to determine how to proceed if no encryption parameters are resolved. */
+    @Nonnull private Predicate<ProfileRequestContext> encryptionOptionalPredicate;
+
+    /** Strategy used to look up the {@link JWTSecurityParametersContext} to extract parameters from. */
+    @Nonnull 
+    private Function<ProfileRequestContext,JWTSecurityParametersContext> securityParametersContextLookupStrategy;
+
+    /** Strategy used to look up a per-request {@link JWTEncryptionConfiguration} list. */
+    @NonnullAfterInit 
+    private Function<ProfileRequestContext,List<JWTEncryptionConfiguration>> configurationLookupStrategy;
+    
+    /** Resolver for parameters to store into context. */
+    @NonnullAfterInit private JWTEncryptionParametersResolver encParamsresolver;
+    
+    /** Active configurations to feed into resolver. */
+    @Nullable @NonnullElements private List<JWTEncryptionConfiguration> encryptionConfigurations;
+    
+    /** Strategy used to look up a OIDC client metadata context. */
+    @Nullable private Function<ProfileRequestContext, OIDCMetadataContext> oidcClientMetadataContextLookupStrategy;
+    
+    /** Strategy used to look up a OIDC provider metadata context. */
+    @Nullable 
+    private Function<ProfileRequestContext, OIDCProviderMetadataContext> oidcProviderMetadataContextLookupStrategy;
+    
+    /** Lookup function for relying party context. */
+    @Nonnull private Function<ProfileRequestContext, RelyingPartyContext> relyingPartyContextLookupStrategy;
+
+    /** Whether encryption is optional. */
+    private boolean encryptionOptional;
+
+    /** Context to populate. */
+    private JWTSecurityParametersContext encryptionContext;
+    
+    /** Constructor. */
+    public PopulateJWTEncryptionParameters() {
+        forFriendlyName = "not-specified";
+        encryptionOptionalPredicate = new EncryptionOptionalPredicate();
+        securityParametersContextLookupStrategy =
+                new ChildContextLookup<>(JWTSecurityParametersContext.class, true).compose(
+                        new OutboundMessageContextLookup());
+        oidcClientMetadataContextLookupStrategy = new ChildContextLookup<>(OIDCMetadataContext.class);
+        oidcProviderMetadataContextLookupStrategy = new ChildContextLookup<>(OIDCProviderMetadataContext.class);
+        relyingPartyContextLookupStrategy =  new ChildContextLookup<>(RelyingPartyContext.class);
+    }
+    
+    /**
+     * Set lookup strategy for relying party context.
+     * 
+     * @param strategy lookup strategy
+     */
+    public void setRelyingPartyContextLookupStrategy(
+            @Nonnull final Function<ProfileRequestContext, RelyingPartyContext> strategy) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        
+        relyingPartyContextLookupStrategy =
+                Constraint.isNotNull(strategy, "RelyingPartyContext lookup strategy cannot be null");
+    }
+    
+    /**
+     * Set the lookup strategy to locate the security parameters context.
+     * 
+     * @param strategy the lookup strategy
+     */
+    public void setSecurityParametersContextLookupStrategy(
+            final Function<ProfileRequestContext, JWTSecurityParametersContext> strategy) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
+
+        securityParametersContextLookupStrategy = Constraint.isNotNull(strategy,
+                "securityParametersContextLookupStrategy can not be null");
+    }
+    
+    /**
+     * Set lookup strategy for {@link OIDCMetadataContext} for input to resolution.
+     * 
+     * @param strategy lookup strategy
+     */
+    public void setClientMetadataContextLookupStrategy(
+            @Nullable final Function<ProfileRequestContext, OIDCMetadataContext> strategy) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+        oidcClientMetadataContextLookupStrategy = strategy;
+    }
+    
+    /**
+     * Set lookup strategy for {@link OIDCProviderMetadataContext} for input to resolution.
+     * 
+     * @param strategy lookup strategy
+     */
+    public void setProviderMetadataContextLookupStrategy(
+            @Nullable final Function<ProfileRequestContext, OIDCProviderMetadataContext> strategy) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+        oidcProviderMetadataContextLookupStrategy = strategy;
+    }
+    
+    /**
+     * Set the friendly name to log as the subject of encryption parameter resolution.
+     * 
+     * @param name the friendly name
+     */
+    public void setForFriendlyName(@Nonnull @NotEmpty final String name) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
+
+        forFriendlyName = Constraint.isNotEmpty(name, "ForFriendlyName can not be null or empty");
+    }
+    
+    /**
+     * Set the strategy used to look up a per-request {@link JWTEncryptionConfiguration} list.
+     * 
+     * @param strategy lookup strategy
+     */
+    public void setConfigurationLookupStrategy(
+            @Nonnull final Function<ProfileRequestContext, List<JWTEncryptionConfiguration>> strategy) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
+
+        configurationLookupStrategy =
+                Constraint.isNotNull(strategy, "EncryptionConfiguration lookup strategy cannot be null");
+    }
+    
+    /**
+     * Set the encParamsresolver to use for the parameters to store into the context.
+     * 
+     * @param newResolver encParamsresolver to use
+     */
+    public void setEncryptionParametersResolver(@Nonnull final JWTEncryptionParametersResolver newResolver) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+        encParamsresolver = Constraint.isNotNull(newResolver, "EncryptionParametersResolver cannot be null");
+    }
+
+    /**
+     * Sets the condition to apply to determine how to proceed if encryption parameter resolution fails.
+     * 
+     * @param condition condition to set
+     */
+    public void setEncryptionOptionalPredicate(@Nonnull final Predicate<ProfileRequestContext> condition) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        
+        encryptionOptionalPredicate = Constraint.isNotNull(condition, "Condition cannot be null");
+    }
+
+    @Override
+    protected void doInitialize() throws ComponentInitializationException {
+        super.doInitialize();
+
+        if (encParamsresolver == null) {
+            throw new ComponentInitializationException("EncryptionParametersResolver cannot be null");
+        } 
+    }
+    
+    @Override
+    protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
+        if (!super.doPreExecute(profileRequestContext)) {
+            log.debug("{} Encryption disabled for '{}'", getLogPrefix(), forFriendlyName);
+            return false;
+        }
+        
+        encryptionContext = securityParametersContextLookupStrategy.apply(profileRequestContext);
+        if (encryptionContext == null) {
+            log.debug("{} No EncryptionContext returned by lookup strategy", getLogPrefix());
+            ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
+            return false;
+        }
+
+        encryptionOptional = encryptionOptionalPredicate.test(profileRequestContext);
+
+        return true;
+    }
+    
+    
+ // Checkstyle: CyclomaticComplexity OFF
+    @Override
+    protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
+
+        log.debug("{} Resolving EncryptionParameters for '{}' encryption", getLogPrefix(),forFriendlyName);
+        
+        try {
+            encryptionConfigurations = configurationLookupStrategy.apply(profileRequestContext);
+            if (encryptionConfigurations == null || encryptionConfigurations.isEmpty()) {
+                throw new ResolverException("No EncryptionConfigurations returned by lookup strategy");
+            }
+            final CriteriaSet criteria = buildCriteriaSet(profileRequestContext);
+            final JWTEncryptionParameters params = encParamsresolver.resolveSingle(criteria);
+            
+            if (params != null) {
+                log.debug("{} Resolved EncryptionParameters for {}", getLogPrefix(),forFriendlyName);
+                encryptionContext.setEncryptionParameters(params);          
+            } else {               
+                if (encryptionOptional) {
+                    log.debug("{} Resolver returned no EncryptionParameters", getLogPrefix());
+                    log.debug("{} Encryption is optional, ignoring inability to encrypt", getLogPrefix());
+                } else {
+                    log.warn("{} Resolver returned no EncryptionParameters", getLogPrefix());
+                    ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_SEC_CFG);
+                }
+            }
+        } catch (final ResolverException e) {
+            if (encryptionOptional) {
+                log.debug("{} Encryption is optional, ignoring inability to encrypt", getLogPrefix());
+            } else {
+                log.error("{} Error resolving EncryptionParameters", getLogPrefix(), e);            
+                ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_SEC_CFG);
+            }
+        }
+    }
+// Checkstyle: CyclomaticComplexity ON
+    
+    /**
+     * Build the criteria used as input to the {@link JWTEncryptionParametersResolver}.
+     * 
+     * @param profileRequestContext current profile request context
+     * 
+     * @return the criteria set to use
+     */
+    @Nonnull
+    private CriteriaSet buildCriteriaSet(@Nonnull final ProfileRequestContext profileRequestContext) {
+
+        final CriteriaSet criteria = new CriteriaSet(
+                new JWTEncryptionConfigurationCriterion(encryptionConfigurations));
+        
+        criteria.add(new UsageCriterion(UsageType.ENCRYPTION));
+        criteria.add(new EncryptionOptionalCriterion(encryptionOptional));
+        
+        // Add client metadata criterion
+        final OIDCMetadataContext oidcMetadataCtx = 
+                oidcClientMetadataContextLookupStrategy.apply(profileRequestContext);
+        if (oidcMetadataCtx != null && oidcMetadataCtx.getClientInformation() != null) {
+            log.debug(
+                    "{} Adding OIDC client information to resolution criteria for encryption algorithms",
+                    getLogPrefix());
+            criteria.add(new ClientInformationCriterion(oidcMetadataCtx.getClientInformation()));
+        } else {
+            log.debug("{} No OIDC client information available", getLogPrefix());
+        }
+        
+        // Add OP metadata criterion
+        final OIDCProviderMetadataContext oidcProviderMetadataCtx = 
+                oidcProviderMetadataContextLookupStrategy.apply(profileRequestContext);
+        if (oidcProviderMetadataCtx != null && oidcProviderMetadataCtx.getProviderInformation() != null) {
+            log.debug("{} Adding OIDC provider information to resolution criteria",
+                    getLogPrefix());
+            criteria.add(new ProviderMetadataCriterion(oidcProviderMetadataCtx.getProviderInformation()));
+        } else {
+            log.debug("{} OIDCProviderMetadataContext is absent", getLogPrefix());
+        }
+        
+        // Add any static credentials from the RP context. If any resolver supports it
+        final RelyingPartyContext rpCtx = 
+                relyingPartyContextLookupStrategy.apply(profileRequestContext);     
+        if (rpCtx != null && rpCtx.getConfiguration() != null &&
+                rpCtx.getProfileConfig() instanceof OIDCAuthorizationConfiguration) {
+            final OIDCAuthorizationConfiguration profileConfiguration = 
+                    (OIDCAuthorizationConfiguration) rpCtx.getProfileConfig();
+            
+            if (profileConfiguration != null) {
+                final Credential credential = profileConfiguration.getClientCredential(profileRequestContext);
+                if (credential != null) {
+                    criteria.add(new StaticCredentialCriterion(credential));
+                } else {
+                    log.trace("{} No credential found from the profile configuration", getLogPrefix());
+                }
+            } else {
+                log.trace("{} Profile configuration not available, "
+                        + "shared secret direct encryption credential not present", getLogPrefix());
+            }
+        }
+        return criteria;
+    }
+
+}

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


More information about the commits mailing list