[java-idp-oidc] branch main updated: JOIDC-21 - Use token authentication for OIDC dynamic client registration
Scott Cantor
cantor.2 at osu.edu
Tue Mar 15 12:28:35 UTC 2022
This is an automated email from the git hooks/post-receive script.
scantor 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=a6306e74bea2103be443f664e3725977008db7ea
The following commit(s) were added to refs/heads/main by this push:
new a6306e74 JOIDC-21 - Use token authentication for OIDC dynamic client registration
a6306e74 is described below
commit a6306e74bea2103be443f664e3725977008db7ea
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Tue Mar 15 08:21:55 2022 -0400
JOIDC-21 - Use token authentication for OIDC dynamic client registration
https://shibboleth.atlassian.net/browse/JOIDC-21
Modify parameter names.
Allow either of token policy or ID to be absent.
Support client ID specification and replacement of registration.
---
.../op/token/support/RegistrationClaimsSet.java | 38 +--
.../cli/IssueRegistrationAccessTokenArguments.java | 122 +++++---
...nitializeRegistrationMetadataPolicyContext.java | 20 +-
.../profile/impl/IssueRegistrationAccessToken.java | 338 ++++++++++++++++-----
.../op/profile/impl/StoreClientInformation.java | 8 +-
.../impl/ValidateRegistrationAccessToken.java | 22 +-
.../impl/ValidateRegistrationRequestMetadata.java | 24 +-
...efaultMetadataPolicyCriteriaLookupFunction.java | 13 +-
...efaultMetadataPolicyLocationLookupFunction.java | 57 ----
.../DefaultMetadataPolicyMergingStrategy.java | 18 +-
...ultRegistrationTokenLifetimeLookupFunction.java | 62 ----
...tRegistrationTokenOnetimeUseLookupFunction.java | 56 ----
...istrationTokenRelyingPartyIdLookupFunction.java | 55 ----
.../issue-registration-access-token-beans.xml | 15 +-
.../issue-registration-access-token-flow.xml | 7 +-
.../idp/flows/oidc/register/register-beans.xml | 15 +-
.../javax.servlet.ServletContainerInitializer | 2 +-
.../flow/IssueRegistrationAccessTokenFlowTest.java | 4 +-
.../oidc/op/profile/flow/RegistrationFlowTest.java | 12 +-
.../impl/IssueRegistrationAccessTokenTest.java | 101 +++++-
20 files changed, 527 insertions(+), 462 deletions(-)
diff --git a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/token/support/RegistrationClaimsSet.java b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/token/support/RegistrationClaimsSet.java
index 2c732127..b197ca33 100644
--- a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/token/support/RegistrationClaimsSet.java
+++ b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/token/support/RegistrationClaimsSet.java
@@ -93,9 +93,9 @@ public final class RegistrationClaimsSet {
@JsonProperty("client_id")
@Nullable @NotEmpty private String clientId;
- /** Flag to signal one-time use of the token. */
- @JsonProperty("onetime")
- @Nullable private Boolean onetime;
+ /** Flag to signal replacement is allowed. */
+ @JsonProperty("replacement")
+ @Nullable private Boolean replacement;
/**
* Constructor.
@@ -346,21 +346,21 @@ public final class RegistrationClaimsSet {
}
/**
- * Get the flag to signal one-time use of the token.
+ * Get the flag to signal replacement is allowed.
*
- * @return The flag to signal one-time use of the token.
+ * @return true iff replacement is allowed
*/
- public boolean isOnetime() {
- return onetime == null ? false : onetime.booleanValue();
+ public boolean isReplacement() {
+ return replacement == null ? false : replacement.booleanValue();
}
/**
- * Set the flag to signal one-time use of the token.
+ * Set the flag to signal replacement is allowed.
*
- * @param flag What to set.
+ * @param flag flag to set
*/
- public void setOnetime(@Nullable final Boolean flag) {
- onetime = flag;
+ public void setReplacement(@Nullable final Boolean flag) {
+ replacement = flag;
}
/**
@@ -401,8 +401,8 @@ public final class RegistrationClaimsSet {
/** Client identifier. */
@Nullable @NotEmpty private String clientId;
- /** Flag to signal one-time use of the token. */
- @Nullable private Boolean onetime;
+ /** Flag to signal replacement use of the token. */
+ @Nullable private Boolean replacement;
/**
* Constructor.
@@ -515,12 +515,12 @@ public final class RegistrationClaimsSet {
}
/**
- * Set the flag to signal one-time use of the token.
- * @param flag What to set.
- * @return The builder instance.
+ * Set the flag to signal replacement use of the token.
+ * @param flag What to set
+ * @return The builder instance
*/
- public Builder withOnetime(@Nullable final Boolean flag) {
- onetime = flag;
+ public Builder withReplacement(@Nullable final Boolean flag) {
+ replacement = flag;
return this;
}
@@ -541,7 +541,7 @@ public final class RegistrationClaimsSet {
claimsSet.setMetadata(metadata);
claimsSet.setRelyingPartyId(relyingPartyId);
claimsSet.setClientId(clientId);
- claimsSet.setOnetime(onetime);
+ claimsSet.setReplacement(replacement);
return claimsSet;
}
}
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/cli/IssueRegistrationAccessTokenArguments.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/cli/IssueRegistrationAccessTokenArguments.java
index 25367534..63c7d81d 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/cli/IssueRegistrationAccessTokenArguments.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/cli/IssueRegistrationAccessTokenArguments.java
@@ -34,60 +34,57 @@ import net.shibboleth.utilities.java.support.primitive.StringSupport;
public class IssueRegistrationAccessTokenArguments extends AbstractCommandLineArguments {
/** The URL parameter name for the metadata policy location. */
- public static final String URL_PARAM_METADATA_POLICY_LOCATION = "metadataPolicyLocation";
+ public static final String URL_PARAM_POLICY_LOCATION = "policyLocation";
/** The URL parameter name for the access token lifetime. */
public static final String URL_PARAM_LIFETIME = "tokenLifetime";
/** The URL parameter name for the relying party identifier. */
- public static final String URL_PARAM_RELYING_PARTY_ID = "relyingPartyId";
-
- /** The URL parameter name for the one-time flag. */
- public static final String URL_PARAM_ONE_TIME_TOKEN = "onetime";
+ public static final String URL_PARAM_POLICY_ID = "policyId";
+
+ /** The URL parameter name for the client identifier. */
+ public static final String URL_PARAM_CLIENT_ID = "clientId";
- /** Metadata policy for the requested OIDC dynamic client registration metadata. */
- @Parameter(names = {"-m", "--metadataPolicyLocation"}, required = true, description = "Metadata policy location")
- @Nullable private String metadata;
+ /** The URL parameter name for the replacement flag. */
+ public static final String URL_PARAM_REPLACEMENT = "replacement";
+
+ /** Metadata policy to embed in the token. */
+ @Parameter(names = {"-loc", "--policyLocation"}, required = false, description = "Metadata policy location")
+ @Nullable private String policyLocation;
/** Lifetime for the access token to be issued. */
- @Parameter(names = {"-l", "--lifetime"}, required = true, description = "Lifetime for the access token")
+ @Parameter(names = {"-l", "--lifetime"}, required = false, description = "Lifetime for the access token")
@Nullable private String lifetime;
- /** Relying party identifier for the access token to be issued. */
- @Parameter(names = {"-i", "--relyingPartyId"}, required = true, description = "Relying party ID for the access token")
- @Nullable private String relyingPartyId;
-
+ /** Metadata policy identifier for the access token to be issued. */
+ @Parameter(names = {"-id", "--policyId"}, required = false, description = "Metadata policy ID")
+ @Nullable private String policyId;
+
+ /** Requested client identifier for the access token to be issued. */
+ @Parameter(names = {"-c", "--clientId"}, required = false, description = "Client ID to request in the access token")
+ @Nullable private String clientId;
+
/** Flag to signal one-time use of the token. */
- @Parameter(names = {"-o", "--onetime"}, required = false, description = "Flag to signal one-time use of the token")
- @Nullable private String onetime;
+ @Parameter(names = {"-r", "--replacement"}, required = false,
+ description = "Flag to request the ability to re-register the same client ID for the life of the token")
+ @Nullable private boolean replacement;
/** Username to be used in the HTTP-Basic authentication. */
- @Parameter(names = {"-user", "--username"}, required = false, description = "Username to be used in HTTP-Basic Auth")
+ @Parameter(names = {"-u", "--username"}, required = false, description = "Username to be used in HTTP-Basic Auth")
@Nullable private String username;
/** Password to be used in the HTTP-Basic authentication. */
- @Parameter(names = {"-pwd", "--password"}, required = false, password = true,
+ @Parameter(names = {"-p", "--password"}, required = false, password = true,
description = "Password to be used in HTTP-Basic Auth")
@Nullable private String password;
-
- /**
- * Constructor.
- */
- public IssueRegistrationAccessTokenArguments() {
- onetime = "true";
- }
/** {@inheritDoc} */
@Override
public void validate() {
- if (metadata == null) {
- throw new IllegalArgumentException("No metadata spefified");
- }
- if (lifetime == null) {
- throw new IllegalArgumentException("No lifetime specified");
- }
- if (relyingPartyId == null) {
- throw new IllegalArgumentException("No relyingPartyId specified");
+ if (policyLocation == null && policyId == null) {
+ throw new IllegalArgumentException("One of policyLocation or policyId is required");
+ } else if (clientId == null && replacement) {
+ throw new IllegalArgumentException("Enabling replacement requires specifying a client ID");
}
}
@@ -99,26 +96,57 @@ public class IssueRegistrationAccessTokenArguments extends AbstractCommandLineAr
builder.append("/profile/admin/oidc/issue-registration-access-token");
}
- if (builder.toString().contains("?")) {
- builder.append('&');
- } else {
- builder.append('?');
- }
-
try {
- builder
- .append(URL_PARAM_LIFETIME + "=")
- .append(URLEncoder.encode(lifetime, "UTF-8"))
- .append("&" + URL_PARAM_METADATA_POLICY_LOCATION + "=")
- .append(URLEncoder.encode(metadata, "UTF-8"))
- .append("&" + URL_PARAM_RELYING_PARTY_ID + "=")
- .append(URLEncoder.encode(relyingPartyId, "UTF-8"))
- .append("&" + URL_PARAM_ONE_TIME_TOKEN + "=" + "true".equalsIgnoreCase(onetime));
+ if (policyLocation != null) {
+ appendSeparator(builder)
+ .append(URL_PARAM_POLICY_LOCATION + "=")
+ .append(URLEncoder.encode(policyLocation, "UTF-8"));
+ }
+
+ if (policyId != null) {
+ appendSeparator(builder)
+ .append(URL_PARAM_POLICY_ID + "=")
+ .append(URLEncoder.encode(policyId, "UTF-8"));
+ }
+
+ if (lifetime != null) {
+ builder
+ .append("&" + URL_PARAM_LIFETIME + "=")
+ .append(URLEncoder.encode(lifetime, "UTF-8"));
+ }
+
+ if (clientId != null) {
+ builder
+ .append("&" + URL_PARAM_CLIENT_ID + "=")
+ .append(URLEncoder.encode(clientId, "UTF-8"));
+
+ if (replacement) {
+ builder.append("&" + URL_PARAM_REPLACEMENT + "=" + replacement);
+ }
+ }
} catch (final UnsupportedEncodingException e) {
// UTF-8 is a required encoding.
throw new RuntimeException("URL encoding failed", e);
}
+ return builder;
+ }
+
+ /**
+ * Append the proper parameter separator to the builder.
+ *
+ * @param builder input builder
+ *
+ * @return the input
+ */
+ @Nonnull private StringBuilder appendSeparator(@Nonnull final StringBuilder builder) {
+
+ if (builder.toString().contains("?")) {
+ builder.append('&');
+ } else {
+ builder.append('?');
+ }
+
return builder;
}
@@ -134,4 +162,4 @@ public class IssueRegistrationAccessTokenArguments extends AbstractCommandLineAr
final String rawHeader = username + ":" + password;
return "Basic " + Base64.getEncoder().encodeToString(rawHeader.getBytes(StandardCharsets.UTF_8));
}
-}
\ No newline at end of file
+}
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/InitializeRegistrationMetadataPolicyContext.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/InitializeRegistrationMetadataPolicyContext.java
index e16faf96..29c97a6e 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/InitializeRegistrationMetadataPolicyContext.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/InitializeRegistrationMetadataPolicyContext.java
@@ -18,6 +18,7 @@
package net.shibboleth.idp.plugin.oidc.op.profile.impl;
import java.util.Map;
+import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;
@@ -67,7 +68,7 @@ public class InitializeRegistrationMetadataPolicyContext extends AbstractProfile
registrationPolicyContextCreationStrategy;
/** The strategy used for merging profile and token based metadata policies. */
- @NonnullAfterInit private Function<Pair<Map<String, MetadataPolicy>, Map<String, MetadataPolicy>>,
+ @NonnullAfterInit private BiFunction<Map<String, MetadataPolicy>, Map<String, MetadataPolicy>,
Pair<Map<String, MetadataPolicy>, Boolean>> metadataPolicyMergingStrategy;
/** The strategy used for validating token and merged metadata policies. */
@@ -133,8 +134,8 @@ public class InitializeRegistrationMetadataPolicyContext extends AbstractProfile
*
* @param strategy What to set.
*/
- public void setMetadataPolicyMergingStrategy(@Nonnull final Function<Pair<Map<String, MetadataPolicy>,
- Map<String, MetadataPolicy>>, Pair<Map<String, MetadataPolicy>, Boolean>> strategy) {
+ public void setMetadataPolicyMergingStrategy(@Nonnull final BiFunction<Map<String,MetadataPolicy>,
+ Map<String,MetadataPolicy>, Pair<Map<String, MetadataPolicy>, Boolean>> strategy) {
ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
metadataPolicyMergingStrategy = Constraint.isNotNull(strategy,
@@ -162,7 +163,7 @@ public class InitializeRegistrationMetadataPolicyContext extends AbstractProfile
metadataPolicyContext = registrationPolicyContextCreationStrategy.apply(profileRequestContext);
if (metadataPolicyContext == null) {
- log.error("{} The registration metadata policy context could not be created, invalid profile context",
+ log.error("{} Registration metadata policy context could not be created, invalid profile context",
getLogPrefix());
ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
return false;
@@ -182,27 +183,28 @@ public class InitializeRegistrationMetadataPolicyContext extends AbstractProfile
tokenMetadataPolicyLookupStrategy.apply(profileRequestContext);
if (!metadataPolicyValidationStrategy.test(tokenMetadataPolicy)) {
- log.warn("{} The token metadata policy is not valid", getLogPrefix());
+ log.warn("{} Metadata policy in token is invalid", getLogPrefix());
ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MESSAGE);
return;
}
final Pair<Map<String, MetadataPolicy>, Boolean> mergedResult =
- metadataPolicyMergingStrategy.apply(new Pair<>(profileMetadataPolicy, tokenMetadataPolicy));
+ metadataPolicyMergingStrategy.apply(profileMetadataPolicy, tokenMetadataPolicy);
if (!mergedResult.getSecond()) {
- log.warn("{} The metadata policies from profile and token could not be merged", getLogPrefix());
+ log.warn("{} Metadata policies from profile and token could not be merged", getLogPrefix());
ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MESSAGE);
return;
}
final Map<String, MetadataPolicy> mergedPolicy = mergedResult.getFirst();
if (!metadataPolicyValidationStrategy.test(mergedPolicy)) {
- log.warn("{} The merged metadata policy is not valid", getLogPrefix());
+ log.warn("{} Merged metadata policy is invalid", getLogPrefix());
ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MESSAGE);
return;
}
metadataPolicyContext.setMetadataPolicy(mergedPolicy);
}
-}
+
+}
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/IssueRegistrationAccessToken.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/IssueRegistrationAccessToken.java
index deb8e141..4f50cc88 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/IssueRegistrationAccessToken.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/IssueRegistrationAccessToken.java
@@ -19,6 +19,7 @@ package net.shibboleth.idp.plugin.oidc.op.profile.impl;
import java.time.Duration;
import java.time.Instant;
+import java.time.format.DateTimeParseException;
import java.util.Map;
import java.util.function.Function;
@@ -31,7 +32,6 @@ import org.opensaml.profile.action.EventIds;
import org.opensaml.profile.context.ProfileRequestContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.springframework.webflow.execution.RequestContext;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -41,19 +41,22 @@ import com.nimbusds.oauth2.sdk.token.AccessToken;
import com.nimbusds.oauth2.sdk.token.BearerAccessToken;
import com.nimbusds.oauth2.sdk.token.Tokens;
-import net.shibboleth.idp.plugin.oidc.op.profile.logic.DefaultRegistrationTokenLifetimeLookupFunction;
-import net.shibboleth.idp.plugin.oidc.op.profile.logic.DefaultRegistrationTokenOnetimeUseLookupFunction;
-import net.shibboleth.idp.plugin.oidc.op.profile.logic.DefaultRegistrationTokenRelyingPartyIdLookupFunction;
+import net.shibboleth.idp.authn.context.AuthenticationContext;
+import net.shibboleth.idp.authn.context.SubjectContext;
+import net.shibboleth.idp.plugin.oidc.op.cli.IssueRegistrationAccessTokenArguments;
import net.shibboleth.idp.plugin.oidc.op.token.support.RegistrationClaimsSet;
import net.shibboleth.idp.profile.AbstractProfileAction;
-import net.shibboleth.idp.profile.context.SpringRequestContext;
import net.shibboleth.idp.profile.context.navigate.ResponderIdLookupFunction;
+import net.shibboleth.idp.profile.function.SpringFlowScopeLookupFunction;
import net.shibboleth.oidc.metadata.policy.MetadataPolicy;
import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
+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.logic.FunctionSupport;
+import net.shibboleth.utilities.java.support.primitive.StringSupport;
+import net.shibboleth.utilities.java.support.security.AccessControlService;
import net.shibboleth.utilities.java.support.security.DataSealer;
import net.shibboleth.utilities.java.support.security.DataSealerException;
import net.shibboleth.utilities.java.support.security.IdentifierGenerationStrategy;
@@ -79,25 +82,43 @@ public class IssueRegistrationAccessToken extends AbstractProfileAction {
@NonnullAfterInit private DataSealer dataSealer;
/** Strategy used to locate the {@link IdentifierGenerationStrategy} to use. */
- @Nonnull private Function<ProfileRequestContext, IdentifierGenerationStrategy> idGeneratorLookupStrategy;
+ @Nonnull private Function<ProfileRequestContext,IdentifierGenerationStrategy> idGeneratorLookupStrategy;
/** JSON object mapper. */
@NonnullAfterInit private ObjectMapper objectMapper;
-
+
+ /** Access control service. */
+ @NonnullAfterInit private AccessControlService accessControlService;
+
+ /** Name of access control policy governing policyLocation acceptance. */
+ @Nullable @NotEmpty private String policyLocationPolicyName;
+
+ /** Name of access control policy governing policyId acceptance. */
+ @Nullable @NotEmpty private String policyIdPolicyName;
+
+ /** Name of access control policy governing clientId acceptance. */
+ @Nullable @NotEmpty private String clientIdPolicyName;
+
/** Lookup function for the metadata policy. */
- @NonnullAfterInit private Function<ProfileRequestContext, Map<String,MetadataPolicy>> metadataPolicyLookupStrategy;
+ @NonnullAfterInit private Function<ProfileRequestContext,Map<String,MetadataPolicy>> metadataPolicyLookupStrategy;
/** Lookup function for the token lifetime. */
- @Nonnull private Function<ProfileRequestContext, Duration> tokenLifetimeLookupStrategy;
+ @Nonnull private Function<ProfileRequestContext,String> tokenLifetimeLookupStrategy;
/** Lookup function for the token issuer. */
- @NonnullAfterInit private Function<ProfileRequestContext, String> issuerLookupStrategy;
-
- /** Lookup function for the relying party identifier. */
- @Nonnull private Function<ProfileRequestContext, String> relyingPartyIdLookupStrategy;
-
- /** Lookup function for the flag signaling one-time use of the token. */
- @Nonnull private Function<ProfileRequestContext, Boolean> onetimeUseLookupStrategy;
+ @NonnullAfterInit private Function<ProfileRequestContext,String> issuerLookupStrategy;
+
+ /** Lookup function for the policy location. */
+ @Nonnull private Function<ProfileRequestContext,String> policyLocationLookupStrategy;
+
+ /** Lookup function for the policy identifier. */
+ @Nonnull private Function<ProfileRequestContext,String> policyIdLookupStrategy;
+
+ /** Lookup function for the client identifier. */
+ @Nonnull private Function<ProfileRequestContext,String> clientIdLookupStrategy;
+
+ /** Lookup function for the flag signaling replacement use of the token. */
+ @Nonnull private Function<ProfileRequestContext,String> replacementLookupStrategy;
/** The identifier generator to use. */
@Nullable private IdentifierGenerationStrategy idGenerator;
@@ -108,11 +129,20 @@ public class IssueRegistrationAccessToken extends AbstractProfileAction {
/** The token issuer. */
@Nonnull private String issuer;
- /** The relying party identifier. */
- @Nullable private String relyingPartyId;
+ /** The policy location. */
+ @Nullable private String policyLocation;
+
+ /** The policy identifier. */
+ @Nullable private String policyId;
+
+ /** The client identifier. */
+ @Nullable private String clientId;
+
+ /** The token lifetime. */
+ @Nullable private Duration defaultTokenLifetime;
/** The token lifetime. */
- private Duration tokenLifetime;
+ @Nullable private Duration tokenLifetime;
/**
* Constructor.
@@ -120,9 +150,18 @@ public class IssueRegistrationAccessToken extends AbstractProfileAction {
public IssueRegistrationAccessToken() {
idGeneratorLookupStrategy = FunctionSupport.constant(new SecureRandomIdentifierGenerationStrategy());
issuerLookupStrategy = new ResponderIdLookupFunction();
- tokenLifetimeLookupStrategy = new DefaultRegistrationTokenLifetimeLookupFunction();
- relyingPartyIdLookupStrategy = new DefaultRegistrationTokenRelyingPartyIdLookupFunction();
- onetimeUseLookupStrategy = new DefaultRegistrationTokenOnetimeUseLookupFunction();
+ tokenLifetimeLookupStrategy =
+ new SpringFlowScopeLookupFunction(IssueRegistrationAccessTokenArguments.URL_PARAM_LIFETIME);
+ policyLocationLookupStrategy =
+ new SpringFlowScopeLookupFunction(IssueRegistrationAccessTokenArguments.URL_PARAM_POLICY_LOCATION);
+ policyIdLookupStrategy =
+ new SpringFlowScopeLookupFunction(IssueRegistrationAccessTokenArguments.URL_PARAM_POLICY_ID);
+ clientIdLookupStrategy =
+ new SpringFlowScopeLookupFunction(IssueRegistrationAccessTokenArguments.URL_PARAM_CLIENT_ID);
+ replacementLookupStrategy =
+ new SpringFlowScopeLookupFunction(IssueRegistrationAccessTokenArguments.URL_PARAM_REPLACEMENT);
+
+ defaultTokenLifetime = Duration.ofDays(1);
}
/**
@@ -147,19 +186,41 @@ public class IssueRegistrationAccessToken extends AbstractProfileAction {
objectMapper = Constraint.isNotNull(mapper, "Object mapper cannot be null");
}
+ /**
+ * Set the {@link AccessControlService} to use.
+ *
+ * @param acs service to use
+ */
+ public void setAccessControlService(@Nonnull final AccessControlService acs) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+ accessControlService = Constraint.isNotNull(acs, "AccessControlService cannot be null");
+ }
+
/**
* Set the strategy used to locate the {@link IdentifierGenerationStrategy} to use.
*
* @param strategy lookup strategy
*/
public void setIdentifierGeneratorLookupStrategy(
- @Nonnull final Function<ProfileRequestContext, IdentifierGenerationStrategy> strategy) {
+ @Nonnull final Function<ProfileRequestContext,IdentifierGenerationStrategy> strategy) {
ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
idGeneratorLookupStrategy =
Constraint.isNotNull(strategy, "IdentifierGenerationStrategy lookup strategy cannot be null");
}
+ /**
+ * Set a lookup strategy for the token issuer.
+ *
+ * @param strategy lookup strategy
+ */
+ public void setIssuerLookupStrategy(@Nonnull final Function<ProfileRequestContext,String> strategy) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+ issuerLookupStrategy = Constraint.isNotNull(strategy, "Issuer lookup strategy cannot be null");
+ }
+
/**
* Set a lookup strategy for the metadata policy.
*
@@ -178,21 +239,21 @@ public class IssueRegistrationAccessToken extends AbstractProfileAction {
*
* @param strategy lookup strategy
*/
- public void setTokenLifetimeLookupStrategy(@Nonnull final Function<ProfileRequestContext, Duration> strategy) {
+ public void setTokenLifetimeLookupStrategy(@Nonnull final Function<ProfileRequestContext,String> strategy) {
ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
tokenLifetimeLookupStrategy = Constraint.isNotNull(strategy, "Token lifetime lookup strategy cannot be null");
}
/**
- * Set a lookup strategy for the token issuer.
+ * Set a lookup strategy for the metadata policy location.
*
* @param strategy lookup strategy
*/
- public void setIssuerLookupStrategy(@Nonnull final Function<ProfileRequestContext, String> strategy) {
+ public void setPolicyLocationLookupStrategy(@Nonnull final Function<ProfileRequestContext,String> strategy) {
ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
- issuerLookupStrategy = Constraint.isNotNull(strategy, "Issuer lookup strategy cannot be null");
+ policyLocationLookupStrategy = Constraint.isNotNull(strategy, "Policy location lookup strategy cannot be null");
}
/**
@@ -200,22 +261,76 @@ public class IssueRegistrationAccessToken extends AbstractProfileAction {
*
* @param strategy lookup strategy
*/
- public void setRelyingPartyIdLookupStrategy(@Nonnull final Function<ProfileRequestContext, String> strategy) {
+ public void setPolicyIdLookupStrategy(@Nonnull final Function<ProfileRequestContext,String> strategy) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+ policyIdLookupStrategy = Constraint.isNotNull(strategy, "Policy ID lookup strategy cannot be null");
+ }
+
+ /**
+ * Set a lookup strategy for the client identifier.
+ *
+ * @param strategy lookup strategy
+ */
+ public void setClientIdLookupStrategy(@Nonnull final Function<ProfileRequestContext,String> strategy) {
ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
- relyingPartyIdLookupStrategy = Constraint.isNotNull(strategy,
- "Relying party ID lookup strategy cannot be null");
+ clientIdLookupStrategy = Constraint.isNotNull(strategy, "Client ID lookup strategy cannot be null");
}
/**
- * Set a lookup strategy for the flag signaling one-time use of the token.
+ * Set a lookup strategy for the flag signaling registration replacement is allowed.
*
* @param strategy lookup strategy
*/
- public void setOnetimeUseLookupStrategy(@Nonnull final Function<ProfileRequestContext, Boolean> strategy) {
+ public void setReplacementLookupStrategy(@Nonnull final Function<ProfileRequestContext,String> strategy) {
ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
- onetimeUseLookupStrategy = Constraint.isNotNull(strategy, "One-time use lookup strategy cannot be null");
+ replacementLookupStrategy = Constraint.isNotNull(strategy, "Replacement lookup strategy cannot be null");
+ }
+
+ /**
+ * Set an explicit policy name to apply governing policyLocation usage.
+ *
+ * @param name policy name
+ */
+ public void setPolicyLocationPolicyName(@Nullable @NotEmpty final String name) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+ policyLocationPolicyName = StringSupport.trimOrNull(name);
+ }
+
+ /**
+ * Set an explicit policy name to apply governing policyId usage.
+ *
+ * @param name policy name
+ */
+ public void setPolicyIdPolicyName(@Nullable @NotEmpty final String name) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+ policyIdPolicyName = StringSupport.trimOrNull(name);
+ }
+
+ /**
+ * Set an explicit policy name to apply governing clientId usage.
+ *
+ * @param name policy name
+ */
+ public void setClientIdPolicyName(@Nullable @NotEmpty final String name) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+ clientIdPolicyName = StringSupport.trimOrNull(name);
+ }
+
+ /**
+ * Set the default token lifetime.
+ *
+ * @param lifetime
+ */
+ public void setDefaultTokenLifetime(@Nonnull final Duration lifetime) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+ defaultTokenLifetime = Constraint.isNotNull(lifetime, "Default token lifetime cannot be null");
}
/** {@inheritDoc} */
@@ -231,31 +346,23 @@ public class IssueRegistrationAccessToken extends AbstractProfileAction {
throw new ComponentInitializationException("ObjectMapper cannot be null");
}
+ if (accessControlService == null) {
+ throw new ComponentInitializationException("AccessControlService cannot be null");
+ }
+
if (metadataPolicyLookupStrategy == null) {
throw new ComponentInitializationException("Metadata policy lookup strategy cannot be null");
}
}
/** {@inheritDoc} */
- @Override protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
+ @Override
+ protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
if (!super.doPreExecute(profileRequestContext)) {
return false;
}
- final SpringRequestContext springRequestContext =
- profileRequestContext.getSubcontext(SpringRequestContext.class);
- if (springRequestContext == null) {
- log.warn("{} Spring request context not found in profile request context", getLogPrefix());
- return false;
- }
-
- final RequestContext requestContext = springRequestContext.getRequestContext();
- if (requestContext == null) {
- log.warn("{} Web Flow request context not found in Spring request context", getLogPrefix());
- return false;
- }
-
idGenerator = idGeneratorLookupStrategy.apply(profileRequestContext);
if (idGenerator == null) {
log.error("{} No identifier generation strategy", getLogPrefix());
@@ -263,57 +370,71 @@ public class IssueRegistrationAccessToken extends AbstractProfileAction {
return false;
}
- metadataPolicy = metadataPolicyLookupStrategy.apply(profileRequestContext);
- // null is not allowed - empty metadata policy is
- if (metadataPolicy == null) {
- log.warn("{} No metadata policy could be resolved", getLogPrefix());
- ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MESSAGE);
- return false;
- }
-
issuer = issuerLookupStrategy.apply(profileRequestContext);
if (issuer == null) {
log.warn("{} No issuer could be resolved", getLogPrefix());
ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MESSAGE);
return false;
}
-
- relyingPartyId = relyingPartyIdLookupStrategy.apply(profileRequestContext);
- if (relyingPartyId == null) {
- log.warn("{} No relying party ID could be resolved", getLogPrefix());
+
+ policyLocation = policyLocationLookupStrategy.apply(profileRequestContext);
+ policyId = policyIdLookupStrategy.apply(profileRequestContext);
+ metadataPolicy = metadataPolicyLookupStrategy.apply(profileRequestContext);
+ if (metadataPolicy == null && policyId == null) {
+ log.warn("{} No metadata policy or policy ID could be resolved", getLogPrefix());
ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MESSAGE);
return false;
}
- tokenLifetime = tokenLifetimeLookupStrategy.apply(profileRequestContext);
- if (tokenLifetime == null) {
- log.warn("{} No token lifetime could be resolved", getLogPrefix());
- ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MESSAGE);
- return false;
+ clientId = clientIdLookupStrategy.apply(profileRequestContext);
+
+ final String lifetimeString = tokenLifetimeLookupStrategy.apply(profileRequestContext);
+ if (lifetimeString != null) {
+ try {
+ tokenLifetime = Duration.parse(lifetimeString);
+ if (tokenLifetime.compareTo(defaultTokenLifetime) > 0) {
+ log.warn("Requested token lifetime greater than default, lowering to default", getLogPrefix());
+ tokenLifetime = defaultTokenLifetime;
+ }
+ } catch (final DateTimeParseException e) {
+ log.warn("{} Token lifetime was not in a supported format", getLogPrefix(), e);
+ }
+ } else {
+ log.debug("{} No token lifetime specified, using default", getLogPrefix());
+ tokenLifetime = defaultTokenLifetime;
}
return true;
}
-
+
/** {@inheritDoc} */
- @Override protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
+ @Override
+ protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
+
+ if (!checkAccess(profileRequestContext)) {
+ return;
+ }
final String id = idGenerator.generateIdentifier();
final Instant now = Instant.now();
final Instant exp = now.plus(tokenLifetime);
- final RegistrationClaimsSet claimsSet = new RegistrationClaimsSet.Builder(id)
+ final RegistrationClaimsSet.Builder builder = new RegistrationClaimsSet.Builder(id)
.withIssuer(issuer)
.withIssuedAt(now)
- .withMetadata(metadataPolicy)
.withExpiration(exp)
- .withRelyingPartyId(relyingPartyId)
- .withOnetime(onetimeUseLookupStrategy.apply(profileRequestContext))
- .build();
+ .withMetadata(metadataPolicy)
+ .withRelyingPartyId(policyId);
+
+ if (clientId != null) {
+ builder.withClientId(clientId)
+ .withReplacement(Boolean.valueOf(replacementLookupStrategy.apply(profileRequestContext)));
+ }
+
+ addAuthenticationClaims(profileRequestContext, builder);
- //TODO: possible end-user authentication claims:
- //via Builder: .withAcr .withAuthTime .withPrincipal
+ final RegistrationClaimsSet claimsSet = builder.build();
final AccessToken accessToken;
@@ -339,4 +460,73 @@ public class IssueRegistrationAccessToken extends AbstractProfileAction {
profileRequestContext.setOutboundMessageContext(mc);
}
-}
+ /**
+ * Check access policies.
+ *
+ * @param profileRequestContext current profile request context
+ *
+ * @return true iff checks pass
+ */
+ private boolean checkAccess(@Nonnull final ProfileRequestContext profileRequestContext) {
+ if (policyId != null) {
+ if (policyIdPolicyName == null) {
+ log.warn("{} No policy name govering policyId usage, disallowing access", getLogPrefix());
+ ActionSupport.buildEvent(profileRequestContext, EventIds.ACCESS_DENIED);
+ return false;
+ } else if (!accessControlService.getInstance(policyIdPolicyName).checkAccess(getHttpServletRequest(),
+ "read", policyId)) {
+ ActionSupport.buildEvent(profileRequestContext, EventIds.ACCESS_DENIED);
+ return false;
+ }
+ }
+
+ if (policyLocation != null) {
+ if (policyLocationPolicyName == null) {
+ log.warn("{} No policy name govering policyLocation usage, disallowing access", getLogPrefix());
+ ActionSupport.buildEvent(profileRequestContext, EventIds.ACCESS_DENIED);
+ return false;
+ } else if (!accessControlService.getInstance(policyLocationPolicyName).checkAccess(getHttpServletRequest(),
+ "read", policyLocation)) {
+ ActionSupport.buildEvent(profileRequestContext, EventIds.ACCESS_DENIED);
+ return false;
+ }
+ }
+
+ if (clientId != null) {
+ if (clientIdPolicyName == null) {
+ log.warn("{} No policy name govering clientId usage, disallowing access", getLogPrefix());
+ ActionSupport.buildEvent(profileRequestContext, EventIds.ACCESS_DENIED);
+ return false;
+ } else if (!accessControlService.getInstance(clientIdPolicyName).checkAccess(getHttpServletRequest(),
+ "write", clientId)) {
+ ActionSupport.buildEvent(profileRequestContext, EventIds.ACCESS_DENIED);
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Decorate the token with authentication-related claims.
+ *
+ * @param profileRequestContext profile request context
+ * @param builder claims set builder
+ */
+ private void addAuthenticationClaims(@Nonnull final ProfileRequestContext profileRequestContext,
+ @Nonnull final RegistrationClaimsSet.Builder builder) {
+
+ final AuthenticationContext authnContext = profileRequestContext.getSubcontext(AuthenticationContext.class);
+ if (authnContext != null) {
+ if (authnContext.getAuthenticationResult() != null) {
+ builder.withAuthTime(authnContext.getAuthenticationResult().getAuthenticationInstant());
+ }
+ }
+
+ final SubjectContext subjectContext = profileRequestContext.getSubcontext(SubjectContext.class);
+ if (subjectContext != null && subjectContext.getPrincipalName() != null) {
+ builder.withPrincipal(subjectContext.getPrincipalName());
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/StoreClientInformation.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/StoreClientInformation.java
index 70d9f9dc..a60d2949 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/StoreClientInformation.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/StoreClientInformation.java
@@ -190,12 +190,8 @@ public class StoreClientInformation extends AbstractProfileAction {
Duration lifetime = registrationValidityPeriodStrategy != null ?
registrationValidityPeriodStrategy.apply(profileRequestContext) : null;
- final boolean replace;
- if (registrationTokenCtx != null) {
- replace = !registrationTokenCtx.getClaimsSet().isOnetime();
- } else {
- replace = false;
- }
+ final boolean replace = registrationTokenCtx != null ?
+ registrationTokenCtx.getClaimsSet().isReplacement() : false;
log.debug("{} Storing client information (replace = {})", getLogPrefix(), replace);
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateRegistrationAccessToken.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateRegistrationAccessToken.java
index e30f802f..df89ceae 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateRegistrationAccessToken.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateRegistrationAccessToken.java
@@ -21,6 +21,7 @@ import java.time.Instant;
import java.util.function.Function;
import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
import org.opensaml.messaging.context.navigate.ChildContextLookup;
import org.opensaml.profile.action.ActionSupport;
@@ -41,6 +42,7 @@ import net.shibboleth.idp.plugin.oidc.op.token.support.RegistrationClaimsSet;
import net.shibboleth.idp.profile.context.RelyingPartyContext;
import net.shibboleth.oidc.profile.core.OidcEventIds;
import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
+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;
@@ -74,10 +76,10 @@ public class ValidateRegistrationAccessToken extends AbstractOIDCRequestAction<O
@NonnullAfterInit private ObjectMapper objectMapper;
/** The relying party context to operate on. */
- private RelyingPartyContext relyingPartyContext;
+ @Nullable private RelyingPartyContext relyingPartyContext;
/** The registration access token to be validated. */
- private String accessToken;
+ @Nullable @NotEmpty private String accessToken;
/**
* Constructor.
@@ -203,7 +205,7 @@ public class ValidateRegistrationAccessToken extends AbstractOIDCRequestAction<O
return;
}
- log.debug("{} registration access token decoded into {}", getLogPrefix(), claimsSet);
+ log.debug("{} Registration access token decoded into {}", getLogPrefix(), claimsSet);
if (Instant.now().isAfter(claimsSet.getExpiration())) {
log.error("{} Registration access token exp is in the past {}", getLogPrefix(), claimsSet.getExpiration());
@@ -217,11 +219,14 @@ public class ValidateRegistrationAccessToken extends AbstractOIDCRequestAction<O
return;
}
final String relyingPartyId = claimsSet.getRelyingPartyId();
- if (relyingPartyId == null) {
- log.error("{} Registration access token {} didn't contain relying party identifier", getLogPrefix(),
+ if (relyingPartyId != null) {
+ log.debug("{} Registration access token {} carries relying party identifier {}", getLogPrefix(),
+ claimsSet.getJti(), relyingPartyId);
+ relyingPartyContext.setVerified(true);
+ relyingPartyContext.setRelyingPartyId(relyingPartyId);
+ } else {
+ log.debug("{} Registration access token {} carries no relying party identifier", getLogPrefix(),
claimsSet.getJti());
- ActionSupport.buildEvent(profileRequestContext, OidcEventIds.INVALID_GRANT);
- return;
}
log.debug("{} Registration access token {} successfully validated", getLogPrefix(), claimsSet.getJti());
@@ -236,9 +241,6 @@ public class ValidateRegistrationAccessToken extends AbstractOIDCRequestAction<O
}
registrationClaimsContext.setClaimsSet(claimsSet);
-
- relyingPartyContext.setVerified(true);
- relyingPartyContext.setRelyingPartyId(relyingPartyId);
}
}
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateRegistrationRequestMetadata.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateRegistrationRequestMetadata.java
index 6bd7f14e..50169532 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateRegistrationRequestMetadata.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateRegistrationRequestMetadata.java
@@ -18,6 +18,7 @@
package net.shibboleth.idp.plugin.oidc.op.profile.impl;
import java.util.Map;
+import java.util.function.BiFunction;
import java.util.function.Function;
import javax.annotation.Nonnull;
@@ -52,15 +53,14 @@ import net.shibboleth.utilities.java.support.logic.Constraint;
public class ValidateRegistrationRequestMetadata extends AbstractProfileAction {
/** Class logger. */
- @Nonnull
- private final Logger log = LoggerFactory.getLogger(ValidateRegistrationRequestMetadata.class);
+ @Nonnull private final Logger log = LoggerFactory.getLogger(ValidateRegistrationRequestMetadata.class);
/** Strategy that will return {@link OIDCClientRegistrationMetadataPolicyContext}. */
@Nonnull private Function<MessageContext, OIDCClientRegistrationMetadataPolicyContext>
registrationMetadataPolicyContextLookupStrategy;
/** Function used for enforcing the metadata policy. */
- @Nonnull private Function<Pair<Object, MetadataPolicy>, Pair<Object, Boolean>> metadataPolicyEnforcer;
+ @Nonnull private BiFunction<Object,MetadataPolicy,Pair<Object,Boolean>> metadataPolicyEnforcer;
/** The OIDCClientRegistrationRequest to validate. */
@Nullable private OIDCClientRegistrationRequest request;
@@ -76,7 +76,7 @@ public class ValidateRegistrationRequestMetadata extends AbstractProfileAction {
*/
public ValidateRegistrationRequestMetadata() {
registrationMetadataPolicyContextLookupStrategy =
- new ChildContextLookup<>(OIDCClientRegistrationMetadataPolicyContext.class, false);
+ new ChildContextLookup<>(OIDCClientRegistrationMetadataPolicyContext.class);
metadataPolicyEnforcer = new DefaultMetadataPolicyEnforcer();
}
@@ -100,7 +100,7 @@ public class ValidateRegistrationRequestMetadata extends AbstractProfileAction {
* @param function Function used for enforcing the metadata policy.
*/
public void setMetadataPolicyEnforcer(
- @Nonnull final Function<Pair<Object, MetadataPolicy>, Pair<Object, Boolean>> function) {
+ @Nonnull final BiFunction<Object,MetadataPolicy,Pair<Object,Boolean>> function) {
ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
metadataPolicyEnforcer = Constraint.isNotNull(function, "The metadata policy enforcer cannot be null");
@@ -149,7 +149,7 @@ public class ValidateRegistrationRequestMetadata extends AbstractProfileAction {
registrationMetadataPolicyContext.setPolicyEnforcedMetadata(request.getOIDCClientMetadata());
return;
}
- log.debug("{} Metadata policy used for the request validation: {}", getLogPrefix(), metadataPolicy);
+ log.debug("{} Metadata policy used for request validation: {}", getLogPrefix(), metadataPolicy);
boolean compliant = true;
@@ -158,16 +158,14 @@ public class ValidateRegistrationRequestMetadata extends AbstractProfileAction {
for (final String claim : metadataPolicy.keySet()) {
final MetadataPolicy policy = metadataPolicy.get(claim);
final Object value = requestMetadata.get(claim);
- log.debug("{} The claim {} set in policy included in the request: {}", getLogPrefix(), claim,
+ log.debug("{} Claim {} set in policy included in the request: {}", getLogPrefix(), claim,
value == null);
- final Pair<Object, MetadataPolicy> candidate = new Pair<>(value, policy);
- final Pair<Object, Boolean> result = metadataPolicyEnforcer.apply(candidate);
-
+ final Pair<Object,Boolean> result = metadataPolicyEnforcer.apply(value, policy);
if (!result.getSecond()) {
- log.warn("{} the metadata claim {} is not compliant with the policy", getLogPrefix(), claim);
+ log.warn("{} Metadata claim {} is not compliant with the policy", getLogPrefix(), claim);
compliant = false;
} else {
- log.trace("{} validation result is OK for claim {}", getLogPrefix(), claim);
+ log.trace("{} Validation result is OK for claim {}", getLogPrefix(), claim);
requestMetadata.put(claim, result.getFirst());
}
}
@@ -187,4 +185,4 @@ public class ValidateRegistrationRequestMetadata extends AbstractProfileAction {
}
}
-}
+}
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/logic/DefaultMetadataPolicyCriteriaLookupFunction.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/logic/DefaultMetadataPolicyCriteriaLookupFunction.java
index 1cb076c2..49d1a8a3 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/logic/DefaultMetadataPolicyCriteriaLookupFunction.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/logic/DefaultMetadataPolicyCriteriaLookupFunction.java
@@ -26,14 +26,17 @@ import org.opensaml.profile.context.ProfileRequestContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import net.shibboleth.idp.plugin.oidc.op.cli.IssueRegistrationAccessTokenArguments;
+import net.shibboleth.idp.profile.function.SpringFlowScopeLookupFunction;
import net.shibboleth.oidc.metadata.criterion.ResourceLocationCriterion;
import net.shibboleth.utilities.java.support.primitive.StringSupport;
import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
/**
* A function returning a {@link CriteriaSet} which contains the metadata policy document location as {@link
- * ResourceLocationCriterion}. The value is fetched from the SWF request parameters, using {@link
- * DefaultMetadataPolicyLocationLookupFunction}.
+ * ResourceLocationCriterion}.
+ *
+ * <p>The value is fetched from the SWF flow scope.</p>
*/
public class DefaultMetadataPolicyCriteriaLookupFunction implements Function<ProfileRequestContext, CriteriaSet> {
@@ -43,13 +46,15 @@ public class DefaultMetadataPolicyCriteriaLookupFunction implements Function<Pro
/** {@inheritDoc} */
@Override @Nullable
public CriteriaSet apply(@Nonnull final ProfileRequestContext profileRequestContext) {
- final String location = new DefaultMetadataPolicyLocationLookupFunction().apply(profileRequestContext);
+ final String location = new SpringFlowScopeLookupFunction(
+ IssueRegistrationAccessTokenArguments.URL_PARAM_POLICY_LOCATION).apply(profileRequestContext);
if (StringSupport.trimOrNull(location) == null) {
- log.warn("Could not find the location for building the criteria set, returning null");
+ log.debug("Could not find a policy location for building the criteria set, returning null");
return null;
}
log.trace("Found a location {} to be included in the criteria set", location);
final ResourceLocationCriterion criterion = new ResourceLocationCriterion(location);
return new CriteriaSet(criterion);
}
+
}
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/logic/DefaultMetadataPolicyLocationLookupFunction.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/logic/DefaultMetadataPolicyLocationLookupFunction.java
deleted file mode 100644
index 7275c072..00000000
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/logic/DefaultMetadataPolicyLocationLookupFunction.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * 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.plugin.oidc.op.profile.logic;
-
-import java.util.function.Function;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import org.opensaml.profile.context.ProfileRequestContext;
-import org.springframework.webflow.execution.RequestContext;
-
-import net.shibboleth.idp.plugin.oidc.op.cli.IssueRegistrationAccessTokenArguments;
-import net.shibboleth.idp.profile.context.SpringRequestContext;
-import net.shibboleth.utilities.java.support.primitive.StringSupport;
-
-/**
- * A lookup function that fetches the metadata policy document location from the SWF request parameters.
- *
- * The parameter key is {@link IssueRegistrationAccessTokenArguments#URL_PARAM_METADATA_POLICY_LOCATION}.
- */
-public class DefaultMetadataPolicyLocationLookupFunction implements Function<ProfileRequestContext, String> {
-
- /** {@inheritDoc} */
- @Override @Nullable
- public String apply(@Nonnull final ProfileRequestContext profileRequestContext) {
- final SpringRequestContext springRequestContext =
- profileRequestContext.getSubcontext(SpringRequestContext.class);
- if (springRequestContext == null) {
- return null;
- }
-
- final RequestContext requestContext = springRequestContext.getRequestContext();
- if (requestContext == null) {
- return null;
- }
-
- final String metadataPolicyUrl = (String) requestContext.getFlowScope().get(
- IssueRegistrationAccessTokenArguments.URL_PARAM_METADATA_POLICY_LOCATION);
- return StringSupport.trimOrNull(metadataPolicyUrl);
- }
-}
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/logic/DefaultMetadataPolicyMergingStrategy.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/logic/DefaultMetadataPolicyMergingStrategy.java
index 67d499b0..ca5faa4c 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/logic/DefaultMetadataPolicyMergingStrategy.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/logic/DefaultMetadataPolicyMergingStrategy.java
@@ -20,7 +20,7 @@ package net.shibboleth.idp.plugin.oidc.op.profile.logic;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
-import java.util.function.Function;
+import java.util.function.BiFunction;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -67,21 +67,17 @@ import net.shibboleth.utilities.java.support.logic.ConstraintViolationException;
* in the policies were compliant.
*/
public class DefaultMetadataPolicyMergingStrategy implements
- Function<Pair<Map<String, MetadataPolicy>, Map<String, MetadataPolicy>>,
+ BiFunction<Map<String, MetadataPolicy>, Map<String, MetadataPolicy>,
Pair<Map<String, MetadataPolicy>, Boolean>> {
/** Class logger. */
- @Nonnull
- private final Logger log = LoggerFactory.getLogger(DefaultMetadataPolicyMergingStrategy.class);
+ @Nonnull private final Logger log = LoggerFactory.getLogger(DefaultMetadataPolicyMergingStrategy.class);
/** {@inheritDoc} */
- @Override
@Nonnull
- public Pair<Map<String, MetadataPolicy>, Boolean> apply(
- @Nullable final Pair<Map<String, MetadataPolicy>, Map<String, MetadataPolicy>> input) {
- final Map<String, MetadataPolicy> first = input.getFirst();
- final Map<String, MetadataPolicy> second = input.getSecond();
- if (first == null) {
+ public Pair<Map<String, MetadataPolicy>, Boolean> apply(@Nullable final Map<String,MetadataPolicy> first,
+ @Nullable final Map<String,MetadataPolicy> second) {
+ if (first == null || first.isEmpty()) {
return new Pair<>(second, Boolean.TRUE);
} else if (second == null || second.isEmpty()) {
return new Pair<>(first, Boolean.TRUE);
@@ -101,4 +97,4 @@ public class DefaultMetadataPolicyMergingStrategy implements
return new Pair<>(result, Boolean.valueOf(valid));
}
-}
+}
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/logic/DefaultRegistrationTokenLifetimeLookupFunction.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/logic/DefaultRegistrationTokenLifetimeLookupFunction.java
deleted file mode 100644
index dc8a0fcd..00000000
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/logic/DefaultRegistrationTokenLifetimeLookupFunction.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * 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.plugin.oidc.op.profile.logic;
-
-import java.time.Duration;
-import java.time.format.DateTimeParseException;
-import java.util.function.Function;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import org.opensaml.profile.context.ProfileRequestContext;
-import org.springframework.webflow.execution.RequestContext;
-
-import net.shibboleth.idp.plugin.oidc.op.cli.IssueRegistrationAccessTokenArguments;
-import net.shibboleth.idp.profile.context.SpringRequestContext;
-
-/**
- * A lookup function that fetches the token lifetime from the SWF request parameters.
- *
- * The parameter key is {@link IssueRegistrationAccessTokenArguments#URL_PARAM_LIFETIME}.
- */
-public class DefaultRegistrationTokenLifetimeLookupFunction implements Function<ProfileRequestContext, Duration> {
-
- /** {@inheritDoc} */
- @Override @Nullable
- public Duration apply(final @Nonnull ProfileRequestContext profileRequestContext) {
- final SpringRequestContext springRequestContext =
- profileRequestContext.getSubcontext(SpringRequestContext.class);
- if (springRequestContext == null) {
- return null;
- }
-
- final RequestContext requestContext = springRequestContext.getRequestContext();
- if (requestContext == null) {
- return null;
- }
-
- final String lifetime = (String) requestContext.getFlowScope().get(
- IssueRegistrationAccessTokenArguments.URL_PARAM_LIFETIME);
- try {
- return lifetime == null ? null : Duration.parse(lifetime);
- } catch (final DateTimeParseException e) {
- return null;
- }
- }
-}
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/logic/DefaultRegistrationTokenOnetimeUseLookupFunction.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/logic/DefaultRegistrationTokenOnetimeUseLookupFunction.java
deleted file mode 100644
index e55a2e37..00000000
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/logic/DefaultRegistrationTokenOnetimeUseLookupFunction.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * 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.plugin.oidc.op.profile.logic;
-
-import java.util.function.Function;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import org.opensaml.profile.context.ProfileRequestContext;
-import org.springframework.webflow.execution.RequestContext;
-
-import net.shibboleth.idp.plugin.oidc.op.cli.IssueRegistrationAccessTokenArguments;
-import net.shibboleth.idp.profile.context.SpringRequestContext;
-
-/**
- * A lookup function that fetches the flag signaling one-time use of the token from the SWF request parameters.
- *
- * The parameter key is {@link IssueRegistrationAccessTokenArguments#URL_PARAM_ONE_TIME_TOKEN}.
- */
-public class DefaultRegistrationTokenOnetimeUseLookupFunction implements Function<ProfileRequestContext, Boolean> {
-
- /** {@inheritDoc} */
- @Override @Nullable
- public Boolean apply(final @Nonnull ProfileRequestContext profileRequestContext) {
- final SpringRequestContext springRequestContext =
- profileRequestContext.getSubcontext(SpringRequestContext.class);
- if (springRequestContext == null) {
- return null;
- }
-
- final RequestContext requestContext = springRequestContext.getRequestContext();
- if (requestContext == null) {
- return null;
- }
-
- final String result = (String) requestContext.getFlowScope().get(
- IssueRegistrationAccessTokenArguments.URL_PARAM_ONE_TIME_TOKEN);
- return Boolean.valueOf(result);
- }
-}
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/logic/DefaultRegistrationTokenRelyingPartyIdLookupFunction.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/logic/DefaultRegistrationTokenRelyingPartyIdLookupFunction.java
deleted file mode 100644
index 7be97fce..00000000
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/logic/DefaultRegistrationTokenRelyingPartyIdLookupFunction.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * 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.plugin.oidc.op.profile.logic;
-
-import java.util.function.Function;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import org.opensaml.profile.context.ProfileRequestContext;
-import org.springframework.webflow.execution.RequestContext;
-
-import net.shibboleth.idp.plugin.oidc.op.cli.IssueRegistrationAccessTokenArguments;
-import net.shibboleth.idp.profile.context.SpringRequestContext;
-
-/**
- * A lookup function that fetches the relying party identifier from the SWF request parameters.
- *
- * The parameter key is {@link IssueRegistrationAccessTokenArguments#URL_PARAM_RELYING_PARTY_ID}.
- */
-public class DefaultRegistrationTokenRelyingPartyIdLookupFunction implements Function<ProfileRequestContext, String> {
-
- /** {@inheritDoc} */
- @Override @Nullable
- public String apply(final @Nonnull ProfileRequestContext profileRequestContext) {
- final SpringRequestContext springRequestContext =
- profileRequestContext.getSubcontext(SpringRequestContext.class);
- if (springRequestContext == null) {
- return null;
- }
-
- final RequestContext requestContext = springRequestContext.getRequestContext();
- if (requestContext == null) {
- return null;
- }
-
- return (String) requestContext.getFlowScope().get(
- IssueRegistrationAccessTokenArguments.URL_PARAM_RELYING_PARTY_ID);
- }
-}
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/admin/oidc/issue-registration-access-token/issue-registration-access-token-beans.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/admin/oidc/issue-registration-access-token/issue-registration-access-token-beans.xml
index e0ae4819..c723bb0b 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/admin/oidc/issue-registration-access-token/issue-registration-access-token-beans.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/admin/oidc/issue-registration-access-token/issue-registration-access-token-beans.xml
@@ -21,7 +21,7 @@
<!-- Default operation/resource suppliers for access checks. -->
- <bean id="shibboleth.AdminOperationLookupStrategy" parent="shibboleth.Functions.Constant" c:target="issue" />
+ <bean id="shibboleth.AdminOperationLookupStrategy" parent="shibboleth.Functions.Constant" c:target="execute" />
<bean id="shibboleth.AdminResourceLookupStrategy" parent="shibboleth.Functions.Constant"
c:target="oidc/issue-registration-access-token" />
@@ -30,10 +30,15 @@
<bean id="IssueRegistrationAccessToken"
class="net.shibboleth.idp.plugin.oidc.op.profile.impl.IssueRegistrationAccessToken" scope="prototype"
+ p:httpServletRequest-ref="shibboleth.HttpServletRequest"
p:httpServletResponse-ref="shibboleth.HttpServletResponse"
p:sealer-ref="#{'%{idp.oidc.dynreg.tokenSealer:shibboleth.oidc.TokenSealer}'.trim()}"
p:objectMapper-ref="shibboleth.oidc.JSONObjectMapper"
- p:metadataPolicyLookupStrategy-ref="%{idp.oidc.admin.registration.lookup.policy:shibboleth.oidc.admin.DefaultMetadataPolicyLookupStrategy}" />
+ p:accessControlService-ref="shibboleth.AccessControlService"
+ p:metadataPolicyLookupStrategy-ref="%{idp.oidc.admin.registration.lookup.policy:shibboleth.oidc.admin.DefaultMetadataPolicyLookupStrategy}"
+ p:policyLocationPolicyName="%{idp.oidc.admin.registration.policyLocationPolicy:AccessByIPAddress}"
+ p:policyIdPolicyName="%{idp.oidc.admin.registration.policyIdPolicy:AccessByIPAddress}"
+ p:clientIdPolicyName="%{idp.oidc.admin.registration.clientIdPolicy:AccessByIPAddress}" />
<bean id="shibboleth.oidc.admin.DefaultMetadataPolicyLookupStrategy"
class="net.shibboleth.oidc.profile.config.navigate.ResolverBasedRegistrationMetadataPolicyLookupFunction"
@@ -66,7 +71,8 @@
<bean id="shibboleth.oidc.admin.MetadataPolicyIdentifierStrategy" parent="shibboleth.Functions.Compose">
<constructor-arg name="g">
- <bean class="net.shibboleth.idp.plugin.oidc.op.profile.logic.DefaultMetadataPolicyLocationLookupFunction" />
+ <bean class="net.shibboleth.idp.profile.function.SpringFlowScopeLookupFunction"
+ c:_0="T(net.shibboleth.idp.plugin.oidc.op.cli.IssueRegistrationAccessTokenArguments).URL_PARAM_POLICY_LOCATION" />
</constructor-arg>
<constructor-arg name="f">
<bean parent="shibboleth.Functions.Expression"
@@ -79,8 +85,7 @@
id="shibboleth.oidc.admin.BaseCacheBuilderSpec" abstract="true"
p:fetchStrategy-ref="shibboleth.oidc.admin.MetadataPolicyFetchingStrategy"
p:criteriaToIdentifierStrategy-ref="shibboleth.oidc.admin.MetadataPolicyIdentifierStrategy"
- p:identifierExtractionStrategy-ref="shibboleth.oidc.admin.MetadataPolicyIdentifierStrategy"
- />
+ p:identifierExtractionStrategy-ref="shibboleth.oidc.admin.MetadataPolicyIdentifierStrategy" />
<bean id="shibboleth.oidc.admin.MetadataPolicyFetchingStrategy"
class="net.shibboleth.oidc.metadata.policy.impl.MetadataPolicyViaLocationFetchingStrategy"
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/admin/oidc/issue-registration-access-token/issue-registration-access-token-flow.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/admin/oidc/issue-registration-access-token/issue-registration-access-token-flow.xml
index 0c66ba19..f0a99342 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/admin/oidc/issue-registration-access-token/issue-registration-access-token-flow.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/admin/oidc/issue-registration-access-token/issue-registration-access-token-flow.xml
@@ -8,9 +8,10 @@
<on-start>
<!-- Extract the parameters in case authentication disturbs the URL. -->
<evaluate expression="T(net.shibboleth.utilities.java.support.primitive.StringSupport).trimOrNull(externalContext.getNativeRequest().getParameter('tokenLifetime'))" result="flowScope.tokenLifetime" />
- <evaluate expression="T(net.shibboleth.utilities.java.support.primitive.StringSupport).trimOrNull(externalContext.getNativeRequest().getParameter('metadataPolicyLocation'))" result="flowScope.metadataPolicyLocation" />
- <evaluate expression="T(net.shibboleth.utilities.java.support.primitive.StringSupport).trimOrNull(externalContext.getNativeRequest().getParameter('relyingPartyId'))" result="flowScope.relyingPartyId" />
- <evaluate expression="T(net.shibboleth.utilities.java.support.primitive.StringSupport).trimOrNull(externalContext.getNativeRequest().getParameter('onetime'))" result="flowScope.onetime" />
+ <evaluate expression="T(net.shibboleth.utilities.java.support.primitive.StringSupport).trimOrNull(externalContext.getNativeRequest().getParameter('policyLocation'))" result="flowScope.policyLocation" />
+ <evaluate expression="T(net.shibboleth.utilities.java.support.primitive.StringSupport).trimOrNull(externalContext.getNativeRequest().getParameter('policyId'))" result="flowScope.policyId" />
+ <evaluate expression="T(net.shibboleth.utilities.java.support.primitive.StringSupport).trimOrNull(externalContext.getNativeRequest().getParameter('clientId'))" result="flowScope.clientId" />
+ <evaluate expression="T(net.shibboleth.utilities.java.support.primitive.StringSupport).trimOrNull(externalContext.getNativeRequest().getParameter('replacement'))" result="flowScope.replacement" />
</on-start>
<action-state id="InitializeProfileRequestContext">
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/register/register-beans.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/register/register-beans.xml
index 0058a9e2..b9c52bf8 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/register/register-beans.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/register/register-beans.xml
@@ -30,6 +30,10 @@
</constructor-arg>
</bean>
+ <bean id="InitializeRelyingPartyContext"
+ class="net.shibboleth.idp.plugin.oidc.op.profile.impl.InitializeUnverifiedRelyingPartyContext"
+ scope="prototype" />
+
<bean id="ValidateRegistrationAccessToken"
class="net.shibboleth.idp.plugin.oidc.op.profile.impl.ValidateRegistrationAccessToken" scope="prototype"
p:revocationCache-ref="shibboleth.oidc.RevocationCache"
@@ -43,17 +47,13 @@
<bean id="SelectProfileConfiguration"
class="net.shibboleth.idp.profile.impl.SelectProfileConfiguration" scope="prototype" />
- <bean id="ValidateRegistrationRequestMetadata"
- class="net.shibboleth.idp.plugin.oidc.op.profile.impl.ValidateRegistrationRequestMetadata" scope="prototype" />
-
<bean id="InitializeRegistrationMetadataPolicyContext"
class="net.shibboleth.idp.plugin.oidc.op.profile.impl.InitializeRegistrationMetadataPolicyContext"
scope="prototype" />
- <bean id="InitializeRelyingPartyContext"
- class="net.shibboleth.idp.plugin.oidc.op.profile.impl.InitializeUnverifiedRelyingPartyContext"
- scope="prototype" />
-
+ <bean id="ValidateRegistrationRequestMetadata"
+ class="net.shibboleth.idp.plugin.oidc.op.profile.impl.ValidateRegistrationRequestMetadata" scope="prototype" />
+
<bean id="CheckRedirectURIs"
class="net.shibboleth.idp.plugin.oidc.op.profile.impl.CheckRedirectURIs"
scope="prototype"
@@ -232,5 +232,4 @@
</property>
</bean>
-
</beans>
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/services/javax.servlet.ServletContainerInitializer b/idp-oidc-extension-impl/src/main/resources/META-INF/services/javax.servlet.ServletContainerInitializer
index 7614630a..bb154f19 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/services/javax.servlet.ServletContainerInitializer
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/services/javax.servlet.ServletContainerInitializer
@@ -1 +1 @@
-net.shibboleth.idp.plugin.oidc.op.servlet.RegisterFilterServletContextInitializer
\ No newline at end of file
+#net.shibboleth.idp.plugin.oidc.op.servlet.RegisterFilterServletContextInitializer
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/IssueRegistrationAccessTokenFlowTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/IssueRegistrationAccessTokenFlowTest.java
index 821259f8..c6acc0bd 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/IssueRegistrationAccessTokenFlowTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/IssueRegistrationAccessTokenFlowTest.java
@@ -97,11 +97,11 @@ public class IssueRegistrationAccessTokenFlowTest extends AbstractOidcFlowTest {
URLEncoder.encode(lifetime, "UTF-8"));
}
if (metadataLocation != null) {
- request.addParameter(IssueRegistrationAccessTokenArguments.URL_PARAM_METADATA_POLICY_LOCATION,
+ request.addParameter(IssueRegistrationAccessTokenArguments.URL_PARAM_POLICY_LOCATION,
metadataLocation);
}
if (relyingPartyId != null) {
- request.addParameter(IssueRegistrationAccessTokenArguments.URL_PARAM_RELYING_PARTY_ID, relyingPartyId);
+ request.addParameter(IssueRegistrationAccessTokenArguments.URL_PARAM_POLICY_ID, relyingPartyId);
}
}
}
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/RegistrationFlowTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/RegistrationFlowTest.java
index fca9528e..db9418a2 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/RegistrationFlowTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/RegistrationFlowTest.java
@@ -25,8 +25,6 @@ import java.time.Instant;
import org.opensaml.storage.StorageService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
-import org.springframework.mock.web.MockHttpServletRequest;
-import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.webflow.executor.FlowExecutionResult;
import org.testng.Assert;
import org.testng.annotations.AfterMethod;
@@ -152,7 +150,7 @@ public class RegistrationFlowTest extends AbstractOidcFlowTest {
clientId = "https://example.org";
setJsonRequest("POST", buildRequestMessage(redirectUri));
- request.addHeader("Authorization", buildRegistrationAccessToken(false, "[\"https://example.org/cb\"]")
+ request.addHeader("Authorization", buildRegistrationAccessToken(true, "[\"https://example.org/cb\"]")
.toAuthorizationHeader());
assertSuccessfulResponse(flowExecutor.launchExecution(FLOW_ID, null, externalContext), clientId);
}
@@ -171,7 +169,7 @@ public class RegistrationFlowTest extends AbstractOidcFlowTest {
initializeThreadLocals();
setJsonRequest("POST", buildRequestMessage(redirectUri));
- request.addHeader("Authorization", buildRegistrationAccessToken(false, "[\"https://example.org/cb\"]")
+ request.addHeader("Authorization", buildRegistrationAccessToken(true, "[\"https://example.org/cb\"]")
.toAuthorizationHeader());
assertSuccessfulResponse(flowExecutor.launchExecution(FLOW_ID, null, externalContext), null);
}
@@ -190,7 +188,7 @@ public class RegistrationFlowTest extends AbstractOidcFlowTest {
initializeThreadLocals();
setJsonRequest("POST", buildRequestMessage(redirectUri));
- request.addHeader("Authorization", buildRegistrationAccessToken(true, "[\"https://example.org/cb\"]")
+ request.addHeader("Authorization", buildRegistrationAccessToken(false, "[\"https://example.org/cb\"]")
.toAuthorizationHeader());
final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
assertErrorCode(result, OAuth2Error.SERVER_ERROR_CODE);
@@ -285,7 +283,7 @@ public class RegistrationFlowTest extends AbstractOidcFlowTest {
Assert.assertEquals(storedMetadata.getPolicyURIEntries(), metadata.getPolicyURIEntries());
}
- protected BearerAccessToken buildRegistrationAccessToken(final boolean onetime, final String redirectUriSubset,
+ protected BearerAccessToken buildRegistrationAccessToken(final boolean replacement, final String redirectUriSubset,
final String... additionalPolicyClaims) throws Exception {
final StringBuilder metadata = new StringBuilder();
if (additionalPolicyClaims != null) {
@@ -311,7 +309,7 @@ public class RegistrationFlowTest extends AbstractOidcFlowTest {
"\"jti\":\"" + idGenerator.generateIdentifier() + "\"," +
"\"rp_id\":\"" + rpId + "\"," +
(clientId != null ? ("\"client_id\":\"" + clientId + "\",") : "") +
- "\"onetime\":" + Boolean.toString(onetime) + "," +
+ "\"replacement\":" + Boolean.toString(replacement) + "," +
"\"metadata\":" + (metadata.length() == 0 ? "null" : "{" + metadata.toString()) + "}" +
"}";
return new BearerAccessToken(BaseOIDCResponseActionTest.initializeDataSealer().wrap(json,
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/IssueRegistrationAccessTokenTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/IssueRegistrationAccessTokenTest.java
index ee83f818..75081777 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/IssueRegistrationAccessTokenTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/IssueRegistrationAccessTokenTest.java
@@ -23,11 +23,14 @@ import java.time.Instant;
import java.util.HashMap;
import java.util.Map;
+import javax.servlet.ServletRequest;
+
import org.opensaml.profile.action.EventIds;
import org.opensaml.profile.context.ProfileRequestContext;
import org.springframework.webflow.execution.Event;
import org.springframework.webflow.execution.RequestContext;
import org.testng.Assert;
+import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
@@ -45,6 +48,8 @@ import net.shibboleth.idp.profile.testing.RequestContextBuilder;
import net.shibboleth.oidc.metadata.policy.MetadataPolicy;
import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
import net.shibboleth.utilities.java.support.logic.FunctionSupport;
+import net.shibboleth.utilities.java.support.security.AccessControl;
+import net.shibboleth.utilities.java.support.security.AccessControlService;
import net.shibboleth.utilities.java.support.security.DataSealer;
import net.shibboleth.utilities.java.support.security.DataSealerException;
@@ -63,12 +68,21 @@ public class IssueRegistrationAccessTokenTest {
private ObjectMapper objectMapper;
+ private AccessControlService accessControlService;
+
private String issuer = "mockIssuer";
private String relyingPartyId = "tokenRpId";
-
+
+ private String clientId = "mockClient";
+
private String lifetime = "P1D";
+ @BeforeClass
+ public void initOnce() {
+ accessControlService = new MockAccessControlService();
+ }
+
@BeforeMethod
public void init() throws ComponentInitializationException, NoSuchAlgorithmException {
dataSealer = BaseOIDCResponseActionTest.initializeDataSealer();
@@ -77,6 +91,10 @@ public class IssueRegistrationAccessTokenTest {
action.setObjectMapper(objectMapper);
action.setMetadataPolicyLookupStrategy(FunctionSupport.constant(defaultMetadataPolicy()));
action.setSealer(dataSealer);
+ action.setAccessControlService(accessControlService);
+ action.setPolicyLocationPolicyName("policyLocationPolicy");
+ action.setPolicyIdPolicyName("policyIdPolicy");
+ action.setClientIdPolicyName("clientIdPolicy");
action.setIssuerLookupStrategy(FunctionSupport.constant(issuer));
action.initialize();
requestCtx = new RequestContextBuilder().buildRequestContext();
@@ -93,6 +111,7 @@ public class IssueRegistrationAccessTokenTest {
public void testNoSealer() throws ComponentInitializationException {
action = new IssueRegistrationAccessToken();
action.setObjectMapper(objectMapper);
+ action.setAccessControlService(accessControlService);
action.setMetadataPolicyLookupStrategy(FunctionSupport.constant(defaultMetadataPolicy()));
action.setIssuerLookupStrategy(FunctionSupport.constant(issuer));
action.initialize();
@@ -102,6 +121,7 @@ public class IssueRegistrationAccessTokenTest {
public void testNoObjectMapper() throws ComponentInitializationException {
action = new IssueRegistrationAccessToken();
action.setSealer(dataSealer);
+ action.setAccessControlService(accessControlService);
action.setMetadataPolicyLookupStrategy(FunctionSupport.constant(defaultMetadataPolicy()));
action.setIssuerLookupStrategy(FunctionSupport.constant(issuer));
action.initialize();
@@ -112,6 +132,7 @@ public class IssueRegistrationAccessTokenTest {
action = new IssueRegistrationAccessToken();
action.setSealer(dataSealer);
action.setObjectMapper(objectMapper);
+ action.setAccessControlService(accessControlService);
action.setIssuerLookupStrategy(FunctionSupport.constant(issuer));
action.initialize();
}
@@ -121,6 +142,7 @@ public class IssueRegistrationAccessTokenTest {
action = new IssueRegistrationAccessToken();
action.setSealer(dataSealer);
action.setObjectMapper(objectMapper);
+ action.setAccessControlService(accessControlService);
action.setMetadataPolicyLookupStrategy(FunctionSupport.constant(null));
action.setIssuerLookupStrategy(FunctionSupport.constant(issuer));
action.initialize();
@@ -130,28 +152,43 @@ public class IssueRegistrationAccessTokenTest {
}
@Test
- public void testNoTokenLifetime() {
- requestCtx.getFlowScope().put(IssueRegistrationAccessTokenArguments.URL_PARAM_RELYING_PARTY_ID,
+ public void testNoTokenLifetime() throws DataSealerException, JsonMappingException, JsonProcessingException {
+ requestCtx.getFlowScope().put(IssueRegistrationAccessTokenArguments.URL_PARAM_POLICY_ID,
relyingPartyId);
final Event event = action.execute(requestCtx);
- ActionTestingSupport.assertEvent(event, EventIds.INVALID_MESSAGE);
+ ActionTestingSupport.assertProceedEvent(event);
+ validateToken(relyingPartyId, null);
}
@Test
- public void testNoRelyingPartyId() {
+ public void testSuccessNoPolicyId() throws DataSealerException, JsonMappingException, JsonProcessingException {
requestCtx.getFlowScope().put(IssueRegistrationAccessTokenArguments.URL_PARAM_LIFETIME, lifetime);
final Event event = action.execute(requestCtx);
- ActionTestingSupport.assertEvent(event, EventIds.INVALID_MESSAGE);
+ ActionTestingSupport.assertProceedEvent(event);
+ validateToken(null, null);
}
@Test
- public void testSuccess() throws DataSealerException, JsonMappingException, JsonProcessingException {
+ public void testSuccessWithPolicyId() throws DataSealerException, JsonMappingException, JsonProcessingException {
requestCtx.getFlowScope().put(IssueRegistrationAccessTokenArguments.URL_PARAM_LIFETIME, lifetime);
- requestCtx.getFlowScope().put(IssueRegistrationAccessTokenArguments.URL_PARAM_RELYING_PARTY_ID,
- relyingPartyId);
- final Instant start = Instant.now();
+ requestCtx.getFlowScope().put(IssueRegistrationAccessTokenArguments.URL_PARAM_POLICY_ID, relyingPartyId);
+ final Event event = action.execute(requestCtx);
+ ActionTestingSupport.assertProceedEvent(event);
+ validateToken(relyingPartyId, null);
+ }
+
+ @Test
+ public void testSuccessWithClientIdPolicyId() throws DataSealerException, JsonMappingException, JsonProcessingException {
+ requestCtx.getFlowScope().put(IssueRegistrationAccessTokenArguments.URL_PARAM_LIFETIME, lifetime);
+ requestCtx.getFlowScope().put(IssueRegistrationAccessTokenArguments.URL_PARAM_POLICY_ID, relyingPartyId);
+ requestCtx.getFlowScope().put(IssueRegistrationAccessTokenArguments.URL_PARAM_CLIENT_ID, clientId);
final Event event = action.execute(requestCtx);
ActionTestingSupport.assertProceedEvent(event);
+ validateToken(relyingPartyId, clientId);
+ }
+
+ protected void validateToken(final String policyId, final String clientId) throws DataSealerException, JsonMappingException, JsonProcessingException {
+ final Instant start = Instant.now();
final Object rawMessage = prc.getOutboundMessageContext().getMessage();
Assert.assertNotNull(rawMessage);
Assert.assertTrue(rawMessage instanceof AccessTokenResponse);
@@ -164,7 +201,8 @@ public class IssueRegistrationAccessTokenTest {
Assert.assertEquals(claimsSet.getKeyType(), "rt");
Assert.assertNotNull(claimsSet.getJti());
Assert.assertEquals(claimsSet.getIssuer(), issuer);
- Assert.assertEquals(claimsSet.getRelyingPartyId(), relyingPartyId);
+ Assert.assertEquals(claimsSet.getRelyingPartyId(), policyId);
+ Assert.assertEquals(claimsSet.getClientId(), clientId);
final Map<String, MetadataPolicy> tokenPolicy = claimsSet.getMetadata();
Assert.assertNotNull(tokenPolicy);
Assert.assertEquals(tokenPolicy.size(), 1);
@@ -174,11 +212,48 @@ public class IssueRegistrationAccessTokenTest {
assertInstantWithSkew(claimsSet.getIssuedAt(), start);
assertInstantWithSkew(claimsSet.getExpiration(), start.plus(Duration.ofDays(1)));
}
-
+
protected void assertInstantWithSkew(final Instant instant, final Instant target) {
final Duration skew = Duration.ofSeconds(5);
Assert.assertTrue(instant.isAfter(target.minus(skew)));
Assert.assertTrue(instant.isBefore(target.plus(skew)));
}
-}
+ /** Mock service for ACL checks. */
+ private class MockAccessControlService implements AccessControlService {
+
+ public boolean isInitialized() {
+ return true;
+ }
+
+ public void initialize() throws ComponentInitializationException {
+
+ }
+
+ public String getId() {
+ return "mockACS";
+ }
+
+ public AccessControl getInstance(final String name) {
+ return new AccessControl() {
+ public boolean checkAccess(final ServletRequest request, final String operation,
+ final String resource) {
+ if ("read".equals(operation)) {
+ if ("policyIdPolicy".equals(name) && relyingPartyId.equals(resource)) {
+ return true;
+ }
+
+ return false;
+ } else if ("write".equals(operation)) {
+ if ("clientIdPolicy".equals(name) && clientId.equals(resource)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ };
+ }
+
+ }
+
+}
\ 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