[java-idp-plugin-oidc-op-oidfed] 03/03: Iniitial version of the hook to wire additional claims into entity configuration
Codeberg
noreply at shibboleth.net
Thu Dec 4 07:24:06 UTC 2025
This is an automated email from the git hooks/post-receive script.
codeberg pushed a commit to branch main
in repository java-idp-plugin-oidc-op-oidfed.
View the commit online:
https://codeberg.org/Shibboleth/java-idp-plugin-oidc-op-oidfed/commit/1f0a94820d5265ece914af8685445977b678514e
commit 1f0a94820d5265ece914af8685445977b678514e
Author: Henri Mikkonen <henri.mikkonen at iki.fi>
AuthorDate: Thu Dec 4 09:23:25 2025 +0200
Iniitial version of the hook to wire additional claims into entity configuration
- shibboleth.oidfed.EntityConfigurationClaimLookupStrategies bean in conf/oidfed/oidfed-entity-configuration-claims.xml
- Type Map<String, Function<ProfileRequestContext,Object>> optionalClaimsLookupStrategies
- shibboleth.oidfed.RemoteTrustMark abstract bean can be used for fetching trust marks via remote API
- Trust Marks are stored in shibboleth.oidfed.TrustMarkMetadataCache
- Tests contain example of wiring trust mark both statically and dynamically via API
---
...esolverEntity.java => TrustedRemoteEntity.java} | 37 +---
.../profile/TrustedRemoteResolverEntity.java | 18 +-
.../op/oidfed/messaging/impl/TrustMarkRequest.java | 113 ++++++++++++
.../oidfed/messaging/impl/TrustMarkResponse.java | 87 +++++++++
.../metadata/DefaultTrustMarkFetchingStrategy.java | 114 ++++++++++++
...arkResponseContainerExpirationTimeStrategy.java | 59 ++++++
...kResponseSignatureValidationFilterStrategy.java | 128 +++++++++++++
.../oidfed/metadata/TrustMarkRequestCriterion.java | 79 ++++++++
.../metadata/TrustMarkResponseContainer.java | 75 ++++++++
.../profile/impl/BuildEntityConfiguration.java | 33 +++-
...TrustMarkFromMetadataCacheFetchingFunction.java | 204 +++++++++++++++++++++
...ntityConfigurationTrustMarksLookupStrategy.java | 77 ++++++++
.../META-INF/net.shibboleth.idp/postconfig.xml | 41 +++++
.../entity-configuration-beans.xml | 3 +-
.../oidfed/oidfed-entity-configuration-claims.xml | 17 ++
.../idp/plugin/oidc/op/oidfed/module.properties | 4 +
.../flow/oidfed/AbstractFederationFlowTest.java | 5 +-
.../flow/oidfed/EntityConfigurationFlowTest.java | 49 +++++
.../oidfed/oidfed-entity-configuration-claims.xml | 43 +++++
19 files changed, 1139 insertions(+), 47 deletions(-)
diff --git a/idp-oidfed-op-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/TrustedRemoteResolverEntity.java b/idp-oidfed-op-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/TrustedRemoteEntity.java
similarity index 54%
copy from idp-oidfed-op-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/TrustedRemoteResolverEntity.java
copy to idp-oidfed-op-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/TrustedRemoteEntity.java
index 005dd3a..624ed2e 100644
--- a/idp-oidfed-op-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/TrustedRemoteResolverEntity.java
+++ b/idp-oidfed-op-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/TrustedRemoteEntity.java
@@ -14,8 +14,6 @@
package net.shibboleth.idp.plugin.oidc.op.oidfed.profile;
-import java.util.Collection;
-
import javax.annotation.Nonnull;
import com.google.common.base.MoreObjects;
@@ -25,52 +23,36 @@ import net.shibboleth.shared.annotation.constraint.NotEmpty;
import net.shibboleth.shared.logic.Constraint;
/**
- * A trusted entity whose federation_resolve_endpoint is exploited.
+ * A trusted remote entity whose federation endpoints are exploited.
*/
-public class TrustedRemoteResolverEntity {
+public class TrustedRemoteEntity {
- /** The entity ID of the remote resolver. */
+ /** The entity ID of the remote entity. */
@Nonnull @NotEmpty private final String entityId;
- /** The trust anchors to be used within the API request. */
- @Nonnull @NotEmpty private final Collection<String> trustAnchors;
-
/**
* Constructor.
*
- * @param entity ntity ID of the remote resolver
- * @param anchors trust anchors to be used within the API request
+ * @param entity entity ID of the remote entity
*/
- public TrustedRemoteResolverEntity(@Nonnull @NotEmpty @ParameterName(name="entity") final String entity,
- @Nonnull @NotEmpty @ParameterName(name="anchors") final Collection<String> anchors) {
+ public TrustedRemoteEntity(@Nonnull @NotEmpty @ParameterName(name="entity") final String entity) {
entityId = Constraint.isNotEmpty(entity, "Entity ID cannot be empty");
- trustAnchors = Constraint.isNotEmpty(anchors, "Trust Anchors cannot be empty");
}
/**
- * Get the entity ID of the remote resolver.
+ * Get the entity ID of the remote entity.
*
- * @return entity ID of the remote resolver
+ * @return entity ID of the remote entity
*/
@Nonnull @NotEmpty public String getEntityId() {
return entityId;
}
- /**
- * Get the trust anchors to be used within the API request.
- *
- * @return trust anchors to be used within the API request
- */
- @Nonnull @NotEmpty public Collection<String> getTrustAnchors() {
- return trustAnchors;
- }
-
/** {@inheritDoc} */
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("entityId", getEntityId())
- .add("trustAnchors", getTrustAnchors())
.toString();
}
@@ -86,8 +68,7 @@ public class TrustedRemoteResolverEntity {
if (getClass() != obj.getClass()) {
return false;
}
- final TrustedRemoteResolverEntity other = (TrustedRemoteResolverEntity) obj;
- return entityId.equals(other.entityId) &&
- trustAnchors.containsAll(other.trustAnchors) && other.trustAnchors.containsAll(trustAnchors);
+ final TrustedRemoteEntity other = (TrustedRemoteEntity) obj;
+ return entityId.equals(other.entityId);
}
}
diff --git a/idp-oidfed-op-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/TrustedRemoteResolverEntity.java b/idp-oidfed-op-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/TrustedRemoteResolverEntity.java
index 005dd3a..4871224 100644
--- a/idp-oidfed-op-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/TrustedRemoteResolverEntity.java
+++ b/idp-oidfed-op-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/TrustedRemoteResolverEntity.java
@@ -27,10 +27,7 @@ import net.shibboleth.shared.logic.Constraint;
/**
* A trusted entity whose federation_resolve_endpoint is exploited.
*/
-public class TrustedRemoteResolverEntity {
-
- /** The entity ID of the remote resolver. */
- @Nonnull @NotEmpty private final String entityId;
+public class TrustedRemoteResolverEntity extends TrustedRemoteEntity {
/** The trust anchors to be used within the API request. */
@Nonnull @NotEmpty private final Collection<String> trustAnchors;
@@ -43,19 +40,10 @@ public class TrustedRemoteResolverEntity {
*/
public TrustedRemoteResolverEntity(@Nonnull @NotEmpty @ParameterName(name="entity") final String entity,
@Nonnull @NotEmpty @ParameterName(name="anchors") final Collection<String> anchors) {
- entityId = Constraint.isNotEmpty(entity, "Entity ID cannot be empty");
+ super(entity);
trustAnchors = Constraint.isNotEmpty(anchors, "Trust Anchors cannot be empty");
}
- /**
- * Get the entity ID of the remote resolver.
- *
- * @return entity ID of the remote resolver
- */
- @Nonnull @NotEmpty public String getEntityId() {
- return entityId;
- }
-
/**
* Get the trust anchors to be used within the API request.
*
@@ -87,7 +75,7 @@ public class TrustedRemoteResolverEntity {
return false;
}
final TrustedRemoteResolverEntity other = (TrustedRemoteResolverEntity) obj;
- return entityId.equals(other.entityId) &&
+ return getEntityId().equals(other.getEntityId()) &&
trustAnchors.containsAll(other.trustAnchors) && other.trustAnchors.containsAll(trustAnchors);
}
}
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/messaging/impl/TrustMarkRequest.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/messaging/impl/TrustMarkRequest.java
new file mode 100644
index 0000000..2a080ab
--- /dev/null
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/messaging/impl/TrustMarkRequest.java
@@ -0,0 +1,113 @@
+/*
+ * 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.oidfed.messaging.impl;
+
+import java.net.URI;
+
+import javax.annotation.Nonnull;
+
+import com.google.common.base.MoreObjects;
+import com.nimbusds.oauth2.sdk.Request;
+import com.nimbusds.oauth2.sdk.http.HTTPRequest;
+
+import net.shibboleth.shared.annotation.constraint.NotEmpty;
+import net.shibboleth.shared.logic.Constraint;
+
+/**
+ * Request message to the OpenID federation Trust Mark endpoint.
+ */
+public class TrustMarkRequest implements Request {
+
+ /** The endpoint URI of the request. */
+ @Nonnull private final URI endpointUri;
+
+ /** The identifier for the type of the Trust Mark. */
+ @Nonnull @NotEmpty private final String trustMarkType;
+
+ /** The entity ID of the Entity to which the Trust Mark is issued. */
+ @Nonnull @NotEmpty private final String subject;
+
+ /**
+ * Constructor.
+ *
+ * @param uri endpoint URI
+ * @param type trust _mark type
+ * @param sub subject entity ID
+ */
+ public TrustMarkRequest(@Nonnull final URI uri, @Nonnull final String type, @Nonnull final String sub) {
+ endpointUri = Constraint.isNotNull(uri, "Endpoint URI cannot be null");
+ trustMarkType = Constraint.isNotEmpty(type, "Trust Mark type cannot be null or empty");
+ subject = Constraint.isNotEmpty(sub, "Subject cannot be null or empty");
+ }
+
+ /** {@inheritDoc} */
+ @Override @Nonnull
+ public URI getEndpointURI() {
+ return endpointUri;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public HTTPRequest toHTTPRequest() {
+ //TODO
+ return null;
+ }
+
+ /**
+ * Get the trust mark type for the request.
+ *
+ * @return trust mark type
+ */
+ @Nonnull @NotEmpty public String getTrustMarkType() {
+ return trustMarkType;
+ }
+
+ /**
+ * Get the subject for the request.
+ *
+ * @return subject
+ */
+ @Nonnull @NotEmpty public String getSubject() {
+ return subject;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("subject", getSubject())
+ .add("trustMarkType", getTrustMarkType())
+ .add("endpointURI", getEndpointURI())
+ .toString();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ final TrustMarkRequest other = (TrustMarkRequest) obj;
+ return endpointUri.equals(other.endpointUri) && subject.equals(other.subject) &&
+ trustMarkType.equals(other.trustMarkType);
+ }
+
+}
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/messaging/impl/TrustMarkResponse.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/messaging/impl/TrustMarkResponse.java
new file mode 100644
index 0000000..c132acb
--- /dev/null
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/messaging/impl/TrustMarkResponse.java
@@ -0,0 +1,87 @@
+/*
+ * 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.oidfed.messaging.impl;
+
+import javax.annotation.Nonnull;
+
+import com.nimbusds.common.contenttype.ContentType;
+import com.nimbusds.jose.JOSEObjectType;
+import com.nimbusds.jwt.SignedJWT;
+import com.nimbusds.oauth2.sdk.ParseException;
+import com.nimbusds.oauth2.sdk.http.HTTPResponse;
+
+import net.shibboleth.shared.logic.ConstraintViolationException;
+import net.shibboleth.shared.primitive.StringSupport;
+
+/**
+ * Response message to the OpenID federation Trust Mark endpoint.
+ */
+public class TrustMarkResponse extends AbstractSignedJWTResponse {
+
+ /** The JWT type header. */
+ @Nonnull
+ public static final JOSEObjectType JWT_TYPE_HEADER = new JOSEObjectType("trust-mark+jwt");
+
+ /** The content type. */
+ @Nonnull public static final ContentType HTTP_RESPONSE_CONTENT_TYPE =
+ new ContentType("application", JWT_TYPE_HEADER.toString());
+
+ /**
+ * Constructor.
+ *
+ * @param statement JWT
+ */
+ public TrustMarkResponse(@Nonnull final SignedJWT statement) {
+ super(statement);
+ }
+
+ /** {@inheritDoc} */
+ protected ContentType getHttpResponseContentType() {
+ return HTTP_RESPONSE_CONTENT_TYPE;
+ }
+
+ /** {@inheritDoc} */
+ protected JOSEObjectType getJWTTypeHeader() {
+ return JWT_TYPE_HEADER;
+ }
+
+ /**
+ * Parses a trust mark success response from the given HTTP response.
+ *
+ * @param httpResponse the HTTP response
+ * @return resolve entity success response
+ * @throws ParseException if HTTP response could not be parsed into trust mark response
+ */
+ @Nonnull
+ public static TrustMarkResponse parse(@Nonnull final HTTPResponse httpResponse)
+ throws ParseException {
+
+ httpResponse.ensureStatusCode(HTTPResponse.SC_OK);
+ httpResponse.ensureEntityContentType(HTTP_RESPONSE_CONTENT_TYPE);
+ final String content = httpResponse.getContent();
+
+ if (StringSupport.trimOrNull(content) == null) {
+ throw new ParseException("Message body is empty");
+ }
+
+ try {
+ final SignedJWT jwt = SignedJWT.parse(httpResponse.getContent());
+ assert jwt != null;
+ return new TrustMarkResponse(jwt);
+ } catch (final java.text.ParseException | ConstraintViolationException e) {
+ throw new ParseException(e.getMessage(), e);
+ }
+ }
+}
\ No newline at end of file
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/DefaultTrustMarkFetchingStrategy.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/DefaultTrustMarkFetchingStrategy.java
new file mode 100644
index 0000000..890d024
--- /dev/null
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/DefaultTrustMarkFetchingStrategy.java
@@ -0,0 +1,114 @@
+/*
+ * 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.oidfed.metadata;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.apache.hc.client5.http.classic.methods.HttpGet;
+import org.apache.hc.core5.http.ClassicHttpRequest;
+import org.apache.hc.core5.http.ClassicHttpResponse;
+import org.apache.hc.core5.http.NameValuePair;
+import org.apache.hc.core5.http.ProtocolException;
+import org.apache.hc.core5.http.io.entity.EntityUtils;
+import org.apache.hc.core5.http.message.BasicNameValuePair;
+import org.apache.hc.core5.net.URIBuilder;
+import org.slf4j.Logger;
+
+import com.nimbusds.oauth2.sdk.ParseException;
+import com.nimbusds.oauth2.sdk.http.HTTPResponse;
+
+import net.shibboleth.idp.plugin.oidc.op.oidfed.messaging.impl.TrustMarkRequest;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.messaging.impl.TrustMarkResponse;
+import net.shibboleth.shared.annotation.constraint.ThreadSafeAfterInit;
+import net.shibboleth.shared.primitive.LoggerFactory;
+import net.shibboleth.shared.resolver.CriteriaSet;
+
+/**
+ * Default strategy for fetching trust mark via Trust Mark API for the request specified in the criteria set. The
+ * response is stored inside a {@link TrustMarkResponseContainer}.
+ */
+ at ThreadSafeAfterInit
+public class DefaultTrustMarkFetchingStrategy
+ extends AbstractFederationEndpointResponseFetchingStrategy<TrustMarkRequest, TrustMarkResponseContainer> {
+
+ /** Class logger. */
+ @Nonnull private Logger log = LoggerFactory.getLogger(DefaultTrustMarkFetchingStrategy.class);
+
+ /**
+ * Constructor.
+ */
+ public DefaultTrustMarkFetchingStrategy() {
+ setCriteriaToRequestStrategy(criteria -> {
+ final TrustMarkRequestCriterion requestCriterion = criteria.get(TrustMarkRequestCriterion.class);
+ if (requestCriterion == null) {
+ log.debug("No request criterion given, returning null");
+ return null;
+ }
+ return requestCriterion.getRequest();
+ });
+ }
+
+ /** {@inheritDoc} */
+ @Nullable protected ClassicHttpRequest initializeHttpRequest(@Nonnull final CriteriaSet criteria,
+ @Nonnull TrustMarkRequest request) {
+ final HttpGet httpRequest = new HttpGet(request.getEndpointURI());
+ final List<NameValuePair> nvps = new ArrayList<>();
+ nvps.add(new BasicNameValuePair("trust_mark_type", request.getTrustMarkType()));
+ nvps.add(new BasicNameValuePair("sub", request.getSubject()));
+ try {
+ final URI uri = new URIBuilder(httpRequest.getUri()).addParameters(nvps).build();
+ httpRequest.setUri(uri);
+ } catch (final URISyntaxException e) {
+ log.error("Could not create URI with the given parameters {}", request, e);
+ }
+ return httpRequest;
+ }
+
+ /** {@inheritDoc} */
+ @Nullable protected TrustMarkResponseContainer parseHttpResponse(@Nonnull final CriteriaSet criteria,
+ @Nonnull final TrustMarkRequest request, @Nullable final ClassicHttpResponse response)
+ throws ProtocolException, IOException {
+ final ResponseContainerExpirationCriterion expirationCriterion =
+ criteria.get(ResponseContainerExpirationCriterion.class);
+ if (expirationCriterion == null) {
+ log.debug("No expiration criterion given, returning null");
+ return null;
+ }
+ if (response != null) {
+ final HTTPResponse nimbusResponse = new HTTPResponse(response.getCode());
+ nimbusResponse.setContent(EntityUtils.toString(response.getEntity()));
+ try {
+ nimbusResponse.setContentType(response.getEntity().getContentType());
+ final TrustMarkResponse parsedResponse = TrustMarkResponse.parse(nimbusResponse);
+ final Instant expirationTime = expirationCriterion.getExpirationInstant();
+ return new TrustMarkResponseContainer(parsedResponse, request, parsedResponse.getJWT(), expirationTime);
+ } catch (final ParseException e) {
+ log.warn("Could not parse resolve entity response from URI: {}", request.getEndpointURI(), e);
+ }
+ } else {
+ log.debug("Unable to fetch resolve entity response from URI: {} (null response)", request.getEndpointURI());
+ }
+
+ return null;
+ }
+}
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/DefaultTrustMarkResponseContainerExpirationTimeStrategy.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/DefaultTrustMarkResponseContainerExpirationTimeStrategy.java
new file mode 100644
index 0000000..a76484c
--- /dev/null
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/DefaultTrustMarkResponseContainerExpirationTimeStrategy.java
@@ -0,0 +1,59 @@
+/*
+ * 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.oidfed.metadata;
+
+import java.text.ParseException;
+import java.time.Instant;
+import java.util.function.Function;
+
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.ThreadSafe;
+
+import net.shibboleth.idp.plugin.oidc.op.oidfed.messaging.impl.TrustMarkResponse;
+import net.shibboleth.oidc.metadata.cache.ExpirationTimeContext;
+
+/**
+ * Default strategy for fetching expiration time for the trust mark response container. The expiration instant is
+ * fetched from which is before: the success response message's JWT expiration time or the instant returned by
+ * {@link TrustMarkResponseContainer#getExpirationInstant()}.
+ */
+ at ThreadSafe
+public class DefaultTrustMarkResponseContainerExpirationTimeStrategy
+ implements Function<ExpirationTimeContext<TrustMarkResponseContainer>, Instant> {
+
+ /** {@inheritDoc} */
+ @Nullable public Instant apply(@Nullable final ExpirationTimeContext<TrustMarkResponseContainer> context) {
+ if (context == null) {
+ return null;
+ }
+ final Instant contextExpiration = context.getNow().plus(context.getMaxCacheDuration());
+ final TrustMarkResponseContainer container = context.getMetadata();
+ if (container == null || container.getExpirationInstant() == null) {
+ return contextExpiration;
+ }
+ final Instant containerExpiration = container.getExpirationInstant();
+ if (container.getResponse() instanceof TrustMarkResponse successResponse) {
+ try {
+ final Instant jwtExpiration =
+ successResponse.getJWT().getJWTClaimsSet().getExpirationTime().toInstant();
+ return jwtExpiration.isBefore(containerExpiration) ? jwtExpiration : containerExpiration;
+ } catch (final ParseException e) {
+ // ignore, use container expiration
+ }
+ }
+ return containerExpiration;
+ }
+
+}
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/DefaultTrustMarkResponseSignatureValidationFilterStrategy.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/DefaultTrustMarkResponseSignatureValidationFilterStrategy.java
new file mode 100644
index 0000000..4cebae4
--- /dev/null
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/DefaultTrustMarkResponseSignatureValidationFilterStrategy.java
@@ -0,0 +1,128 @@
+/*
+ * 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.oidfed.metadata;
+
+import java.text.ParseException;
+import java.util.List;
+import java.util.function.BiFunction;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.security.trust.TrustEngine;
+import org.slf4j.Logger;
+
+import com.nimbusds.jwt.SignedJWT;
+import com.nimbusds.openid.connect.sdk.federation.entities.EntityStatement;
+
+import net.shibboleth.idp.plugin.oidc.op.oidfed.messaging.impl.TrustMarkResponse;
+import net.shibboleth.oidc.metadata.cache.MetadataCache;
+import net.shibboleth.oidc.metadata.cache.MetadataCacheException;
+import net.shibboleth.oidc.metadata.filter.MetadataFilterContext;
+import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.shared.annotation.constraint.ThreadSafeAfterInit;
+import net.shibboleth.shared.component.ComponentInitializationException;
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.primitive.LoggerFactory;
+import net.shibboleth.shared.resolver.CriteriaSet;
+
+/**
+ * Default signature validating filter for Trust Mark response. The signature validation is performed via
+ * configurable {@link TrustEngine}.
+ */
+ at ThreadSafeAfterInit
+public class DefaultTrustMarkResponseSignatureValidationFilterStrategy
+ extends AbstractTrustEngineSignatureValidationComponent
+ implements BiFunction<TrustMarkResponseContainer, MetadataFilterContext, TrustMarkResponseContainer> {
+
+ /** Class logger. */
+ @Nonnull private Logger log =
+ LoggerFactory.getLogger(DefaultTrustMarkResponseSignatureValidationFilterStrategy.class);
+
+ /** Cache used to fetch the issuer entity configuration from. */
+ @NonnullAfterInit private MetadataCache<List<List<EntityStatement>>> trustChainCache;
+
+ /**
+ * Set the cache used to fetch the trust chain for the trust mark issuer from.
+ *
+ * @param cache cache used to fetch the trust chain for the trust mark issuer from
+ */
+ public void setTrustChainCache(@Nonnull final MetadataCache<List<List<EntityStatement>>> cache) {
+ checkSetterPreconditions();
+ trustChainCache = Constraint.isNotNull(cache, "Trust Chain cache cannot be null");
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void doInitialize() throws ComponentInitializationException {
+ super.doInitialize();
+ if (trustChainCache == null) {
+ throw new ComponentInitializationException("Trust Chain cache cannot be null");
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override @Nullable
+ public TrustMarkResponseContainer apply(@Nullable final TrustMarkResponseContainer response,
+ @Nullable final MetadataFilterContext filterContext) {
+ checkComponentActive();
+ if (response == null) {
+ return null;
+ }
+ if (response.getResponse() instanceof TrustMarkResponse successResponse) {
+ final SignedJWT jwt = successResponse.getJWT();
+ final String entityId;
+ try {
+ entityId = jwt.getJWTClaimsSet().getIssuer();
+ } catch (final ParseException e) {
+ log.error("Could not parse issuer entity ID value from the response", e);
+ return null;
+ }
+ final EntityStatement issuerStatement = fetchIssuerStatement(entityId);
+ if (issuerStatement == null) {
+ return null;
+ }
+ log.trace("Starting signature validation of success response from {}", entityId);
+ final CriteriaSet criteria = new CriteriaSet(new SubjectEntityStatementCriterion(issuerStatement));
+ if (validateJwt(jwt, criteria, entityId)) {
+ return response;
+ }
+ } else {
+ log.trace("Ignoring signature validation for the error response");
+ return response;
+ }
+ return null;
+ }
+
+ /**
+ * Fetch the issuer entity configuration from the trust chain metadata cache.
+ *
+ * @param issuer the issuer entity ID
+ * @return the issuer entity configuration, or null if could not be fetched
+ */
+ @Nullable protected EntityStatement fetchIssuerStatement(@Nonnull final String issuer) {
+ final CriteriaSet criteria = new CriteriaSet(new SubjectEntityIDCriterion(issuer));
+ try {
+ final List<List<List<EntityStatement>>> result = trustChainCache.get(criteria);
+ if (!result.isEmpty() && !result.get(0).isEmpty() && !result.get(0).get(0).isEmpty()) {
+ return result.get(0).get(0).get(0);
+ }
+ } catch (final MetadataCacheException e) {
+ log.debug("Error while fetching issuer trust chain for {}", issuer, e);
+ }
+ log.warn("Could not fetch entity configuration for {}", issuer);
+ return null;
+ }
+}
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/TrustMarkRequestCriterion.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/TrustMarkRequestCriterion.java
new file mode 100644
index 0000000..2629074
--- /dev/null
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/TrustMarkRequestCriterion.java
@@ -0,0 +1,79 @@
+/*
+ * 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.oidfed.metadata;
+
+import java.util.Objects;
+
+import javax.annotation.Nonnull;
+
+import net.shibboleth.idp.plugin.oidc.op.oidfed.messaging.impl.TrustMarkRequest;
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.resolver.Criterion;
+
+/**
+ * A {@link Criterion} representing request message to a Trust Mark API.
+ */
+public class TrustMarkRequestCriterion implements Criterion {
+
+ /** The request message. */
+ @Nonnull private final TrustMarkRequest request;
+
+ /**
+ * Constructor.
+ *
+ * @param requestMessage request message, must not be null
+ */
+ public TrustMarkRequestCriterion(@Nonnull final TrustMarkRequest requestMessage) {
+ request = Constraint.isNotNull(requestMessage, "Request cannot be null");
+ }
+
+ /**
+ * Get the request message.
+ *
+ * @return the request message
+ */
+ @Nonnull public TrustMarkRequest getRequest() {
+ return request;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ return "TrustMarkRequestCriterion [request=" + request + "]";
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ return Objects.hash(request);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ final TrustMarkRequestCriterion other = (TrustMarkRequestCriterion) obj;
+ return request.equals(other.request);
+ }
+
+}
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/TrustMarkResponseContainer.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/TrustMarkResponseContainer.java
new file mode 100644
index 0000000..755b8fc
--- /dev/null
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/TrustMarkResponseContainer.java
@@ -0,0 +1,75 @@
+/*
+ * 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.oidfed.metadata;
+
+import java.io.Serializable;
+import java.time.Instant;
+
+import javax.annotation.Nonnull;
+
+import com.nimbusds.jwt.SignedJWT;
+import com.nimbusds.oauth2.sdk.Response;
+
+import net.shibboleth.idp.plugin.oidc.op.oidfed.messaging.impl.TrustMarkRequest;
+import net.shibboleth.shared.logic.Constraint;
+
+/**
+ * A container class for metadata caches carrying request and response message details related to Trust Mark API.
+ */
+public class TrustMarkResponseContainer extends NimbusResponseContainer implements Serializable {
+
+ /** Serial version UID. */
+ private static final long serialVersionUID = 756269383556865370L;
+
+ /** Request message used for obtaining the Trust Mark. */
+ @Nonnull private final TrustMarkRequest request;
+
+ /** Trust Mark. */
+ @Nonnull private final SignedJWT trustMark;
+
+ /**
+ * Constructor.
+ *
+ * @param responseMessage response message
+ * @param requestMessage request message
+ * @param jwt trust mark
+ * @param expirationInstant expiration instant
+ */
+ public TrustMarkResponseContainer(@Nonnull final Response responseMessage,
+ @Nonnull final TrustMarkRequest requestMessage, @Nonnull final SignedJWT jwt,
+ @Nonnull final Instant expirationInstant) {
+ super(responseMessage, expirationInstant);
+ request = Constraint.isNotNull(requestMessage, "Request nessage cannot be null");
+ trustMark = Constraint.isNotNull(jwt, "Trust Mark JWT cannot be null");
+ }
+
+ /**
+ * Get request message.
+ *
+ * @return request message
+ */
+ public TrustMarkRequest getRequestMessage() {
+ return request;
+ }
+
+ /**
+ * Get trust mark.
+ *
+ * @return trust mark
+ */
+ public SignedJWT getTrustMark() {
+ return trustMark;
+ }
+}
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/BuildEntityConfiguration.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/BuildEntityConfiguration.java
index f453ba7..be1f5b4 100644
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/BuildEntityConfiguration.java
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/BuildEntityConfiguration.java
@@ -74,6 +74,9 @@ public class BuildEntityConfiguration extends AbstractBuildEntityStatementAction
/** Strategy used to locate trust_anchor_hints. */
@NonnullAfterInit private Function<ProfileRequestContext,List<String>> trustAnchorHintsLookupStrategy;
+ /** Strategies used to locate values for optional claims. */
+ @NonnullAfterInit private Map<String, Function<ProfileRequestContext,Object>> optionalClaimsLookupStrategies;
+
/** Metadata to publish. */
@NonnullBeforeExec private Map<String,Map<String,Object>> metadata;
@@ -134,6 +137,19 @@ public class BuildEntityConfiguration extends AbstractBuildEntityStatementAction
Constraint.isNotNull(strategy, "Trust anchor hints lookup strategy cannot be null");
}
+ /**
+ * Set the map of lookup strategies to locate values for optional claims.
+ *
+ * @param strategies map of lookup strategies
+ */
+ public void setOptionalClaimsLookupStrategies(
+ @Nonnull final Map<String,Function<ProfileRequestContext,Object>> strategies) {
+ checkSetterPreconditions();
+
+ optionalClaimsLookupStrategies =
+ Constraint.isNotNull(strategies, "Optional claims lookup strategies cannot be null");
+ }
+
/** {@inheritDoc} */
@Override
protected void doInitialize() throws ComponentInitializationException {
@@ -142,6 +158,9 @@ public class BuildEntityConfiguration extends AbstractBuildEntityStatementAction
if (trustAnchorHintsLookupStrategy == null) {
throw new ComponentInitializationException("Trust anchor hints lookup strategy cannot be null");
}
+ if (optionalClaimsLookupStrategies == null) {
+ throw new ComponentInitializationException("Optional claims lookup strategies cannot be null");
+ }
}
/** {@inheritDoc} */
@@ -205,7 +224,19 @@ public class BuildEntityConfiguration extends AbstractBuildEntityStatementAction
if (trustAnchorHints != null && !trustAnchorHints.isEmpty()) {
builder.claim("trust_anchor_hints", trustAnchorHints);
}
- return true;
+ for (final String claim : optionalClaimsLookupStrategies.keySet()) {
+ log.trace("{} Looking up the value for clain {}", getLogPrefix(), claim);
+ final Function<ProfileRequestContext,Object> lookup = optionalClaimsLookupStrategies.get(claim);
+ final Object value = lookup.apply(profileRequestContext);
+ if (value != null) {
+ log.debug("{} Resolved value {} for clain {}", getLogPrefix(), value, claim);
+ builder.claim(claim, value);
+ } else {
+ log.debug("{} No value resolved for clain {}", getLogPrefix(), claim);
+ }
+ }
+
+ return true;
}
}
\ No newline at end of file
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/DefaultTrustMarkFromMetadataCacheFetchingFunction.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/DefaultTrustMarkFromMetadataCacheFetchingFunction.java
new file mode 100644
index 0000000..90bd729
--- /dev/null
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/DefaultTrustMarkFromMetadataCacheFetchingFunction.java
@@ -0,0 +1,204 @@
+/*
+ * 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.oidfed.profile.impl;
+
+import java.net.URI;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.slf4j.Logger;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.nimbusds.openid.connect.sdk.federation.entities.EntityStatement;
+
+import net.shibboleth.idp.plugin.oidc.op.oidfed.messaging.impl.TrustMarkRequest;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.messaging.impl.TrustMarkResponse;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.EntityStatementHelper;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.ResponseContainerExpirationCriterion;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.TrustMarkRequestCriterion;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.TrustMarkResponseContainer;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.TrustedRemoteEntity;
+import net.shibboleth.oidc.metadata.cache.MetadataCache;
+import net.shibboleth.oidc.metadata.cache.MetadataCacheException;
+import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.shared.annotation.constraint.NotEmpty;
+import net.shibboleth.shared.component.AbstractIdentifiableInitializableComponent;
+import net.shibboleth.shared.component.ComponentInitializationException;
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.primitive.LoggerFactory;
+import net.shibboleth.shared.resolver.CriteriaSet;
+
+/**
+ * Function to fetch a trust mark from the configured cache of trust marks. The configurable cache is used for
+ * fetching the trust_mark_endpoint of the trusted entity.
+ */
+public class DefaultTrustMarkFromMetadataCacheFetchingFunction extends AbstractIdentifiableInitializableComponent
+ implements Function<ProfileRequestContext, Map<String, String>> {
+
+ /** Class logger. */
+ @Nonnull private Logger log = LoggerFactory.getLogger(DefaultTrustMarkFromMetadataCacheFetchingFunction.class);
+
+ /** Cache used to fetch the issuer entity configuration from. */
+ @NonnullAfterInit private MetadataCache<EntityStatement> entityConfigurationCache;
+
+ /** Cache containing responses from Trust Mark APIs. */
+ @NonnullAfterInit private MetadataCache<TrustMarkResponseContainer> trustMarkCache;
+
+ /** JSON object mapper used for decoding JSON into Map. */
+ @NonnullAfterInit private ObjectMapper objectMapper;
+
+ /** Trusted trust mark API entity. */
+ @NonnullAfterInit private TrustedRemoteEntity trustedEntity;
+
+ /** Trust mark type. */
+ @NonnullAfterInit private String trustMarkType;
+
+ /** Subject of the trust mark. */
+ @NonnullAfterInit private String subject;
+
+ /** Lifetime for the cached object. */
+ @Nonnull private Duration cachedLifetime;
+
+ /**
+ * Constructor.
+ */
+ public DefaultTrustMarkFromMetadataCacheFetchingFunction() {
+ cachedLifetime = Duration.ofHours(1);
+ }
+
+ /**
+ * Set the cache used to fetch the issuer entity configuration from.
+ *
+ * @param cache cache used to fetch the issuer entity configuration from
+ */
+ public void setEntityConfigurationCache(@Nonnull final MetadataCache<EntityStatement> cache) {
+ checkSetterPreconditions();
+ entityConfigurationCache = Constraint.isNotNull(cache, "Entity Configuration cache cannot be null");
+ }
+
+ /**
+ * Set the cache containing responses from Trust Mark APIs.
+ *
+ * @param cache cache containing responses from Trust Mark APIs
+ */
+ public void setTrustMarkCache(@Nonnull final MetadataCache<TrustMarkResponseContainer> cache) {
+ checkSetterPreconditions();
+ trustMarkCache = Constraint.isNotNull(cache, "Trust Mark cache cannot be null");
+ }
+
+ /**
+ * Set the JSON {@link ObjectMapper} used for decoding JSON into Map.
+ *
+ * @param mapper object mapper
+ */
+ public void setObjectMapper(@Nonnull final ObjectMapper mapper) {
+ checkSetterPreconditions();
+ objectMapper = Constraint.isNotNull(mapper, "Object mapper cannot be null");
+ }
+
+ /**
+ * Set the trusted trust mark API entity.
+ *
+ * @param entity trusted trust mark API entity
+ */
+ public void setTrustedEntity(@Nonnull final TrustedRemoteEntity entity) {
+ checkSetterPreconditions();
+ trustedEntity = Constraint.isNotNull(entity, "Trusted entity cannot be null");
+ }
+
+ /**
+ * Set the trust mark type.
+ *
+ * @param type trust mark type
+ */
+ public void setTrustMarkType(@Nonnull @NotEmpty final String type) {
+ checkSetterPreconditions();
+ trustMarkType = Constraint.isNotEmpty(type, "Trust mark type cannot be empty");
+ }
+
+ /**
+ * Set the subject.
+ *
+ * @param sub subject
+ */
+ public void setSubject(@Nonnull @NotEmpty final String sub) {
+ checkSetterPreconditions();
+ subject = Constraint.isNotEmpty(sub, "Subject cannot be empty");
+ }
+
+ /** {@inheritDoc} */
+ protected void doInitialize() throws ComponentInitializationException {
+ super.doInitialize();
+ if (entityConfigurationCache == null) {
+ throw new ComponentInitializationException("Entity configuration cache cannot be null");
+ }
+ if (trustMarkCache == null) {
+ throw new ComponentInitializationException("Trust Mark cache cannot be null");
+ }
+ if (objectMapper == null) {
+ throw new ComponentInitializationException("Object mapper cannot be null");
+ }
+ if (trustedEntity == null) {
+ throw new ComponentInitializationException("Trusted entity cannot be null");
+ }
+ if (trustMarkType == null) {
+ throw new ComponentInitializationException("Trust mark type cannot be null");
+ }
+ if (subject == null) {
+ throw new ComponentInitializationException("Subject cannot be null");
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override @Nullable
+ public Map<String, String> apply(@Nullable final ProfileRequestContext profileRequestContext) {
+ checkComponentActive();
+ final String entityId = trustedEntity.getEntityId();
+ final URI uri = EntityStatementHelper.fetchEndpointUriFromFederationEntity(entityConfigurationCache,
+ entityId, "trust_mark_endpoint", objectMapper);
+ if (uri == null) {
+ log.warn("Could not fetch trust mark endpoint for {}", entityId);
+ return null;
+ }
+ final TrustMarkRequest trustMarkRequest = new TrustMarkRequest(uri, trustMarkType, subject);
+ final CriteriaSet criteriaSet = new CriteriaSet(new TrustMarkRequestCriterion(trustMarkRequest));
+ criteriaSet.add(new ResponseContainerExpirationCriterion(Instant.now().plus(cachedLifetime)));
+ final List<TrustMarkResponseContainer> cacheResult;
+ try {
+ cacheResult = trustMarkCache.get(criteriaSet);
+ } catch (final MetadataCacheException e) {
+ log.warn("Could not resolve trust mark {} from {}", trustMarkType, trustedEntity, e);
+ return null;
+ }
+ if (cacheResult.isEmpty()) {
+ log.debug("No data resolved for {} from {}", trustMarkType, trustedEntity);
+ return null;
+ }
+ if (cacheResult.get(0).getResponse() instanceof TrustMarkResponse successResponse) {
+ return Map.of("trust_mark_type", trustMarkType, "trust_mark", successResponse.getJWT().serialize());
+ } else {
+ log.debug("The response from {} was not a success response: {}", trustedEntity,
+ cacheResult.get(0).getResponse());
+ }
+ return null;
+ }
+}
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultEntityConfigurationTrustMarksLookupStrategy.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultEntityConfigurationTrustMarksLookupStrategy.java
new file mode 100644
index 0000000..fd2f506
--- /dev/null
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultEntityConfigurationTrustMarksLookupStrategy.java
@@ -0,0 +1,77 @@
+/*
+ * 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.oidfed.profile.navigate;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import net.shibboleth.shared.collection.CollectionSupport;
+import net.shibboleth.shared.component.AbstractIdentifiableInitializableComponent;
+import net.shibboleth.shared.logic.Constraint;
+
+/**
+ * Default strategy to fetch trust marks to be included in the entity configuration.
+ */
+public class DefaultEntityConfigurationTrustMarksLookupStrategy extends AbstractIdentifiableInitializableComponent
+ implements Function<ProfileRequestContext,List<Map<String,String>>> {
+
+ /** Lookup strategies to fetch trust mark values to be included in the entity configuration. */
+ @Nonnull private List<Function<ProfileRequestContext,Map<String,String>>> trustMarkLookupStrategies;
+
+ /**
+ * Constructor.
+ */
+ public DefaultEntityConfigurationTrustMarksLookupStrategy() {
+ trustMarkLookupStrategies = CollectionSupport.emptyList();
+ }
+
+ /**
+ * Set the lookup strategies to fetch trust mark values to be included in the entity configuration.
+ *
+ * @param strategies lookup strategies
+ */
+ public void setTrustMarkLookupStrategies(
+ @Nonnull final List<Function<ProfileRequestContext,Map<String,String>>> strategies) {
+ checkSetterPreconditions();
+ Constraint.isNotNull(strategies, "Trust mark lookup strategies cannot be null");
+ trustMarkLookupStrategies = strategies;
+ }
+
+ /** {@inheritDoc} */
+ @Override @Nonnull
+ public List<Map<String, String>> apply(@Nullable final ProfileRequestContext profileRequestContext) {
+ checkComponentActive();
+ final List<Map<String, String>> trustMarks = new ArrayList<>();
+ for (final Function<ProfileRequestContext,Map<String,String>> strategy : trustMarkLookupStrategies) {
+ if (strategy == null) {
+ continue;
+ }
+ Optional.ofNullable(strategy.apply(profileRequestContext))
+ .filter(trustMark -> trustMark != null && !trustMark.isEmpty())
+ .ifPresent(trustMark -> trustMarks.add(trustMark));
+
+ }
+ return CollectionSupport.copyToList(trustMarks);
+ }
+
+}
diff --git a/idp-oidfed-op-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml b/idp-oidfed-op-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
index ea4b2d8..7230ea8 100644
--- a/idp-oidfed-op-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
+++ b/idp-oidfed-op-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
@@ -328,6 +328,39 @@
</property>
</bean>
+ <bean id="shibboleth.oidfed.TrustMarkMetadataCache" parent="shibboleth.oidc.CacheBuilder">
+ <constructor-arg>
+ <bean p:cacheId="DefaultTrustMarkMetadataCache" parent="shibboleth.oidfed.TrustMarkMetadataCacheBuilderSpec"
+ p:cleanupTaskInterval="PT30S"/>
+ </constructor-arg>
+ </bean>
+
+ <bean id="shibboleth.oidfed.TrustMarkMetadataCacheBuilderSpec"
+ class="net.shibboleth.oidc.metadata.cache.impl.DynamicMetadataCacheBuilderSpec"
+ p:minCacheDuration="%{idp.oidfed.cache.trustMark.minRefreshDelay:PT1H}"
+ p:maxCacheDuration="%{idp.oidfed.cache.trustMark.maxRefreshDelay:PT24H}">
+ <property name="identifierExtractionStrategy">
+ <bean parent="shibboleth.Functions.Expression" c:expression="#input?.getRequestMessage().toString()"/>
+ </property>
+ <property name="criteriaToIdentifierStrategy">
+ <bean parent="shibboleth.Functions.Expression" c:expression="#input?.get(T(net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.TrustMarkRequestCriterion))?.getRequest().toString()"/>
+ </property>
+ <property name="metadataFilterStrategy">
+ <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.DefaultTrustMarkResponseSignatureValidationFilterStrategy"
+ p:trustChainCache-ref="shibboleth.oidfed.TrustChainMetadataCache"
+ p:trustEngine-ref="shibboleth.oidfed.DefaultEntityConfigurationTrustEngine"/>
+ </property>
+ <property name="fetchStrategy">
+ <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.DefaultTrustMarkFetchingStrategy"
+ p:httpClient="#{getObject('shibboleth.oidfed.HttpClient') ?: getObject('shibboleth.InternalHttpClient')}"
+ p:httpClientSecurityParameters="#{getObject('shibboleth.oidfed.NonBrowser.HttpClientSecurityParameters')}"
+ p:objectMapper-ref="shibboleth.oidfed.policy.JSONObjectMapper"/>
+ </property>
+ <property name="metadataExpirationTimeStrategy">
+ <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.DefaultTrustMarkResponseContainerExpirationTimeStrategy"/>
+ </property>
+ </bean>
+
<bean id="shibboleth.oidfed.DefaultDelegatedTrustMarkTrustEngine"
class="net.shibboleth.oidc.security.impl.ExplicitKeySignedJWTTrustEngine">
<constructor-arg index="0">
@@ -672,6 +705,14 @@
<alias alias="UseResolverApiCondition" name="%{idp.oidfed.trustchain.resolver.useResolverApiCondition:shibboleth.Conditions.FALSE}" />
<alias alias="FallbackToLocalResolutionCondition" name="%{idp.oidfed.trustchain.resolver.fallbackToLocalCondition:shibboleth.Conditions.TRUE}" />
+ <bean id="shibboleth.oidfed.RemoteTrustMark" abstract="true"
+ class="net.shibboleth.idp.plugin.oidc.op.oidfed.profile.impl.DefaultTrustMarkFromMetadataCacheFetchingFunction"
+ p:subject-ref="shibboleth.oidc.issuer"
+ p:objectMapper-ref="shibboleth.oidc.JSONObjectMapper"
+ p:entityConfigurationCache-ref="shibboleth.oidfed.EntityConfigurationMetadataCache"
+ p:trustMarkCache-ref="shibboleth.oidfed.TrustMarkMetadataCache"/>
+
<import resource="${idp.home}/conf/oidfed/oidfed-trustchain-resolver.xml"/>
+ <import resource="${idp.home}/conf/oidfed/oidfed-entity-configuration-claims.xml"/>
</beans>
\ No newline at end of file
diff --git a/idp-oidfed-op-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidfed/entity-configuration/entity-configuration-beans.xml b/idp-oidfed-op-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidfed/entity-configuration/entity-configuration-beans.xml
index 9eb731d..053f4fe 100644
--- a/idp-oidfed-op-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidfed/entity-configuration/entity-configuration-beans.xml
+++ b/idp-oidfed-op-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidfed/entity-configuration/entity-configuration-beans.xml
@@ -98,7 +98,8 @@
class="net.shibboleth.idp.plugin.oidc.op.oidfed.profile.impl.BuildEntityConfiguration" scope="prototype"
p:identifierGeneratorLookupStrategy-ref="shibboleth.oidc.DefaultIdentifierGenerationStrategy"
p:objectMapper-ref="#{'%{idp.oidc.logging.objectMapper:shibboleth.oidc.JSONObjectMapper}'.trim()}"
- p:trustAnchorHintsLookupStrategy-ref="#{'%{idp.oidfed.entity-configuration.trustAnchoHintsLookup:DefaultTrustAnchorHintsLookupStrategy}'.trim()}"/>
+ p:trustAnchorHintsLookupStrategy-ref="#{'%{idp.oidfed.entity-configuration.trustAnchoHintsLookup:DefaultTrustAnchorHintsLookupStrategy}'.trim()}"
+ p:optionalClaimsLookupStrategies-ref="shibboleth.oidfed.EntityConfigurationClaimLookupStrategies" />
<bean id="DefaultTrustAnchorHintsLookupStrategy"
class="net.shibboleth.idp.plugin.oidc.op.oidfed.profile.navigate.DefaultTrustAnchorHintsLookupStrategy"
diff --git a/idp-oidfed-op-impl/src/main/resources/net/shibboleth/idp/plugin/oidc/op/oidfed/conf/oidfed/oidfed-entity-configuration-claims.xml b/idp-oidfed-op-impl/src/main/resources/net/shibboleth/idp/plugin/oidc/op/oidfed/conf/oidfed/oidfed-entity-configuration-claims.xml
new file mode 100644
index 0000000..dac0420
--- /dev/null
+++ b/idp-oidfed-op-impl/src/main/resources/net/shibboleth/idp/plugin/oidc/op/oidfed/conf/oidfed/oidfed-entity-configuration-claims.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:context="http://www.springframework.org/schema/context"
+ xmlns:util="http://www.springframework.org/schema/util" xmlns:p="http://www.springframework.org/schema/p"
+ xmlns:c="http://www.springframework.org/schema/c" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
+ http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
+
+ default-init-method="initialize" default-destroy-method="destroy">
+
+ <!-- TODO: document with examples once the structure is settled -->
+ <util:map id="shibboleth.oidfed.EntityConfigurationClaimLookupStrategies"
+ value-type="java.util.function.Function">
+ </util:map>
+
+</beans>
\ No newline at end of file
diff --git a/idp-oidfed-op-impl/src/main/resources/net/shibboleth/idp/plugin/oidc/op/oidfed/module.properties b/idp-oidfed-op-impl/src/main/resources/net/shibboleth/idp/plugin/oidc/op/oidfed/module.properties
index 54d11a0..854bdda 100644
--- a/idp-oidfed-op-impl/src/main/resources/net/shibboleth/idp/plugin/oidc/op/oidfed/module.properties
+++ b/idp-oidfed-op-impl/src/main/resources/net/shibboleth/idp/plugin/oidc/op/oidfed/module.properties
@@ -19,3 +19,7 @@ idp.oidc.OP.oidfed.2.replace = false
idp.oidc.OP.oidfed.3.src = /net/shibboleth/idp/plugin/oidc/op/oidfed/conf/oidfed/oidfed-trustchain-resolver.xml
idp.oidc.OP.oidfed.3.dest = conf/oidfed/oidfed-trustchain-resolver.xml
idp.oidc.OP.oidfed.3.replace = false
+
+idp.oidc.OP.oidfed.4.src = /net/shibboleth/idp/plugin/oidc/op/oidfed/conf/oidfed/oidfed-entity-configuration-claims.xml
+idp.oidc.OP.oidfed.4.dest = conf/oidfed/oidfed-entity-configuration-claims.xml
+idp.oidc.OP.oidfed.4.replace = false
diff --git a/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/AbstractFederationFlowTest.java b/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/AbstractFederationFlowTest.java
index 75555b4..d2526e8 100644
--- a/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/AbstractFederationFlowTest.java
+++ b/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/AbstractFederationFlowTest.java
@@ -80,7 +80,6 @@ import net.shibboleth.idp.plugin.oidc.op.oidfed.TrustChainTestUtil;
import net.shibboleth.idp.plugin.oidc.op.oidfed.messaging.impl.ResolveEntityResponse;
import net.shibboleth.idp.plugin.oidc.op.profile.flow.AbstractOidcFlowTest;
import net.shibboleth.oidc.security.credential.BasicJWKCredential;
-import net.shibboleth.shared.collection.CollectionSupport;
import net.shibboleth.shared.logic.Constraint;
/**
@@ -100,6 +99,7 @@ public class AbstractFederationFlowTest extends AbstractOidcFlowTest {
final String anchorFetchEndpoint = anchorId + "/fetch";
final String anchorResolveEndpoint = anchorId + "/resolve";
final String trustMarkIssuerId = "https://trust-mark-issuer.federation.local";
+ final String trustMarkEndpoint = "https://trust-mark-issuer.federation.local/issue";
String issuer = "https://op.example.org";
JWK rpKey;
@@ -282,7 +282,8 @@ public class AbstractFederationFlowTest extends AbstractOidcFlowTest {
.claim("jwks", new JWKSet(trustMarkIssuerKey).toJSONObject(true))
.claim("authority_hints", authorityHints == null || authorityHints.length == 0 ?
new String[] { anchorId } : authorityHints)
- .claim("metadata", Map.of("federation_entity", CollectionSupport.emptyMap()))
+ .claim("metadata", Map.of("federation_entity", Map.of("trust_mark_endpoint",
+ trustMarkEndpoint)))
.build();
final EntityStatement rpConfiguration =
TrustChainTestUtil.entityStatement(JWSAlgorithm.RS256, trustMarkIssuerKey, claimsSet);
diff --git a/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/EntityConfigurationFlowTest.java b/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/EntityConfigurationFlowTest.java
index 12d6846..25729f6 100644
--- a/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/EntityConfigurationFlowTest.java
+++ b/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/EntityConfigurationFlowTest.java
@@ -17,16 +17,21 @@ package net.shibboleth.idp.plugin.oidc.op.profile.flow.oidfed;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
+import java.net.URLEncoder;
+import java.nio.charset.Charset;
+import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
+import java.util.Map;
import org.springframework.webflow.executor.FlowExecutionResult;
import org.testng.Assert;
import org.testng.annotations.Test;
import com.nimbusds.jose.Algorithm;
+import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.oauth2.sdk.ParseException;
import com.nimbusds.oauth2.sdk.Response;
import com.nimbusds.oauth2.sdk.id.Issuer;
@@ -36,6 +41,10 @@ import com.nimbusds.openid.connect.sdk.federation.entities.FederationEntityMetad
import com.nimbusds.openid.connect.sdk.federation.registration.ClientRegistrationType;
import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.TrustChainTestUtil;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.messaging.impl.TrustMarkResponse;
+import net.shibboleth.shared.collection.CollectionSupport;
+
/**
* Unit test for the entity configuration flow.
*/
@@ -43,12 +52,34 @@ public class EntityConfigurationFlowTest extends AbstractFederationFlowTest {
public static final String FLOW_ID = "oidfed/entity-configuration";
+ final String dynamicTrustMarkIssuerId = "https://dyn-trust-mark-issuer.federation.local";
+ final String dynamicTrustMarkType = dynamicTrustMarkIssuerId + "/example";
+
protected EntityConfigurationFlowTest() {
super(FLOW_ID);
}
@Test
public void testOutputAndCaching() throws ParseException, IOException, InterruptedException {
+ final String trustMark = TrustChainTestUtil.trustMark(JWSAlgorithm.RS256, trustMarkIssuerKey,
+ dynamicTrustMarkIssuerId, issuer, dynamicTrustMarkType, Instant.now().plusSeconds(300)).serialize();
+ try {
+ mapResponse(entityConfigurationUrl(dynamicTrustMarkIssuerId),
+ mockResponse(trustMarkIssuerConfiguration(dynamicTrustMarkIssuerId)));
+
+ mapResponse(entityConfigurationUrl(anchorId), mockResponse(trustedAnchorConfiguration()));
+ mapResponse(subordinateStatementUrl(anchorFetchEndpoint, dynamicTrustMarkIssuerId),
+ mockResponse(subordinateStatement(dynamicTrustMarkIssuerId,
+ Map.of("federation_entity", CollectionSupport.emptyMap()), trustMarkIssuerKey)));
+ final String trustMarkUrl = trustMarkEndpoint + "?trust_mark_type="
+ + URLEncoder.encode(dynamicTrustMarkType, Charset.forName("UTF-8"))
+ + "&sub=" + URLEncoder.encode(issuer, Charset.forName("UTF-8"));
+ mapResponse(trustMarkUrl,
+ mockResponse(200, TrustMarkResponse.HTTP_RESPONSE_CONTENT_TYPE.toString(), trustMark));
+ } catch (UnsupportedOperationException | IOException e) {
+ Assert.fail("Could not initialize mock HTTP client", e);
+ }
+
request.setRequestURI("/idp/profile/oidfed/entity-configuration");
final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
final Response response = parseResponse(result);
@@ -108,6 +139,24 @@ public class EntityConfigurationFlowTest extends AbstractFederationFlowTest {
List.of(new EntityID("https://anchor1.example.org"), new EntityID("https://anchor2.example.org")));
final List<String> trustAnchorHints = entityStatement.getClaimsSet().getStringListClaim("trust_anchor_hints");
Assert.assertEquals(trustAnchorHints, List.of(anchorId));
+ final List<Object> trustMarks = entityStatement.getClaimsSet().getJSONArrayClaim("trust_marks");
+ Assert.assertNotNull(trustMarks);
+ @SuppressWarnings("unchecked")
+ final List<Map<String,Object>> trustMarkMap = trustMarks.stream().filter(Map.class::isInstance)
+ .map(object -> (Map<String,Object>) object).toList();
+ Assert.assertEquals(trustMarkMap.size(), 2);
+ Assert.assertNotNull(trustMarkMap.stream()
+ .filter(map -> map.entrySet().stream()
+ .filter(entry -> "trust_mark_type".equals(entry.getKey()) &&
+ "https://example.org/a-trust-mark".equals(entry.getValue()))
+ .findAny().isPresent())
+ .findAny().orElse(null));
+ Assert.assertNotNull(trustMarkMap.stream()
+ .filter(map -> map.entrySet().stream()
+ .filter(entry -> "trust_mark_type".equals(entry.getKey()) &&
+ dynamicTrustMarkType.equals(entry.getValue()))
+ .findAny().isPresent())
+ .findAny().orElse(null));
}
protected boolean containsAll(Collection<? extends Algorithm> algs, Collection<String> strings) {
diff --git a/idp-oidfed-op-impl/src/test/resources/net/shibboleth/idp/module/conf/oidfed/oidfed-entity-configuration-claims.xml b/idp-oidfed-op-impl/src/test/resources/net/shibboleth/idp/module/conf/oidfed/oidfed-entity-configuration-claims.xml
new file mode 100644
index 0000000..26504a9
--- /dev/null
+++ b/idp-oidfed-op-impl/src/test/resources/net/shibboleth/idp/module/conf/oidfed/oidfed-entity-configuration-claims.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:context="http://www.springframework.org/schema/context"
+ xmlns:util="http://www.springframework.org/schema/util" xmlns:p="http://www.springframework.org/schema/p"
+ xmlns:c="http://www.springframework.org/schema/c" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
+ http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
+
+ default-init-method="initialize" default-destroy-method="destroy">
+
+ <bean id="ExampleTrustedTrustMarkIssuer"
+ class="net.shibboleth.idp.plugin.oidc.op.oidfed.profile.TrustedRemoteEntity"
+ c:entity="https://dyn-trust-mark-issuer.federation.local"/>
+
+ <util:map id="shibboleth.oidfed.EntityConfigurationClaimLookupStrategies"
+ value-type="java.util.function.Function">
+ <entry key="trust_marks">
+ <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.profile.navigate.DefaultEntityConfigurationTrustMarksLookupStrategy">
+ <property name="trustMarkLookupStrategies">
+ <util:list value-type="java.util.function.Function">
+ <bean parent="shibboleth.oidfed.RemoteTrustMark"
+ p:trustMarkType="https://dyn-trust-mark-issuer.federation.local/example"
+ p:trustedEntity-ref="ExampleTrustedTrustMarkIssuer" />
+ <bean parent="shibboleth.Functions.Constant">
+ <constructor-arg name="target">
+ <util:map key-type="java.lang.String" value-type="java.lang.String">
+ <entry
+ key="trust_mark_type"
+ value="https://example.org/a-trust-mark" />
+ <entry
+ key="trust_mark"
+ value="eyJraWQiOiJtb2NrVHJ1c3RNYXJrSXNzdWVyS2V5IiwidHlwIjoidHJ1c3QtbWFyaytqd3QiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL3RydXN0LW1hcmstaXNzdWVyLmZlZGVyYXRpb24ubG9jYWwiLCJzdWIiOiJodHRwczovL29wLmV4YW1wbGUub3JnIiwidHJ1c3RfbWFya190eXBlIjoiaHR0cHM6Ly9leGFtcGxlLm9yZy9hLXRydXN0LW1hcmsiLCJleHAiOjQ5MTgzNjczMzYsImlhdCI6MTc2NDc2NzMzNn0.smmtxeU_vCh2XFHLCxGHtwr_ZQ9A0-T7V9Poq5tNqwuU7_QlMAUJG1CJcprqQ9hH2oNSSQPIfUk7fOB1VUEY66U_bGBQ-KNQiIj-j25IQs7JalOCT1qjzcsMkq6i [...]
+ </util:map>
+ </constructor-arg>
+ </bean>
+ </util:list>
+ </property>
+ </bean>
+ </entry>
+ </util:map>
+
+</beans>
\ 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