[java-plugin-shibd-oidc] branch main updated: Support attribute resolution during the extract claims step
Codeberg
noreply at shibboleth.net
Fri Nov 28 11:08:04 UTC 2025
This is an automated email from the git hooks/post-receive script.
codeberg pushed a commit to branch main
in repository java-plugin-shibd-oidc.
View the commit online:
https://codeberg.org/Shibboleth/java-plugin-shibd-oidc/commit/49cc7970ba28d579becab58c1002018ccd285647
The following commit(s) were added to refs/heads/main by this push:
new 49cc797 Support attribute resolution during the extract claims step
49cc797 is described below
commit 49cc7970ba28d579becab58c1002018ccd285647
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Fri Nov 28 11:07:57 2025 +0000
Support attribute resolution during the extract claims step
- Add a test resolver configuration
- Need to think about UID lookup
---
.../idp/module/conf/attribute-resolver.xml | 57 +++++++++++++++++++
.../sp/oidc/profile/impl/ExtractOIDCClaims.java | 64 +++++++++++++++++++++-
2 files changed, 118 insertions(+), 3 deletions(-)
diff --git a/sp-oidc-conf-impl/src/test/resources/net/shibboleth/idp/module/conf/attribute-resolver.xml b/sp-oidc-conf-impl/src/test/resources/net/shibboleth/idp/module/conf/attribute-resolver.xml
new file mode 100644
index 0000000..74f6bba
--- /dev/null
+++ b/sp-oidc-conf-impl/src/test/resources/net/shibboleth/idp/module/conf/attribute-resolver.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+This file is a rudimentary example. While it is semi-functional, it isn't very
+interesting. It is here only as a starting point for your deployment process
+to avoid any dependency on components like an LDAP directory.
+
+Very few attribute definitions and data connectors are demonstrated, and the
+data is derived statically from the logged-in username and a static example
+connector.
+
+The file(s) in the examples directory contain more examples that involve more
+complex approaches. Deployers should refer to the documentation for a complete
+list of possible components and their options.
+-->
+<AttributeResolver
+ xmlns="urn:mace:shibboleth:2.0:resolver"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:oidc="urn:mace:shibboleth:2.0:resolver:oidc"
+ xsi:schemaLocation="urn:mace:shibboleth:2.0:resolver http://shibboleth.net/schema/idp/shibboleth-attribute-resolver.xsd
+ urn:mace:shibboleth:2.0:resolver:oidc http://shibboleth.net/schema/oidc/shibboleth-attribute-encoder-oidc.xsd">
+
+
+
+<!-- <AttributeDefinition id="eduPersonPrincipalName" xsi:type="Scoped" scope="%{idp.scope}">
+ <InputAttributeDefinition ref="uid" />
+ </AttributeDefinition>
+
+ <AttributeDefinition id="uid" xsi:type="PrincipalName" /> -->
+
+ <AttributeDefinition id="mail" xsi:type="Template">
+ <InputDataConnector ref="staticAttributes" attributeNames="name" />
+ <Template>
+ <![CDATA[
+ ${name}@example.org
+ ]]>
+ </Template>
+ </AttributeDefinition>
+
+ <!--
+ This is an example of an attribute sourced from a data connector.
+ -->
+ <AttributeDefinition id="eduPersonScopedAffiliation" xsi:type="Scoped" scope="%{idp.scope}">
+ <InputDataConnector ref="staticAttributes" attributeNames="affiliation" />
+ </AttributeDefinition>
+
+
+ <DataConnector id="staticAttributes" xsi:type="Static" exportAttributes="schacHomeOrganization">
+ <Attribute id="affiliation">
+ <Value>member</Value>
+ </Attribute>
+ <Attribute id="name">
+ <Value>jdoe</Value>
+ </Attribute>
+ </DataConnector>
+
+
+</AttributeResolver>
diff --git a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/ExtractOIDCClaims.java b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/ExtractOIDCClaims.java
index c2d6ecf..2d69040 100644
--- a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/ExtractOIDCClaims.java
+++ b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/ExtractOIDCClaims.java
@@ -18,8 +18,10 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
+import java.util.stream.Collectors;
import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
import org.opensaml.messaging.context.navigate.ChildContextLookup;
import org.opensaml.messaging.context.navigate.RecursiveTypedParentContextLookup;
@@ -43,6 +45,8 @@ import net.shibboleth.idp.attribute.filter.AttributeFilter;
import net.shibboleth.idp.attribute.filter.AttributeFilterException;
import net.shibboleth.idp.attribute.filter.context.AttributeFilterContext;
import net.shibboleth.idp.attribute.filter.context.AttributeFilterContext.Direction;
+import net.shibboleth.idp.attribute.resolver.AttributeResolver;
+import net.shibboleth.idp.attribute.resolver.context.AttributeResolutionContext;
import net.shibboleth.idp.attribute.transcoding.AttributeTranscoder;
import net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry;
import net.shibboleth.idp.attribute.transcoding.TranscoderSupport;
@@ -61,6 +65,7 @@ import net.shibboleth.shared.logic.Constraint;
import net.shibboleth.shared.primitive.LoggerFactory;
import net.shibboleth.shared.service.ServiceException;
import net.shibboleth.shared.service.ServiceableComponent;
+import net.shibboleth.sp.Application;
import net.shibboleth.sp.profile.AbstractApplicationAction;
/**
@@ -248,10 +253,64 @@ public class ExtractOIDCClaims extends AbstractApplicationAction {
//TODO custom extraction, attribute resolution
+ // TODO Extract standard attributes. We might want to extract additional authentication information, not sure yet
+
+
+ final Function<ProfileRequestContext,Collection<IdPAttribute>> aes =
+ profileConfiguration.getAttributeExtractionStrategy(profileRequestContext);
+ if (aes != null) {
+ log.debug("{} Applying custom attribute extraction strategy", getLogPrefix());
+ final Collection<IdPAttribute> newAttributes = aes.apply(profileRequestContext);
+ if (newAttributes != null && !newAttributes.isEmpty()) {
+ if (log.isDebugEnabled()) {
+ log.debug("{} Extracted attributes with custom strategy: {}", getLogPrefix(),
+ newAttributes.stream().map(IdPAttribute::getId).collect(Collectors.toUnmodifiableList()));
+ }
+ IdPAttributeSupport.withMapMergeDuplicates(accumulator, newAttributes);
+ }
+ }
+
+ if (profileConfiguration.isResolveAttributes(profileRequestContext)) {
+ log.debug("{} Performing internal attribute resolution", getLogPrefix());
+ final Collection<IdPAttribute> newAttributes = resolveAttributes(profileRequestContext);
+ if (newAttributes != null && !newAttributes.isEmpty()) {
+ if (log.isDebugEnabled()) {
+ log.debug("{} Resolved attributes internally: {}", getLogPrefix(),
+ newAttributes.stream().map(IdPAttribute::getId).collect(Collectors.toUnmodifiableList()));
+ }
+ IdPAttributeSupport.withMapMergeDuplicates(accumulator, newAttributes);
+ }
+ }
+
// Install the final result back.
attributeContext.setIdPAttributes(accumulator);
}
+ /**
+ * Perform internal attribute resolution using the {@link Application}-associated
+ * {@link AttributeResolver} service.
+ *
+ * @param profileRequestContext profile request context
+ *
+ * @return resolved attributes
+ */
+ @Nullable private Collection<IdPAttribute> resolveAttributes(
+ @Nonnull final ProfileRequestContext profileRequestContext) {
+
+ final AttributeResolutionContext resolutionContext = new AttributeResolutionContext();
+
+ resolutionContext.setResolutionLabel("sp/consumer/oidc")
+ .setPrincipal(profileConfiguration.getAttributeResolutionPrincipal(profileRequestContext))
+ .setAttributeIssuerID(issuerLookupStrategy.apply(profileRequestContext))
+ .setAttributeRecipientID(requesterLookupStrategy.apply(profileRequestContext))
+ .setAttributeRecipientGroupID(profileConfiguration.getAttributeRecipientGroupID(profileRequestContext))
+ .setRequestedIdPAttributeNames(profileConfiguration.getRequestedIdPAttributeNames(profileRequestContext));
+
+ profileRequestContext.addSubcontext(resolutionContext, true);
+ resolutionContext.resolveAttributes(ensureApplication().getAttributeResolver());
+ return resolutionContext.getResolvedIdPAttributes().values();
+ }
+
/**
* Process the inbound OIDC Claims.
*
@@ -313,7 +372,7 @@ public class ExtractOIDCClaims extends AbstractApplicationAction {
final ServiceableComponent<MetadataResolver> metadataResolverComponent =
ensureApplication().getMetadataResolver().getServiceableComponent()) {
- // Populate here for locking scope.
+ // TODO, can only use SAML metadata here. Populate here for locking scope.
//filterContext.setMetadataResolver(metadataResolverComponent.getComponent());
final AttributeFilter filter = filterComponent.getComponent();
@@ -348,8 +407,7 @@ public class ExtractOIDCClaims extends AbstractApplicationAction {
.setProxiedRequesterContextLookupStrategy(null)
.setAttributeIssuerID(issuerLookupStrategy.apply(profileRequestContext))
.setAttributeRecipientID(requesterLookupStrategy.apply(profileRequestContext))
- //TODO add this back
- //.setAttributeRecipientGroupID(profileConfiguration.getAttributeRecipientGroupID(profileRequestContext))
+ .setAttributeRecipientGroupID(profileConfiguration.getAttributeRecipientGroupID(profileRequestContext))
.setIncludeUnfilteredAttributes(acceptUnfilteredAttributes);
}
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list