[java-plugin-shibd-oidc] branch dev/JSHIBDOIDC-31-cred-resolver updated: WIP: Adding the new credential resolver to the decryption key lookup

Codeberg noreply at shibboleth.net
Fri Jul 31 12:11:44 UTC 2026


This is an automated email from the git hooks/post-receive script.

codeberg pushed a commit to branch dev/JSHIBDOIDC-31-cred-resolver
in repository java-plugin-shibd-oidc.

View the commit online:
https://codeberg.org/Shibboleth/java-plugin-shibd-oidc/commit/a5dbceb5f5d5fe9db28b3b7a2312bf017e8e3b6f

The following commit(s) were added to refs/heads/dev/JSHIBDOIDC-31-cred-resolver by this push:
     new a5dbceb  WIP: Adding the new credential resolver to the decryption key lookup
a5dbceb is described below

commit a5dbceb5f5d5fe9db28b3b7a2312bf017e8e3b6f
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Fri Jul 31 13:11:37 2026 +0100

    WIP: Adding the new credential resolver to the decryption key lookup
    
     - this works, but the StaticFilteringCriterionCredentialResolver needs
    more explaining, and there is criterion filtering happening at a number
    of overlapping levels e.g. StaticFilteringCriterionCredentialResolver
    and LocalJOSEObjectCredentialResolver which needs to be cleaned up.
---
 .../net/shibboleth/sp/oidc/testing/TestHelper.java |  38 ++++++-
 .../net/shibboleth/sp/service/agent/postconfig.xml |  41 +++++--
 .../flows/AbstractOIDCTokenConsumerFlowTest.java   |   7 +-
 .../sp/oidc/flows/OIDCTokenConsumerFlowTest.java   |  68 +++++++++++-
 .../src/test/resources/logback-flow-test.xml       |   4 +
 .../idp/module/conf/sp/oidc-credentials.xml        |   2 +
 .../net/shibboleth/sp/oidc-test-beans.xml          |   1 +
 ...StaticFilteringCriterionCredentialResolver.java | 119 +++++++++++++++++++++
 8 files changed, 266 insertions(+), 14 deletions(-)

diff --git a/sp-oidc-api/src/test/java/net/shibboleth/sp/oidc/testing/TestHelper.java b/sp-oidc-api/src/test/java/net/shibboleth/sp/oidc/testing/TestHelper.java
index 2cdc89b..28290c8 100644
--- a/sp-oidc-api/src/test/java/net/shibboleth/sp/oidc/testing/TestHelper.java
+++ b/sp-oidc-api/src/test/java/net/shibboleth/sp/oidc/testing/TestHelper.java
@@ -273,6 +273,31 @@ public final class TestHelper {
     @Nonnull public static JWT createJWT(@Nonnull final JWTClaimsSet payload, @Nullable final JWSAlgorithm sigAlg, 
             @Nullable final JWEAlgorithm jweAlg, @Nullable final EncryptionMethod enc, 
             @Nullable final Credential sigKey, @Nullable final Credential encKey) throws JOSEException, ParseException {
+    	return createJWT(payload, sigAlg, jweAlg, enc, sigKey, encKey, null);
+    }
+    
+    /**
+     * Create a JWT from the given payload. The JWT can either be plain, or signed and encrypted. If encrypted, it must
+     * be signed. 
+     * 
+     * @param payload the payload to create the JWT from
+     * @param sigAlg the signature alg to use. {@code Null} if the JWT is not going to be signed and/or encrypted.
+     * @param jweAlg the encryption alg to use. {@code Null} if the JWT is not going to be encrypted. Note, if encrypted
+     *                  the signature algorithm must also be supplied, otherwise a plain JWT will be returned.
+     * @param enc the content encryption algorithm to use.
+     * @param sigKey the key to use for signing
+     * @param encKey the key to use for encryption
+     * @param encKeyKid the keyID for the encryption key to add to the header.
+     * 
+     * @return and plain JWT, JWS, or JWE.
+     * 
+     * @throws JOSEException on error.
+     * @throws ParseException on error.
+     */
+    @Nonnull public static JWT createJWT(@Nonnull final JWTClaimsSet payload, @Nullable final JWSAlgorithm sigAlg, 
+            @Nullable final JWEAlgorithm jweAlg, @Nullable final EncryptionMethod enc, 
+            @Nullable final Credential sigKey, @Nullable final Credential encKey, @Nullable final String encKeyKid) 
+            		throws JOSEException, ParseException {
         
         // Sign first.
         if (sigAlg != null && sigKey != null) {
@@ -290,11 +315,22 @@ public final class TestHelper {
             }
             
             if (jweAlg != null && encKey != null) {
-                final JWEObject jweObject = 
+            	JWEObject jweObject = null;
+            	if (encKeyKid != null) {
+            		jweObject = 
+                            new JWEObject(new JWEHeader.Builder(jweAlg, enc)
+                            .contentType("JWT")
+                            .keyID(encKeyKid)
+                            .build(),
+                            new Payload(signedJWT));
+            	}
+            	else{
+            		jweObject = 
                         new JWEObject(new JWEHeader.Builder(jweAlg, enc)
                         .contentType("JWT")
                         .build(),
                         new Payload(signedJWT));
+            	}
                 
                 if (JWEAlgorithm.Family.RSA.contains(jweAlg)) {                    
                     jweObject.encrypt(new RSAEncrypter((RSAPublicKey)encKey.getPublicKey()));                    
diff --git a/sp-oidc-conf-impl/src/main/resources/META-INF/net/shibboleth/sp/service/agent/postconfig.xml b/sp-oidc-conf-impl/src/main/resources/META-INF/net/shibboleth/sp/service/agent/postconfig.xml
index 6f71b8b..3829842 100644
--- a/sp-oidc-conf-impl/src/main/resources/META-INF/net/shibboleth/sp/service/agent/postconfig.xml
+++ b/sp-oidc-conf-impl/src/main/resources/META-INF/net/shibboleth/sp/service/agent/postconfig.xml
@@ -256,16 +256,11 @@
             </list>
         </property>
     </bean>
-    
-    <bean id="shibboleth.sp.oidc.SigningCredentialsFactory"
-        class="net.shibboleth.oidc.profile.config.CredentialsListFactory"
-        c:_0="#{getObject('shibboleth.oidc.SigningCredentials')}" />
         
     <bean id="defaultOIDCKeyEncryptionCredentialResolver"
         class="net.shibboleth.oidc.security.credential.impl.ChainingJOSEObjectCredentialResolver">
         <constructor-arg>
             <list>
-                <!--  Used by the RP -->
                 <bean id="OIDCProviderMetadataCredentialResolver"
                     class="net.shibboleth.oidc.security.credential.impl.ProviderMetadataCredentialResolver"
                     c:remoteJwkSetCache-ref="shibboleth.oidc.RemoteJwkSetCache"
@@ -280,7 +275,6 @@
         class="net.shibboleth.oidc.security.credential.impl.ChainingJOSEObjectCredentialResolver">
         <constructor-arg>
             <list>
-                <!--  Used by the RP -->
                 <bean id="ClientSecretCriterionCredentialResolver"
                     class="net.shibboleth.oidc.security.credential.impl.ClientSecretCriterionCredentialResolver"/>
             </list>
@@ -296,18 +290,44 @@
     <!-- 
         A resolver to public/private key encryption keys global to the RP 
     -->
+    
+    <!-- This bridges the bean below that resolves decryption keys into our service while adding needed criteria. -->
+    <bean id="localDecryptionKEKResolver" class="net.shibboleth.sp.jose.config.impl.StaticFilteringCriterionCredentialResolver"
+            c:resolver-ref="shibboleth.sp.CredentialResolverBridge">
+        <constructor-arg name="criteria">
+            <set>
+                <bean class="org.opensaml.security.criteria.UsageCriterion">
+                    <constructor-arg>
+                        <util:constant static-field="org.opensaml.security.credential.UsageType.ENCRYPTION" />
+                    </constructor-arg>
+                </bean>
+                <bean class="org.opensaml.saml.criterion.ProtocolCriterion">
+                    <constructor-arg>
+                        <util:constant static-field="net.shibboleth.oidc.saml.xmlobject.Constants.OIDC_PROTOCOL_URI" />
+                    </constructor-arg>
+                </bean>
+            </set>
+        </constructor-arg>
+    </bean>
+    
     <bean id="defaultOIDCKeyDecryptionCredentialResolver"
         class="net.shibboleth.oidc.security.credential.impl.ChainingJOSEObjectCredentialResolver">
         <constructor-arg>
             <list>
-                <bean class="net.shibboleth.oidc.security.credential.impl.LocalJOSEObjectCredentialResolver">
-                    <constructor-arg name="localCredentialResolver"><!-- TODO change to the shibboleth.sp.CredentialResolverBridge -->
+            <!-- 
+                This resolver pulls out keys from JWT headers and matches them to local credentials. In addition, 
+                it adds local credentials if none are present in the headers, using the keyId, if present, for 
+                credential resolvers to use in matching local keys.
+             -->
+                <bean id="localDecryptionCredentialResolver" 
+                    class="net.shibboleth.oidc.security.credential.impl.LocalJOSEObjectCredentialResolver"
+                    c:localCredentialResolver-ref="localDecryptionKEKResolver">
+                    <!-- <constructor-arg name="localCredentialResolver">
                         <bean 
                             class="net.shibboleth.oidc.security.credential.impl.CollectionJOSEObjectCredentialResolver"
                             c:credentials-ref="shibboleth.oidc.EncryptionCredentials" />
-                    </constructor-arg>
+                    </constructor-arg> -->
                 </bean>
-                <!--  Used by the RP -->
                 <bean id="CriterionCredentialResolver"
                     class="net.shibboleth.oidc.security.credential.impl.ClientSecretCriterionCredentialResolver"/>
             </list>
@@ -340,6 +360,7 @@
         class="net.shibboleth.oidc.security.credential.impl.BasicJOSEObjectCredentialResolver" />
     
      <!-- A resolver for resolving trusted credentials to match against those resolved from the JWT -->
+     <!-- TODO the client_secret credential should come from the new credential resolver -->
     <bean id="defaultSignedJWTTrustedCredentialResolver"
         class="net.shibboleth.oidc.security.credential.impl.ChainingJOSEObjectCredentialResolver">
         <constructor-arg>
diff --git a/sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/AbstractOIDCTokenConsumerFlowTest.java b/sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/AbstractOIDCTokenConsumerFlowTest.java
index 6a75ad2..fd136a5 100644
--- a/sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/AbstractOIDCTokenConsumerFlowTest.java
+++ b/sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/AbstractOIDCTokenConsumerFlowTest.java
@@ -200,13 +200,16 @@ public abstract class AbstractOIDCTokenConsumerFlowTest extends AbstractSPFlowTe
      * @param expiry expiry time
      * @param issuedAt issue time
      * @param nonce the nonce
+     * @param additionalClaims any additional claims
+     * @param encKeyId the KeyID of the encryption key to add to the headers
+     * 
      * @return the tokens
      * @throws ParseException 
      * @throws JOSEException 
      */
     protected OIDCTokenResponse constructSuccessfulTokenResponseSignedAndEncrypted(
             @Nonnull final Instant expiry, @Nonnull final Instant issuedAt, @Nullable final String nonce,
-            @Nullable final Map<String, Object> additionalClaims) 
+            @Nullable final Map<String, Object> additionalClaims, @Nullable final String encKeyId) 
                     throws JOSEException, ParseException {
 
          final JWTClaimsSet.Builder claimsSetBuilder = new JWTClaimsSet.Builder()
@@ -228,7 +231,7 @@ public abstract class AbstractOIDCTokenConsumerFlowTest extends AbstractSPFlowTe
          
          final JWT encryptedIdToken = 
                  TestHelper.createJWT(claimsSet, JWSAlgorithm.RS256, JWEAlgorithm.RSA_OAEP_256, 
-                         EncryptionMethod.A128CBC_HS256, opSigningCredential, rpEncryptionCredential);
+                         EncryptionMethod.A128CBC_HS256, opSigningCredential, rpEncryptionCredential, encKeyId);
          
          final AccessToken accessToken = new BearerAccessToken("fake-access-token-value", 3600, null);
          final RefreshToken refreshToken = new RefreshToken("fake-refresh-token-value");
diff --git a/sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/OIDCTokenConsumerFlowTest.java b/sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/OIDCTokenConsumerFlowTest.java
index d6bb66f..c45892a 100644
--- a/sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/OIDCTokenConsumerFlowTest.java
+++ b/sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/OIDCTokenConsumerFlowTest.java
@@ -261,7 +261,7 @@ public class OIDCTokenConsumerFlowTest extends AbstractOIDCTokenConsumerFlowTest
     public void testSuccess_SignedEncryptedIDToken_PlainUserInfo() throws Exception {
         
         mockOIDCEndpoints(constructSuccessfulTokenResponseSignedAndEncrypted(Instant.now().plusSeconds(3600), 
-                Instant.now(), null, null), constructJSONUserInfoResponse());
+                Instant.now(), null, null, null), constructJSONUserInfoResponse());
 
         final AuthenticationSuccessResponse response = 
                 TestHelper.buildOIDCAuthorizationCodeResponse(TestConstants.RESPONSE_URL, ResponseMode.QUERY, 
@@ -286,6 +286,72 @@ public class OIDCTokenConsumerFlowTest extends AbstractOIDCTokenConsumerFlowTest
                 TestConstants.RESOURCE_URL);
     }
     
+    /**
+     * Test successful flow with a signed and encrypted id_token and a plain user info response. The kid of the key 
+     * used to encrypt the IDToken is in the Headers.
+     * 
+     * @throws IOException on error
+     */
+    @Test
+    public void testSuccess_SignedEncryptedIDToken_WithDecryptionKID_PlainUserInfo() throws Exception {
+        
+        mockOIDCEndpoints(constructSuccessfulTokenResponseSignedAndEncrypted(Instant.now().plusSeconds(3600), 
+                Instant.now(), null, null, rpEncryptionCredential.getKid()), constructJSONUserInfoResponse());
+
+        final AuthenticationSuccessResponse response = 
+                TestHelper.buildOIDCAuthorizationCodeResponse(TestConstants.RESPONSE_URL, ResponseMode.QUERY, 
+                        TestConstants.STATE_TOKEN);         
+        final DDF input = buildRemotedQueryStringResponse(response);
+        
+        // Add cookies
+        input.addmember("http.headers.Cookie").unsafe_string(TestHelper.buildCookieHeader(
+                TestConstants.STATE_TOKEN, 
+                TestConstants.APPLICATION_ID_REQUEST_OBJECT,
+                TestHelper.buildAuthenticationState(null, false, null), true));
+        
+        setApplicationRequest(TestConstants.APPLICATION_ID_REQUEST_OBJECT, input);
+
+        final FlowExecutionResult result = flowExecutor.launchExecution(TestConstants.FLOW_ID, null, externalContext);
+        assertFlowExecutionResult(result, TestConstants.FLOW_ID);
+        assertFlowExecutionOutcome(result.getOutcome());
+        final DDF output = assertOutputMessageSuccess(result);
+        assert output != null;
+        System.out.println("test output: " + output.toString());
+        validateOutputMessage(result, CollectionSupport.setOf("sub","mail","displayName","eduPersonScopedAffiliation"),
+                TestConstants.RESOURCE_URL);
+    }
+    
+    /**
+     * Test failure flow with a signed and encrypted id_token and a plain user info response. The kid of the key 
+     * used to encrypt the IDToken is not one that matches to a local key defined.
+     * 
+     * @throws IOException on error
+     */
+    @Test
+    public void testFail_SignedEncryptedIDToken_WithDifferentDecryptionKID_PlainUserInfo() throws Exception {
+        
+        mockOIDCEndpoints(constructSuccessfulTokenResponseSignedAndEncrypted(Instant.now().plusSeconds(3600), 
+                Instant.now(), null, null, "wrong-encryption-keyid"), constructJSONUserInfoResponse());
+
+        final AuthenticationSuccessResponse response = 
+                TestHelper.buildOIDCAuthorizationCodeResponse(TestConstants.RESPONSE_URL, ResponseMode.QUERY, 
+                        TestConstants.STATE_TOKEN);         
+        final DDF input = buildRemotedQueryStringResponse(response);
+        
+        // Add cookies
+        input.addmember("http.headers.Cookie").unsafe_string(TestHelper.buildCookieHeader(
+                TestConstants.STATE_TOKEN, 
+                TestConstants.APPLICATION_ID_REQUEST_OBJECT,
+                TestHelper.buildAuthenticationState(null, false, null), true));
+        
+        setApplicationRequest(TestConstants.APPLICATION_ID_REQUEST_OBJECT, input);
+
+        final FlowExecutionResult result = flowExecutor.launchExecution(TestConstants.FLOW_ID, null, externalContext);
+        assertFlowExecutionResult(result, TestConstants.FLOW_ID);
+        assertFlowExecutionOutcome(result.getOutcome());
+        assertOutputMessageEvent(result, EventIds.MESSAGE_PROC_ERROR);
+    }
+    
     /**
      * Test successful flow with a signed id_token and a JWT user info response.
      * 
diff --git a/sp-oidc-conf-impl/src/test/resources/logback-flow-test.xml b/sp-oidc-conf-impl/src/test/resources/logback-flow-test.xml
index a89c91c..fedae41 100644
--- a/sp-oidc-conf-impl/src/test/resources/logback-flow-test.xml
+++ b/sp-oidc-conf-impl/src/test/resources/logback-flow-test.xml
@@ -18,6 +18,10 @@
         <appender-ref ref="STDOUT" />
     </logger>
     
+    <logger name="org.opensaml.security" level="DEBUG" additivity="false">
+        <appender-ref ref="STDOUT" />
+    </logger>
+    
     <logger name="net.shibboleth.idp" level="DEBUG" additivity="false">
         <appender-ref ref="STDOUT" />
     </logger>
diff --git a/sp-oidc-conf-impl/src/test/resources/net/shibboleth/idp/module/conf/sp/oidc-credentials.xml b/sp-oidc-conf-impl/src/test/resources/net/shibboleth/idp/module/conf/sp/oidc-credentials.xml
index 4a26085..955a893 100644
--- a/sp-oidc-conf-impl/src/test/resources/net/shibboleth/idp/module/conf/sp/oidc-credentials.xml
+++ b/sp-oidc-conf-impl/src/test/resources/net/shibboleth/idp/module/conf/sp/oidc-credentials.xml
@@ -11,6 +11,8 @@
                            
        default-init-method="initialize"
        default-destroy-method="destroy">
+       
+       <!--  TODO: with changes to credential resolver, remove this!! -->
     
      <!--
     This file contains default OIDC signing and encryption credentials. This file should be imported into agents.xml.
diff --git a/sp-oidc-conf-impl/src/test/resources/net/shibboleth/sp/oidc-test-beans.xml b/sp-oidc-conf-impl/src/test/resources/net/shibboleth/sp/oidc-test-beans.xml
index 32ee7bf..084a430 100644
--- a/sp-oidc-conf-impl/src/test/resources/net/shibboleth/sp/oidc-test-beans.xml
+++ b/sp-oidc-conf-impl/src/test/resources/net/shibboleth/sp/oidc-test-beans.xml
@@ -28,6 +28,7 @@
     <bean id="shibboleth.oidc.JWKCredential" abstract="true"
         class="net.shibboleth.oidc.security.credential.BasicJWKCredentialFactoryBean" />
 
+    <!-- These need to be the same as those resolvable by the security configuration used -->
     <bean id="dummy.op.signing.Credential" parent="shibboleth.oidc.JWKCredential"
         p:resource="%{idp.home}/credentials/op/op-signing-rsa.jwk" p:throwIfNull="false" />
         
diff --git a/sp-oidc-impl/src/main/java/net/shibboleth/sp/jose/config/impl/StaticFilteringCriterionCredentialResolver.java b/sp-oidc-impl/src/main/java/net/shibboleth/sp/jose/config/impl/StaticFilteringCriterionCredentialResolver.java
new file mode 100644
index 0000000..66d2eb9
--- /dev/null
+++ b/sp-oidc-impl/src/main/java/net/shibboleth/sp/jose/config/impl/StaticFilteringCriterionCredentialResolver.java
@@ -0,0 +1,119 @@
+/*
+ * 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.sp.jose.config.impl;
+
+import java.util.Collection;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.security.credential.Credential;
+import org.opensaml.security.credential.CredentialResolver;
+import org.opensaml.security.credential.criteria.impl.EvaluableCredentialCriterion;
+import org.opensaml.security.credential.impl.AbstractCriteriaFilteringCredentialResolver;
+
+import net.shibboleth.oidc.security.credential.JOSEObjectCredentialResolver;
+import net.shibboleth.shared.annotation.ParameterName;
+import net.shibboleth.shared.annotation.constraint.NonnullElements;
+import net.shibboleth.shared.collection.CollectionSupport;
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.resolver.CriteriaSet;
+import net.shibboleth.shared.resolver.Criterion;
+import net.shibboleth.shared.resolver.ResolverException;
+
+/**
+ * A filtering resolver that adds statically-defined {@link Criterion} objects to a new {@link CriteriaSet}
+ * fed into a wrapped, delegated, resolver. 
+ * 
+ * <p>The new criteria set may optionally include the criteria supplied by the caller. The caller's input 
+ * {@link CriteriaSet} instance is never passed directly to the wrapped resolver.</p>
+ * 
+ * <p>After credential resolution has been performed by the wrapped resolver, the filtering behaviour from 
+ * {@link AbstractCriteriaFilteringCredentialResolver} is applied using any {@link EvaluableCredentialCriterion} 
+ * instances present in the input criteria set (not including the static criteria, or anything from the underlying
+ * resolver). Technically these filters are added as an additional filter-view layer on top of whatever is produced
+ * by the wrapped resolver.</p>
+ * 
+ * <p>Restated, the statically-defined criteria (and, optionally, the caller-supplied criteria) are passed to the 
+ * wrapped credential resolver, which may use them as it sees fit during credential resolution. Once the wrapped
+ * resolver has finished its resolution, this resolver adds another filter layer to the resulting credential iterable 
+ * using only those {@link EvaluableCredentialCriterion} instances present in the input criteria set.</p>
+ * 
+ * <p>It exports the additional {@link JOSEObjectCredentialResolver} interface so it can be used for JOSE credential
+ * resolution. Effectively bridging between JOSE credential use cases, and an underlying, generic, 
+ * CredentialResolver.</p>
+ * 
+ */
+public class StaticFilteringCriterionCredentialResolver extends AbstractCriteriaFilteringCredentialResolver 
+							implements JOSEObjectCredentialResolver {
+
+    /** List of credentials held by this resolver. */
+    @Nonnull private final CredentialResolver credentialResolver;
+    
+    /** Static criteria to add. */
+    @Nonnull private final Collection<Criterion> staticCriteria;
+    
+    /** 
+     * If the {@link CriteriaSet} from the caller should be copied into the {@link CriteriaSet} passed into the 
+     * credential resolver. Defaults to false if not set, where only the staticCriteria are passed through.
+     */
+    private final boolean enableCriteriaPassthrough;
+    
+    /**
+     * Constructor.
+     *
+     * @param resolver underlying resolver
+     * @param criteria static criteria to add to all resolutions
+     * @param passthrough If the {@link CriteriaSet} from the caller should be copied into the 
+     * 								{@link CriteriaSet} passed into the credential resolver
+     */
+    public StaticFilteringCriterionCredentialResolver(
+    		@Nonnull @ParameterName(name="resolver") final CredentialResolver resolver,
+            @Nullable @ParameterName(name="criteria") final Collection<Criterion> criteria,
+            @ParameterName(name="criteriaPassthrough") final boolean passthrough) {
+        credentialResolver = Constraint.isNotNull(resolver, "CredentialResolver cannot be null");
+        if (criteria != null) {
+            staticCriteria = CollectionSupport.copyToSet(criteria);
+        } else {
+            staticCriteria = CollectionSupport.emptySet();
+        }
+        enableCriteriaPassthrough = passthrough;
+    }
+    
+    /**
+     * Constructor.
+     *
+     * @param resolver underlying resolver
+     * @param criteria static criteria to add to all resolutions
+     */
+    public StaticFilteringCriterionCredentialResolver(
+    		@Nonnull @ParameterName(name="resolver") final CredentialResolver resolver,
+            @Nullable @ParameterName(name="criteria") final Collection<Criterion> criteria) {
+    	this(resolver, criteria, false);
+    }
+    
+    /** {@inheritDoc} */
+    @Override
+    @Nonnull @NonnullElements public Iterable<Credential> resolveFromSource(
+            @Nullable final CriteriaSet criteriaSet) throws ResolverException {
+    	final CriteriaSet finalCriteria = new CriteriaSet();
+    	if (enableCriteriaPassthrough && criteriaSet != null) {
+    		finalCriteria.addAll(criteriaSet);
+    	}
+        finalCriteria.addAll(staticCriteria);
+        return credentialResolver.resolve(finalCriteria);
+    }
+    
+}
\ 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