[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