[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