[java-idp-oidc] branch main updated: JOIDC-6 - Release policy for OAuth2 scope values based on IdPAttributes
Henri Mikkonen
henri.mikkonen at iki.fi
Fri Jun 17 10:46:35 UTC 2022
This is an automated email from the git hooks/post-receive script.
hjmikkon pushed a commit to branch main
in repository java-idp-oidc.
View the commit online:
http://git.shibboleth.net/view/?p=java-idp-oidc.git;a=commit;h=daa568e2c1577091b27dea2aa0d654798bf4dd5d
The following commit(s) were added to refs/heads/main by this push:
new daa568e2 JOIDC-6 - Release policy for OAuth2 scope values based on IdPAttributes
daa568e2 is described below
commit daa568e2c1577091b27dea2aa0d654798bf4dd5d
Author: Henri Mikkonen <henri.mikkonen at iki.fi>
AuthorDate: Fri Jun 17 13:42:53 2022 +0300
JOIDC-6 - Release policy for OAuth2 scope values based on IdPAttributes
https://shibboleth.atlassian.net/browse/JOIDC-6
Created a new action-state in authorize-flow right after DoAuthenticationSubflow:
DoPostAuthValidations. Moved ValidateScope and ValidateAudience there to allow
end-user authentication results to be exploited by their functions.
New class 'net.shibboleth.idp.plugin.oidc.op.profile.ScopeUtil' provides static
helper methods for handling Scope objects, especially by the scripted attributes.
---
idp-oidc-extension-api/pom.xml | 5 +
.../idp/plugin/oidc/op/profile/ScopeUtil.java | 203 +++++++++++++++++++++
.../idp/flows/oidc/authorize/authorize-flow.xml | 11 +-
3 files changed, 216 insertions(+), 3 deletions(-)
diff --git a/idp-oidc-extension-api/pom.xml b/idp-oidc-extension-api/pom.xml
index 29358386..c4208d72 100644
--- a/idp-oidc-extension-api/pom.xml
+++ b/idp-oidc-extension-api/pom.xml
@@ -53,6 +53,11 @@
<artifactId>idp-saml-api</artifactId>
<scope>provided</scope>
</dependency>
+ <dependency>
+ <groupId>net.shibboleth.idp</groupId>
+ <artifactId>idp-attribute-resolver-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
<dependency>
<groupId>org.opensaml</groupId>
<artifactId>opensaml-security-api</artifactId>
diff --git a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/ScopeUtil.java b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/ScopeUtil.java
new file mode 100644
index 00000000..ff90a471
--- /dev/null
+++ b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/ScopeUtil.java
@@ -0,0 +1,203 @@
+/*
+ * 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;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.nimbusds.oauth2.sdk.Scope;
+
+import net.shibboleth.idp.attribute.IdPAttribute;
+import net.shibboleth.idp.attribute.IdPAttributeValue;
+import net.shibboleth.idp.attribute.StringAttributeValue;
+import net.shibboleth.idp.attribute.resolver.ResolutionException;
+import net.shibboleth.idp.attribute.resolver.scripted.ScriptedIdPAttribute;
+
+/**
+ * Static helper methods for handling {@link Scope} objects, especially with IdP attributes.
+ *
+ * @since 3.2.0
+ */
+public class ScopeUtil {
+
+ /** Class logger. */
+ private static Logger log = LoggerFactory.getLogger(ScopeUtil.class);
+
+ /**
+ * Private constructor.
+ */
+ private ScopeUtil() {
+ // no op
+ }
+
+ /**
+ * Builds an {@link IdPAttribute} with given identifier and contents taken from the given scope.
+ *
+ * @param attributeId The attribute ID to use
+ * @param scope The contents for the attribute
+ * @return The built attribute
+ */
+ @Nonnull public static IdPAttribute buildAttribute(@Nonnull final String attributeId,
+ @Nullable final Scope scope) {
+ final IdPAttribute attribute = new IdPAttribute(attributeId);
+ if (scope == null || scope.isEmpty()) {
+ return attribute;
+ }
+ setAttributeValues(attribute, scope);
+ return attribute;
+ }
+
+ /**
+ * Sets the contents for {@link IdPAttribute} from the given scope.
+ *
+ * @param attribute The attribute whose contents are set
+ * @param scope The contents for the attribute
+ */
+ public static void setAttributeValues(@Nonnull final IdPAttribute attribute, @Nullable final Scope scope) {
+ if (scope == null || scope.isEmpty()) {
+ return;
+ }
+ final List<IdPAttributeValue> values = new ArrayList<>(scope.size());
+ scope.forEach(value -> values.add(new StringAttributeValue(value.getValue())));
+ attribute.setValues(values);
+ }
+
+ /**
+ * Adds values from the given scope to the {@link ScriptedIdPAttribute}.
+ *
+ * @param attribute The attribute whose contents are fulfilled
+ * @param scope The contents to fulfill for the attribute
+ */
+ public static void populateScriptedAttribute(@Nonnull final ScriptedIdPAttribute attribute,
+ @Nullable final Scope scope) {
+ if (scope != null && !scope.isEmpty()) {
+ scope.forEach(value -> {
+ try {
+ attribute.addValue(value.getValue());
+ } catch (final ResolutionException e) {
+ log.warn("Resolution exception catched when populating attribute", e);
+ }
+ });
+ }
+ }
+
+ /**
+ * Adds values from the given {@link ScriptedIdPAttribute} to another {@link ScriptedIdPAttribute}.
+ *
+ * @param attribute The attribute whose contents are fulfilled
+ * @param source The contents to fulfill for the attribute
+ * @throws ResolutionException If the soure attribute contents cannot be accessed
+ */
+ public static void populateScriptedAttribute(@Nonnull final ScriptedIdPAttribute attribute,
+ @Nullable final ScriptedIdPAttribute source) throws ResolutionException {
+ if (source != null && !source.getValues().isEmpty()) {
+ source.getValues().forEach(value -> {
+ try {
+ attribute.addValue(value);
+ } catch (final ResolutionException e) {
+ log.warn("Resolution exception catched when populating attribute", e);
+ }
+ });
+ }
+ }
+
+ /**
+ * Builds {@link Scope} from the given attribute contents. Only the {@link StringAttributeValue}s are recognized.
+ *
+ * @param attribute The attribute whose contents are used for the scope
+ * @return The built scope
+ */
+ @Nonnull public static Scope buildScope(@Nullable final IdPAttribute attribute) {
+ final Scope scope = new Scope();
+ if (attribute == null || attribute.getValues().isEmpty()) {
+ return scope;
+ }
+ for (final IdPAttributeValue value : attribute.getValues()) {
+ if (value instanceof StringAttributeValue) {
+ scope.add(new Scope.Value(((StringAttributeValue)value).getValue()));
+ }
+ }
+ return scope;
+ }
+
+ /**
+ * Builds {@link Scope} from the given {@link ScriptedIdPAttribute}. The contents are assumed to be {@link String}.
+ *
+ * @param attribute The attribute whose contents are used for the scope
+ * @return The built scope
+ * @throws ResolutionException If the attribute values cannot be accessed
+ */
+ @Nonnull public static Scope buildScope(@Nullable final ScriptedIdPAttribute attribute)
+ throws ResolutionException {
+ final Scope scope = new Scope();
+ if (attribute == null) {
+ return scope;
+ }
+ attribute.getValues().forEach(value -> scope.add(new Scope.Value((String) value)));
+ return scope;
+ }
+
+ /**
+ * Builds intersected {@link Scope} from the given {@link IdPAttribute} and {@link Scope}.
+ *
+ * @param attribute The attribute whose contents are processed
+ * @param scope The existing scope whose contents are processed
+ * @return The intersection of the two contents
+ */
+ @Nonnull public static Scope buildIntersectedScope(@Nullable final IdPAttribute attribute,
+ @Nullable final Scope scope) {
+ final Scope attributeScope = buildScope(attribute);
+ if (scope != null) {
+ attributeScope.retainAll(scope);
+ }
+ return attributeScope;
+ }
+
+ /**
+ * Builds intersected {@link Scope} from the given {@link ScriptedIdPAttribute} and {@link Scope}.
+ *
+ * @param attribute The attribute whose contents are processed
+ * @param scope The existing scope whose contents are processed
+ * @return The intersection of the two contents
+ * @throws ResolutionException If th attribute values cannot be accessed
+ */
+ @Nonnull public static Scope buildIntersectedScope(@Nullable final ScriptedIdPAttribute attribute,
+ @Nullable final Scope scope) throws ResolutionException {
+ final Scope attributeScope = buildScope(attribute);
+ if (scope != null) {
+ attributeScope.retainAll(scope);
+ }
+ return attributeScope;
+ }
+
+ /**
+ * Removes a single value from the given {@link Scope}.
+ *
+ * @param scope The scope to modify
+ * @param value The value to be removed if it exists
+ */
+ public static void removeValue(@Nonnull final Scope scope, @Nonnull final String value) {
+ scope.remove(new Scope.Value(value));
+ }
+}
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/authorize/authorize-flow.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/authorize/authorize-flow.xml
index 7de875fc..db319735 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/authorize/authorize-flow.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/authorize/authorize-flow.xml
@@ -49,8 +49,6 @@
<evaluate expression="ValidateRedirectURI" />
<evaluate expression="ValidateResponseType" />
<evaluate expression="ValidateCodeChallenge" />
- <evaluate expression="ValidateScope" />
- <evaluate expression="ValidateAudience" />
<evaluate expression="SetRequestedClaimsToResponseContext" />
<evaluate expression="SetRequestedSubjectToResponseContext" />
<evaluate expression="'proceed'" />
@@ -78,10 +76,17 @@
<subflow-state id="DoAuthenticationSubflow" subflow="authn">
<input name="calledAsSubflow" value="true" />
- <transition on="proceed" to="SetAuthenticationInformationToResponseContext" />
+ <transition on="proceed" to="DoPostAuthValidations" />
<transition on="RestartAuthentication" to="PostInitialSetup" />
</subflow-state>
+ <action-state id="DoPostAuthValidations">
+ <evaluate expression="ValidateScope" />
+ <evaluate expression="ValidateAudience" />
+ <evaluate expression="'proceed'" />
+ <transition on="proceed" to="SetAuthenticationInformationToResponseContext" />
+ </action-state>
+
<action-state id="SetAuthenticationInformationToResponseContext">
<evaluate expression="SetAuthenticationContextClassReferenceToResponseContext" />
<evaluate expression="SetAuthenticationTimeToResponseContext" />
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list