[java-idp-oidc] branch main updated: JOIDC-186 - Support additional refresh token types

Henri Mikkonen henri.mikkonen at iki.fi
Fri Mar 22 07:50:14 UTC 2024


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=107e1d40408eb231bb2ef82e9a7756cbb78f27e1

The following commit(s) were added to refs/heads/main by this push:
     new 107e1d40 JOIDC-186 - Support additional refresh token types
107e1d40 is described below

commit 107e1d40408eb231bb2ef82e9a7756cbb78f27e1
Author: Henri Mikkonen <henri.mikkonen at iki.fi>
AuthorDate: Fri Mar 22 09:49:55 2024 +0200

    JOIDC-186 - Support additional refresh token types
    
    https://shibboleth.atlassian.net/browse/JOIDC-186
    
    Refactored the bean-injection to the map of serializers and the list of deserializers.
    This way we make sure that the beans that get injected are prototypes.
    
    The beans are injected via two factories (global beans):
    
    - shibboleth.oidc.RefreshTokenSerializerFactory
    - shibboleth.oidc.RefreshTokenDeserializerFactory
    
    Those can be adjusted with two constructor parameters:
    
    - id: the id for the bean to be wired
    - allowNonPrototype: whether to allow non-prototype beans, defaults to false
---
 .../op/profile/spring/TokenExtensionFactory.java   | 117 +++++++++++++++++++++
 .../oidc/op/profile/spring/package-info.java       |  18 ++++
 .../META-INF/net.shibboleth.idp/postconfig.xml     |  11 ++
 .../oidc/abstract-api/oidc-abstract-api-beans.xml  |  99 +++++++++--------
 .../idp/flows/oidc/token/token-beans.xml           |  81 +++++++-------
 .../profile/spring/TokenExtensionFactoryTest.java  |  93 ++++++++++++++++
 6 files changed, 336 insertions(+), 83 deletions(-)

diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/spring/TokenExtensionFactory.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/spring/TokenExtensionFactory.java
new file mode 100644
index 00000000..547bf81d
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/spring/TokenExtensionFactory.java
@@ -0,0 +1,117 @@
+/*
+ * Licensed 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.spring;
+
+import java.util.List;
+import java.util.function.BiFunction;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.slf4j.Logger;
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.core.ParameterizedTypeReference;
+import org.springframework.core.ResolvableType;
+
+import net.shibboleth.idp.plugin.oidc.op.token.support.RefreshTokenClaimsSet;
+import net.shibboleth.shared.annotation.ParameterName;
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.primitive.LoggerFactory;
+
+/**
+ * Factory used for obtaining token extension beans of the desired type, identifier and scope.
+ */
+public class TokenExtensionFactory implements ApplicationContextAware {
+
+    /** Enumeration of the supported token extension types. */
+    public enum TokenExtensionType {
+        /** Refresh token serializer. */
+        REFRESH_TOKEN_SERIALIZER,
+        /** Refresh token deserializer. */
+        REFRESH_TOKEN_DESERIALIZER
+    }
+
+    /** Class logger. */
+    @Nonnull private Logger log = LoggerFactory.getLogger(TokenExtensionFactory.class);
+
+    /** The application context from where to fetch the custom policy operators. */
+    private ApplicationContext applicationContext;
+
+    /** The identifier for the token extension bean. */
+    @Nonnull private final String beanId;
+
+    /** The token extension type. */
+    @Nonnull private final ResolvableType extensionType;
+
+    /** Flag to signal whether to allow non-prototype beans. */
+    private final boolean allowNonPrototype;
+
+    /**
+     * Constructor.
+     *
+     * @param id The identifier for the token extension bean.
+     * @param tokenExtensionType The token extension type.
+     * @param nonPrototype Flag to signal whether to allow non-prototype beans.
+     */
+    public TokenExtensionFactory(@Nonnull @ParameterName(name="id") final String id,
+            @Nonnull @ParameterName(name="tokenExtensionType") final TokenExtensionType tokenExtensionType,
+            @ParameterName(name="allowNonPrototype") final boolean nonPrototype) {
+        beanId = Constraint.isNotEmpty(id, "Bean id cannot be empty");
+        Constraint.isNotNull(tokenExtensionType, "Token extension type cannot be null");
+        final ParameterizedTypeReference<?> typeReference = switch (tokenExtensionType) {
+            case REFRESH_TOKEN_SERIALIZER ->
+                new ParameterizedTypeReference<BiFunction<ProfileRequestContext,RefreshTokenClaimsSet,String>>() {};
+            case REFRESH_TOKEN_DESERIALIZER ->
+                new ParameterizedTypeReference<BiFunction<ProfileRequestContext,String,RefreshTokenClaimsSet>>() {};
+        };
+        extensionType = ResolvableType.forType(typeReference);
+        allowNonPrototype = nonPrototype;
+    }
+
+    /**
+     * Get the token extension bean by using constraints from the class variables.
+     * 
+     * @return The bean if one meeting the constraints was found, or null otherwise.
+     */
+    @Nullable public Object getBean() {
+        log.trace("Looking for bean {}: exists={}, isPrototype={}, isSingleton={}", beanId,
+                applicationContext.containsBean(beanId), applicationContext.isPrototype(beanId),
+                applicationContext.isSingleton(beanId));
+        final String[] compatibleBeans = applicationContext.getBeanNamesForType(extensionType);
+        if (compatibleBeans.length == 0) {
+            log.warn("No compatible beans with type {} found", extensionType);
+            return null;
+        }
+        if (List.of(compatibleBeans).contains(beanId)) {
+            if (!applicationContext.isPrototype(beanId) && !allowNonPrototype) {
+                log.error("The bean {} is not prototype which is not allowed", beanId);
+                return null;
+            }
+            return applicationContext.getBean(beanId);
+        }
+        log.warn("The bean {} is not compatible with type {}", beanId, extensionType);
+        return null;
+    }
+
+    @Override
+    public void setApplicationContext(@Nonnull final ApplicationContext context) throws BeansException {
+        applicationContext = context;
+    }
+
+    
+}
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/spring/package-info.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/spring/package-info.java
new file mode 100644
index 00000000..173b9f8b
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/spring/package-info.java
@@ -0,0 +1,18 @@
+/*
+ * Licensed 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.
+ */
+
+/**
+ * Spring-related tools.
+ */
+package net.shibboleth.idp.plugin.oidc.op.profile.spring;
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
index 29a16fb8..0873f2bc 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
@@ -786,6 +786,17 @@
         class="net.shibboleth.profile.config.navigate.IdentifierGenerationStrategyLookupFunction"
         p:defaultIdentifierGenerationStrategy-ref="shibboleth.DefaultIdentifierGenerationStrategy" />
 
+    <bean id="shibboleth.oidc.RefreshTokenSerializerFactory"
+        class="net.shibboleth.idp.plugin.oidc.op.profile.spring.TokenExtensionFactory"
+        c:tokenExtensionType="REFRESH_TOKEN_SERIALIZER"
+        c:allowNonPrototype="false"
+        abstract="true"/>
+
+    <bean id="shibboleth.oidc.RefreshTokenDeserializerFactory"
+        class="net.shibboleth.idp.plugin.oidc.op.profile.spring.TokenExtensionFactory"
+        c:tokenExtensionType="REFRESH_TOKEN_DESERIALIZER"
+        c:allowNonPrototype="false"
+        abstract="true"/>
 
     <!-- TODO: OPCSP-prefixed beans temporarily defined here and used in views to calculate CSP hashes and nonces.
          Switch into shibboleth.CSP -prefixed ones once we depend on 5.1+ -->
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/abstract-api/oidc-abstract-api-beans.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/abstract-api/oidc-abstract-api-beans.xml
index 7a271bf4..1aa04370 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/abstract-api/oidc-abstract-api-beans.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/abstract-api/oidc-abstract-api-beans.xml
@@ -13,52 +13,59 @@
         class="net.shibboleth.idp.saml.profile.impl.InitializeAuthenticationContext" scope="prototype" />
 
     <util:list id="shibboleth.oidc.DefaultRefreshTokenDeserializers">
-        <bean class="net.shibboleth.idp.plugin.oidc.op.profile.logic.DefaultJwtRefreshTokenDeserializationFunction"
-            scope="prototype"
-            p:dataSealer-ref="#{'%{idp.oidc.tokenSealer:shibboleth.oidc.TokenSealer}'.trim()}">
-            <property name="credentialResolver">
-                <bean class="net.shibboleth.profile.relyingparty.RelyingPartyCredentialResolver" 
-                    c:_0-ref="shibboleth.RelyingPartyResolverService" />
-            </property>
-            <property name="typeHeaderValidationStrategy">
-                <bean parent="shibboleth.BiConditions.Expression" c:expression="#input2 == null"/>
-            </property>
-            <property name="claimsValidator">
-                <bean class="net.shibboleth.oidc.security.jwt.claims.impl.ChainingJWTClaimsValidator">
-                    <property name="claimValidators">
-                        <util:list value-type="net.shibboleth.oidc.jwt.claims.ClaimsValidator">
-                            <bean class="net.shibboleth.oidc.security.jwt.claims.impl.RequiredClaimsValidator"
-                                p:requiredClaims="jti" />
-                            <bean class="net.shibboleth.oidc.security.jwt.claims.impl.ExpiryClaimsValidator"
-                                p:clockSkew="%{idp.policy.clockSkew:PT1M}" />
-                            <bean class="net.shibboleth.oidc.security.jwt.claims.impl.ExactMatchClaimsValidator"
-                                p:claimName="iss">
-                                <property name="valueToMatchLookupStrategy">
-                                    <bean class="net.shibboleth.shared.logic.BiFunctionSupport"
-                                        factory-method="forFunctionOfFirstArg"
-                                        c:_0-ref="shibboleth.ResponderIdLookup.Simple" />
-                                </property>
-                            </bean>
-                            <bean class="net.shibboleth.oidc.security.jwt.claims.impl.ExactMatchClaimsValidator"
-                                p:claimName="client_id">
-                                <property name="valueToMatchLookupStrategy">
-                                    <bean class="net.shibboleth.shared.logic.BiFunctionSupport"
-                                        factory-method="forFunctionOfFirstArg"
-                                        c:_0-ref="shibboleth.RelyingPartyIdLookup.Simple" />
-                                </property>
-                            </bean>
-                            <bean class="net.shibboleth.oidc.security.jwt.claims.impl.AudienceClaimsValidator">
-                                <property name="audienceLookupStrategy">
-                                    <bean parent="shibboleth.BiFunctions.Expression"
-                                        c:expression="#custom.get().getRequestURL().toString().replace('/profile/oauth2/introspection','/profile/oidc/token').replace('/profile/oauth2/revocation','/profile/oidc/token')"
-                                        p:customObject-ref="shibboleth.HttpServletRequestSupplier" />
-                                </property>
-                            </bean>
-                        </util:list>
-                    </property>
-                </bean>
-            </property>
-        </bean>
+        <bean factory-bean="DefaultJwtRefreshTokenDeserializerFactory" factory-method="getBean" />
     </util:list>
 
+    <bean id="DefaultJwtRefreshTokenDeserializerFactory"
+        parent="shibboleth.oidc.RefreshTokenDeserializerFactory"
+        c:id="DefaultJwtRefreshTokenDeserializationFunction"/>
+
+    <bean id="DefaultJwtRefreshTokenDeserializationFunction" 
+        class="net.shibboleth.idp.plugin.oidc.op.profile.logic.DefaultJwtRefreshTokenDeserializationFunction"
+        scope="prototype"
+        p:dataSealer-ref="#{'%{idp.oidc.tokenSealer:shibboleth.oidc.TokenSealer}'.trim()}">
+        <property name="credentialResolver">
+            <bean class="net.shibboleth.profile.relyingparty.RelyingPartyCredentialResolver" 
+                c:_0-ref="shibboleth.RelyingPartyResolverService" />
+        </property>
+        <property name="typeHeaderValidationStrategy">
+            <bean parent="shibboleth.BiConditions.Expression" c:expression="#input2 == null"/>
+        </property>
+        <property name="claimsValidator">
+            <bean class="net.shibboleth.oidc.security.jwt.claims.impl.ChainingJWTClaimsValidator">
+                <property name="claimValidators">
+                    <util:list value-type="net.shibboleth.oidc.jwt.claims.ClaimsValidator">
+                        <bean class="net.shibboleth.oidc.security.jwt.claims.impl.RequiredClaimsValidator"
+                            p:requiredClaims="jti" />
+                        <bean class="net.shibboleth.oidc.security.jwt.claims.impl.ExpiryClaimsValidator"
+                            p:clockSkew="%{idp.policy.clockSkew:PT1M}" />
+                        <bean class="net.shibboleth.oidc.security.jwt.claims.impl.ExactMatchClaimsValidator"
+                            p:claimName="iss">
+                            <property name="valueToMatchLookupStrategy">
+                                <bean class="net.shibboleth.shared.logic.BiFunctionSupport"
+                                    factory-method="forFunctionOfFirstArg"
+                                    c:_0-ref="shibboleth.ResponderIdLookup.Simple" />
+                            </property>
+                        </bean>
+                        <bean class="net.shibboleth.oidc.security.jwt.claims.impl.ExactMatchClaimsValidator"
+                            p:claimName="client_id">
+                            <property name="valueToMatchLookupStrategy">
+                                <bean class="net.shibboleth.shared.logic.BiFunctionSupport"
+                                    factory-method="forFunctionOfFirstArg"
+                                    c:_0-ref="shibboleth.RelyingPartyIdLookup.Simple" />
+                            </property>
+                        </bean>
+                        <bean class="net.shibboleth.oidc.security.jwt.claims.impl.AudienceClaimsValidator">
+                            <property name="audienceLookupStrategy">
+                                <bean parent="shibboleth.BiFunctions.Expression"
+                                    c:expression="#custom.get().getRequestURL().toString().replace('/profile/oauth2/introspection','/profile/oidc/token').replace('/profile/oauth2/revocation','/profile/oidc/token')"
+                                    p:customObject-ref="shibboleth.HttpServletRequestSupplier" />
+                            </property>
+                        </bean>
+                    </util:list>
+                </property>
+            </bean>
+        </property>
+    </bean>
+
 </beans>
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/token/token-beans.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/token/token-beans.xml
index f36bef14..48370dfb 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/token/token-beans.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/token/token-beans.xml
@@ -419,48 +419,55 @@
         </property>
     </bean>
 
-    <util:map id="shibboleth.oidc.DefaultRefreshTokenSerializationStrategies" scope="prototype">
+    <util:map id="shibboleth.oidc.DefaultRefreshTokenSerializationStrategies">
         <entry key="JWT">
-            <bean class="net.shibboleth.idp.plugin.oidc.op.profile.logic.DefaultJwtRefreshTokenSerializationFunction"
-                scope="prototype"
-                p:dataSealer-ref="#{'%{idp.oidc.tokenSealer:shibboleth.oidc.TokenSealer}'.trim()}">
-                <property name="signingParametersHandler">
-                    <bean class="net.shibboleth.oidc.profile.impl.PopulateJWTSignatureSigningParametersHandler"
-                        scope="prototype">
-                        <property name="configurationLookupStrategy">
-                            <bean parent="shibboleth.Functions.Compose">
-                                <constructor-arg name="f">
-                                    <bean class="org.opensaml.messaging.context.navigate.ParentContextLookup"
-                                        c:type="org.opensaml.profile.context.ProfileRequestContext" />
-                                </constructor-arg>
-                                <constructor-arg name="g">
-                                    <bean lazy-init="true"
-                                        class="net.shibboleth.oidc.profile.config.navigate.JWTSignatureSigningConfigurationLookupFunction" />
-                                </constructor-arg>
-                            </bean>
-                        </property>
-                        <property name="signatureSigningParametersResolver">
-                            <bean class="net.shibboleth.oidc.security.jose.impl.ClientInformationSignatureSigningParametersResolver">
-                                <constructor-arg name="signatureAlgorithmLookupStrategy">
-                                    <bean class="net.shibboleth.oidc.profile.config.navigate.ClientInformationStringValueLookupFunction"
-                                        c:keyName="id_token_signed_response_alg" />
-                                </constructor-arg>
-                                <constructor-arg name="defaultAlgorithmValue" value="RS256" />
-                            </bean>
-                        </property>
+            <bean factory-bean="DefaultJwtRefreshTokenSerializerFactory" factory-method="getBean" />
+        </entry>
+    </util:map>
+
+    <bean id="DefaultJwtRefreshTokenSerializerFactory"
+        parent="shibboleth.oidc.RefreshTokenSerializerFactory"
+        c:id="DefaultJwtRefreshTokenSerializationFunction"/>
+
+    <bean id="DefaultJwtRefreshTokenSerializationFunction"
+        class="net.shibboleth.idp.plugin.oidc.op.profile.logic.DefaultJwtRefreshTokenSerializationFunction"
+        scope="prototype"
+        p:dataSealer-ref="#{'%{idp.oidc.tokenSealer:shibboleth.oidc.TokenSealer}'.trim()}">
+        <property name="signingParametersHandler">
+            <bean class="net.shibboleth.oidc.profile.impl.PopulateJWTSignatureSigningParametersHandler"
+                scope="prototype">
+                <property name="configurationLookupStrategy">
+                    <bean parent="shibboleth.Functions.Compose">
+                        <constructor-arg name="f">
+                            <bean class="org.opensaml.messaging.context.navigate.ParentContextLookup"
+                                c:type="org.opensaml.profile.context.ProfileRequestContext" />
+                        </constructor-arg>
+                        <constructor-arg name="g">
+                            <bean lazy-init="true"
+                                class="net.shibboleth.oidc.profile.config.navigate.JWTSignatureSigningConfigurationLookupFunction" />
+                        </constructor-arg>
                     </bean>
                 </property>
-                <property name="audienceLookupStrategy">
-                    <bean parent="shibboleth.Functions.Expression"
-                        c:expression="#custom.get().getRequestURL().toString()"
-                        p:customObject-ref="shibboleth.HttpServletRequestSupplier" />
-                </property>
-                <property name="typeHeaderLookupStrategy">
-                    <bean parent="shibboleth.BiFunctions.Expression" c:expression="#null" />
+                <property name="signatureSigningParametersResolver">
+                    <bean class="net.shibboleth.oidc.security.jose.impl.ClientInformationSignatureSigningParametersResolver">
+                        <constructor-arg name="signatureAlgorithmLookupStrategy">
+                            <bean class="net.shibboleth.oidc.profile.config.navigate.ClientInformationStringValueLookupFunction"
+                                c:keyName="id_token_signed_response_alg" />
+                        </constructor-arg>
+                        <constructor-arg name="defaultAlgorithmValue" value="RS256" />
+                    </bean>
                 </property>
             </bean>
-        </entry>
-    </util:map>
+        </property>
+        <property name="audienceLookupStrategy">
+            <bean parent="shibboleth.Functions.Expression"
+                c:expression="#custom.get().getRequestURL().toString()"
+                p:customObject-ref="shibboleth.HttpServletRequestSupplier" />
+        </property>
+        <property name="typeHeaderLookupStrategy">
+            <bean parent="shibboleth.BiFunctions.Expression" c:expression="#null" />
+        </property>
+    </bean>
 
 
     <bean id="DefaultRefreshTokenActivationCondition" parent="shibboleth.Conditions.AND">
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/spring/TokenExtensionFactoryTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/spring/TokenExtensionFactoryTest.java
new file mode 100644
index 00000000..0a3cf07b
--- /dev/null
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/spring/TokenExtensionFactoryTest.java
@@ -0,0 +1,93 @@
+/*
+ * Licensed 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.spring;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.function.BiFunction;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.springframework.context.ApplicationContext;
+import org.springframework.core.ResolvableType;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import net.shibboleth.idp.plugin.oidc.op.profile.spring.TokenExtensionFactory.TokenExtensionType;
+import net.shibboleth.idp.plugin.oidc.op.token.support.RefreshTokenClaimsSet;
+import net.shibboleth.shared.logic.BiFunctionSupport;
+
+/**
+ * Unit tests for {@link TokenExtensionFactory}.
+ */
+public class TokenExtensionFactoryTest {
+
+    TokenExtensionFactory factory;
+    @Nonnull String beanId = "mockBeanId";
+
+    public void initFactory(@Nonnull final TokenExtensionType type, @Nullable final Object bean,
+            final boolean isPrototype, final boolean nonPrototypeAllowed) {
+        ApplicationContext applicationContext = mock(ApplicationContext.class);
+        if (bean != null) {
+            when(applicationContext.getBeanNamesForType(any(ResolvableType.class))).thenReturn(new String[] { beanId });
+            when(applicationContext.getBean(eq(beanId))).thenReturn(bean);
+            when(applicationContext.isPrototype(eq(beanId))).thenReturn(isPrototype);
+            factory =
+                    new TokenExtensionFactory(beanId, TokenExtensionType.REFRESH_TOKEN_SERIALIZER, nonPrototypeAllowed);
+        } else {
+            when(applicationContext.getBeanNamesForType(any(ResolvableType.class))).thenReturn(new String[] {});
+            factory = new TokenExtensionFactory("notExisting", TokenExtensionType.REFRESH_TOKEN_SERIALIZER, true);
+        }
+        factory.setApplicationContext(applicationContext);
+    }
+
+    @Test
+    public void testRefreshTokenSerializer_NonPrototypeNotAllowed_nonPrototypeShouldReturnNull() {
+        initFactory(TokenExtensionType.REFRESH_TOKEN_SERIALIZER, mockSerializer(), false, false);
+        Assert.assertNull(factory.getBean());
+    }
+
+    @Test
+    public void testRefreshTokenSerializer_NonPrototypeAllowed_nonPrototypeShouldReturnBean() {
+        initFactory(TokenExtensionType.REFRESH_TOKEN_SERIALIZER, mockSerializer(), false, true);
+        Assert.assertNotNull(factory.getBean());
+    }
+
+    @Test
+    public void testRefreshTokenDeserializer_NonPrototypeNotAllowed_nonPrototypeShouldReturnNull() {
+        initFactory(TokenExtensionType.REFRESH_TOKEN_DESERIALIZER, mockDeserializer(), false, false);
+        Assert.assertNull(factory.getBean());
+    }
+
+    @Test
+    public void testRefreshTokenDeserializer_NonPrototypeAllowed_nonPrototypeShouldReturnBean() {
+        initFactory(TokenExtensionType.REFRESH_TOKEN_DESERIALIZER, mockDeserializer(), false, true);
+        Assert.assertNotNull(factory.getBean());
+    }
+
+    protected BiFunction<ProfileRequestContext,RefreshTokenClaimsSet,String> mockSerializer() {
+        return BiFunctionSupport.constant("mockValue");
+    }
+
+    @SuppressWarnings("unchecked")
+    protected BiFunction<ProfileRequestContext,String,RefreshTokenClaimsSet> mockDeserializer() {
+        return mock(BiFunction.class);
+    }
+}

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


More information about the commits mailing list