[java-plugin-shibd-saml] branch main updated: Build NameID decoding layer using registry API.
Scott Cantor
cantor.2 at osu.edu
Tue Sep 17 18:08:31 UTC 2024
This is an automated email from the git hooks/post-receive script.
scantor pushed a commit to branch main
in repository java-plugin-shibd-saml.
View the commit online:
http://git.shibboleth.net/view/?p=java-plugin-shibd-saml.git;a=commit;h=aca664cc24bf5e5a7ebf6a5050a3cec184402073
The following commit(s) were added to refs/heads/main by this push:
new aca664c Build NameID decoding layer using registry API.
aca664c is described below
commit aca664cc24bf5e5a7ebf6a5050a3cec184402073
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Tue Sep 17 14:08:24 2024 -0400
Build NameID decoding layer using registry API.
---
sp-saml-api/pom.xml | 5 +
.../transcoding/AbstractSAML2NameIDTranscoder.java | 210 +++++++++++++++++++++
.../nameid/transcoding/SAML2NameIDTranscoder.java | 41 ++++
.../saml2/nameid/transcoding/package-info.java | 18 ++
.../META-INF/net.shibboleth.idp/postconfig.xml | 8 +
.../idp/flows/sp/consumer/saml2/saml2-beans.xml | 2 -
.../idp/service/attribute/registry/postconfig.xml | 22 +++
sp-saml-impl/pom.xml | 35 ++++
.../impl/SAML2ScopedStringNameIDTranscoder.java | 100 ++++++++++
.../impl/SAML2StringNameIDTranscoder.java | 95 ++++++++++
.../nameid/transcoding/impl/package-info.java | 18 ++
.../saml2/profile/impl/ExtractSAMLAttributes.java | 133 +++++++++----
.../SAML2ScopedStringNameIDTranscoderTest.java | 144 ++++++++++++++
.../impl/SAML2StringNameIDTranscoderTest.java | 141 ++++++++++++++
14 files changed, 933 insertions(+), 39 deletions(-)
diff --git a/sp-saml-api/pom.xml b/sp-saml-api/pom.xml
index 6e935e5..f472b00 100644
--- a/sp-saml-api/pom.xml
+++ b/sp-saml-api/pom.xml
@@ -39,6 +39,11 @@
<artifactId>shib-attribute-api</artifactId>
<scope>provided</scope>
</dependency>
+ <dependency>
+ <groupId>${shib-attribute.groupId}</groupId>
+ <artifactId>shib-saml-attribute-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
<dependency>
<groupId>${shib-metadata.groupId}</groupId>
<artifactId>shib-metadata-api</artifactId>
diff --git a/sp-saml-api/src/main/java/net/shibboleth/sp/saml/saml2/nameid/transcoding/AbstractSAML2NameIDTranscoder.java b/sp-saml-api/src/main/java/net/shibboleth/sp/saml/saml2/nameid/transcoding/AbstractSAML2NameIDTranscoder.java
new file mode 100644
index 0000000..984acdf
--- /dev/null
+++ b/sp-saml-api/src/main/java/net/shibboleth/sp/saml/saml2/nameid/transcoding/AbstractSAML2NameIDTranscoder.java
@@ -0,0 +1,210 @@
+/*
+ * 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.saml.saml2.nameid.transcoding;
+
+import java.util.List;
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.core.xml.XMLObject;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.saml.saml2.core.NameID;
+import org.opensaml.saml.saml2.core.NameIDType;
+
+import com.google.common.base.Strings;
+
+import net.shibboleth.idp.attribute.AttributeDecodingException;
+import net.shibboleth.idp.attribute.AttributeEncodingException;
+import net.shibboleth.idp.attribute.IdPAttribute;
+import net.shibboleth.idp.attribute.IdPAttributeValue;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry;
+import net.shibboleth.idp.attribute.transcoding.TranscodingRule;
+import net.shibboleth.idp.saml.attribute.transcoding.AbstractSAMLAttributeTranscoder;
+import net.shibboleth.profile.context.navigate.IssuerLookupFunction;
+import net.shibboleth.profile.context.navigate.RelyingPartyIdLookupFunction;
+import net.shibboleth.shared.collection.CollectionSupport;
+import net.shibboleth.shared.logic.Constraint;
+
+/**
+ * Base class for transcoders that operate on a SAML 2 {@link NameID}.
+ *
+ * <p>For the moment, these are decode only, as the encoding side was already
+ * handled with a dedicated generation service. The registry concept came later.</p>
+ *
+ * @param <EncodedType> the type of data that can be handled by the transcoder
+ */
+public abstract class AbstractSAML2NameIDTranscoder<EncodedType extends IdPAttributeValue> extends
+ AbstractSAMLAttributeTranscoder<NameID,EncodedType> implements SAML2NameIDTranscoder<EncodedType> {
+
+ /** Function used to obtain the requester ID. */
+ @Nonnull private Function<ProfileRequestContext,String> serviceProviderNameLookupStrategy;
+
+ /** Function used to obtain the issuer ID. */
+ @Nonnull private Function<ProfileRequestContext,String> identityProviderNameLookupStrategy;
+
+ /** Constructor. */
+ public AbstractSAML2NameIDTranscoder() {
+ serviceProviderNameLookupStrategy = new IssuerLookupFunction();
+ identityProviderNameLookupStrategy = new RelyingPartyIdLookupFunction();
+ }
+
+ /**
+ * Set the strategy used to locate the name of the service provider in this transaction.
+ *
+ * @param strategy lookup strategy
+ */
+ public void setServiceProviderNameLookupStrategy(@Nonnull final Function<ProfileRequestContext,String> strategy) {
+ checkSetterPreconditions();
+ serviceProviderNameLookupStrategy = Constraint.isNotNull(strategy, "SP name lookup strategy cannot be null");
+ }
+
+ /**
+ * Set the strategy used to locate the name of the identity provider in this transaction.
+ *
+ * @param strategy lookup strategy
+ */
+ public void setIdentityProviderNameLookupStrategy(@Nonnull final Function<ProfileRequestContext,String> strategy) {
+ checkSetterPreconditions();
+ identityProviderNameLookupStrategy = Constraint.isNotNull(strategy, "IdP name lookup strategy cannot be null");
+ }
+
+ /** {@inheritDoc} */
+ @Nonnull public Class<NameID> getEncodedType() {
+ return NameID.class;
+ }
+
+ /** {@inheritDoc} */
+ @Nullable public String getEncodedName(@Nonnull final TranscodingRule rule) {
+ final String format = rule.getOrDefault(PROP_NAME_FORMAT, String.class, NameIDType.UNSPECIFIED);
+ return "SAML2:NameID:" + format;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ @Nonnull protected NameID buildAttribute(@Nullable final ProfileRequestContext profileRequestContext,
+ @Nullable final IdPAttribute attribute, @Nonnull final Class<? extends NameID> to,
+ @Nonnull final TranscodingRule rule, @Nonnull final List<XMLObject> attributeValues)
+ throws AttributeEncodingException {
+
+ throw new AttributeEncodingException("NameID transcoders do not support encoding");
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ @Nonnull protected IdPAttribute buildIdPAttribute(
+ @Nullable final ProfileRequestContext profileRequestContext, @Nonnull final NameID nameID,
+ @Nonnull final TranscodingRule rule, @Nonnull final List<IdPAttributeValue> attributeValues)
+ throws AttributeDecodingException {
+
+ if (nameID.getValue() != null && attributeValues.isEmpty()) {
+ throw new AttributeDecodingException("Failed to decode value for NameID with Format " + nameID.getFormat());
+ }
+
+ final String id = rule.get(AttributeTranscoderRegistry.PROP_ID, String.class);
+ if (Strings.isNullOrEmpty(id)) {
+ throw new AttributeDecodingException("Required transcoder property 'id' not found");
+ }
+ assert id != null;
+
+ final IdPAttribute idpAttribute = new IdPAttribute(id);
+ idpAttribute.setValues(attributeValues);
+ return idpAttribute;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ @Nonnull protected Iterable<XMLObject> getValues(@Nonnull final NameID input) {
+ return input.getValue() != null ? CollectionSupport.singletonList(input) : CollectionSupport.emptyList();
+ }
+
+ /**
+ * Compute the effective NameQualifier to use based on both the input and the transaction.
+ *
+ * @param profileRequestContext profile request context
+ * @param input input object
+ * @param useDefaultQualifier true iff the qualifier should be defaulted in if not set
+ *
+ * @return the effective NameQualifier to use
+ */
+ @Nonnull protected String getNameQualifier(@Nullable final ProfileRequestContext profileRequestContext,
+ @Nonnull final NameID input, final boolean useDefaultQualifier) {
+
+ if (input.getNameQualifier() instanceof String qual) {
+ return qual;
+ }
+
+ if (useDefaultQualifier) {
+ final String qual = identityProviderNameLookupStrategy.apply(profileRequestContext);
+ if (qual != null) {
+ return qual;
+ }
+ }
+
+ return "";
+ }
+
+ /**
+ * Compute the effective SPNameQualifier to use based on both the input and the transaction.
+ *
+ * @param profileRequestContext profile request context
+ * @param input input object
+ * @param useDefaultQualifier true iff the qualifier should be defaulted in if not set
+ *
+ * @return the effective NameQualifier to use
+ */
+ @Nonnull protected String getSPNameQualifier(@Nullable final ProfileRequestContext profileRequestContext,
+ @Nonnull final NameID input, final boolean useDefaultQualifier) {
+
+ if (input.getSPNameQualifier() instanceof String qual) {
+ return qual;
+ }
+
+ if (useDefaultQualifier) {
+ final String qual = serviceProviderNameLookupStrategy.apply(profileRequestContext);
+ if (qual != null) {
+ return qual;
+ }
+ }
+
+ return "";
+ }
+
+ /**
+ * A function to produce a "canonical" name for a SAML 2.0 {@link NameID} for transcoding rules.
+ */
+ public static class NamingFunction implements Function<NameID,String> {
+
+ /** {@inheritDoc} */
+ @Nullable public String apply(@Nullable final NameID input) {
+
+ if (input == null) {
+ return null;
+ }
+
+ String format = input.getFormat();
+ if (format == null) {
+ format = NameIDType.UNSPECIFIED;
+ }
+
+ final StringBuilder builder = new StringBuilder();
+ builder.append("SAML2:NameID:").append(format);
+ return builder.toString();
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/sp-saml-api/src/main/java/net/shibboleth/sp/saml/saml2/nameid/transcoding/SAML2NameIDTranscoder.java b/sp-saml-api/src/main/java/net/shibboleth/sp/saml/saml2/nameid/transcoding/SAML2NameIDTranscoder.java
new file mode 100644
index 0000000..29d7035
--- /dev/null
+++ b/sp-saml-api/src/main/java/net/shibboleth/sp/saml/saml2/nameid/transcoding/SAML2NameIDTranscoder.java
@@ -0,0 +1,41 @@
+/*
+ * 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.saml.saml2.nameid.transcoding;
+
+import javax.annotation.Nonnull;
+
+import net.shibboleth.idp.attribute.IdPAttributeValue;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoder;
+import net.shibboleth.shared.annotation.constraint.NotEmpty;
+
+import org.opensaml.saml.saml2.core.Attribute;
+import org.opensaml.saml.saml2.core.NameID;
+import org.opensaml.saml.saml2.metadata.RequestedAttribute;
+
+/**
+ * Marker interface for transcoders that operate on a SAML 2 {@link Attribute} or {@link RequestedAttribute}.
+ *
+ * @param <EncodedType> the type of data that can be handled by the transcoder
+ */
+public interface SAML2NameIDTranscoder<EncodedType extends IdPAttributeValue> extends
+ AttributeTranscoder<NameID> {
+
+ /** The NameID format. */
+ @Nonnull @NotEmpty static final String PROP_NAME_FORMAT = "saml2.nameFormat";
+
+ /** Whether to default in missing qualifiers from the active transaction. */
+ @Nonnull @NotEmpty static final String PROP_DEFAULT_QUALIFIERS = "saml2.defaultQualifiers";
+
+}
\ No newline at end of file
diff --git a/sp-saml-api/src/main/java/net/shibboleth/sp/saml/saml2/nameid/transcoding/package-info.java b/sp-saml-api/src/main/java/net/shibboleth/sp/saml/saml2/nameid/transcoding/package-info.java
new file mode 100644
index 0000000..ec63d7a
--- /dev/null
+++ b/sp-saml-api/src/main/java/net/shibboleth/sp/saml/saml2/nameid/transcoding/package-info.java
@@ -0,0 +1,18 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * API classes for SAML 2.0 NameID decoding classes.
+ */
+package net.shibboleth.sp.saml.saml2.nameid.transcoding;
\ No newline at end of file
diff --git a/sp-saml-conf-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml b/sp-saml-conf-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
index b62b312..1a0e10e 100644
--- a/sp-saml-conf-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
+++ b/sp-saml-conf-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
@@ -43,4 +43,12 @@
</property>
</bean>
+ <!-- Necessary for NameID decoding to function. -->
+
+ <bean parent="shibboleth.RegistryNamingFunction" c:claz="org.opensaml.saml.saml2.core.NameID">
+ <constructor-arg name="function">
+ <bean class="net.shibboleth.sp.saml.saml2.nameid.transcoding.AbstractSAML2NameIDTranscoder.NamingFunction" />
+ </constructor-arg>
+ </bean>
+
</beans>
diff --git a/sp-saml-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/consumer/saml2/saml2-beans.xml b/sp-saml-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/consumer/saml2/saml2-beans.xml
index f79800b..04c1f62 100644
--- a/sp-saml-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/consumer/saml2/saml2-beans.xml
+++ b/sp-saml-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/consumer/saml2/saml2-beans.xml
@@ -177,8 +177,6 @@
<bean id="ExtractSAMLAttributes"
class="net.shibboleth.sp.saml.saml2.profile.impl.ExtractSAMLAttributes" scope="prototype"
- p:responderLookupStrategy-ref="shibboleth.RelyingPartyIdLookup.Simple"
- p:requesterLookupStrategy-ref="shibboleth.IssuerLookup.Simple"
p:standardExtractionStrategy-ref="StandardExtractionStrategy" />
</beans>
diff --git a/sp-saml-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/service/attribute/registry/postconfig.xml b/sp-saml-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/service/attribute/registry/postconfig.xml
new file mode 100644
index 0000000..db4c846
--- /dev/null
+++ b/sp-saml-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/service/attribute/registry/postconfig.xml
@@ -0,0 +1,22 @@
+<?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">
+
+ <!-- NameID decoders. -->
+
+ <bean id="SAML2StringNameIDTranscoder"
+ class="net.shibboleth.sp.saml.saml2.nameid.transcoding.impl.SAML2StringNameIDTranscoder" />
+
+ <bean id="SAML2ScopedStringNameIDTranscoder"
+ class="net.shibboleth.sp.saml.saml2.nameid.transcoding.impl.SAML2ScopedStringNameIDTranscoder" />
+
+</beans>
diff --git a/sp-saml-impl/pom.xml b/sp-saml-impl/pom.xml
index 63b4b34..69510b0 100644
--- a/sp-saml-impl/pom.xml
+++ b/sp-saml-impl/pom.xml
@@ -61,6 +61,11 @@
<artifactId>shib-attribute-api</artifactId>
<scope>provided</scope>
</dependency>
+ <dependency>
+ <groupId>${shib-attribute.groupId}</groupId>
+ <artifactId>shib-saml-attribute-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
<dependency>
<groupId>${shib-attribute.groupId}</groupId>
<artifactId>shib-attribute-filter-api</artifactId>
@@ -91,6 +96,36 @@
<artifactId>idp-testing</artifactId>
<scope>test</scope>
</dependency>
+
+ <dependency>
+ <groupId>${shib-attribute.groupId}</groupId>
+ <artifactId>shib-attribute-impl</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>${opensaml.groupId}</groupId>
+ <artifactId>opensaml-core-impl</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>${opensaml.groupId}</groupId>
+ <artifactId>opensaml-saml-impl</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>${opensaml.groupId}</groupId>
+ <artifactId>opensaml-testing</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>${shib-shared.groupId}</groupId>
+ <artifactId>shib-testing</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
</project>
diff --git a/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/nameid/transcoding/impl/SAML2ScopedStringNameIDTranscoder.java b/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/nameid/transcoding/impl/SAML2ScopedStringNameIDTranscoder.java
new file mode 100644
index 0000000..e69faae
--- /dev/null
+++ b/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/nameid/transcoding/impl/SAML2ScopedStringNameIDTranscoder.java
@@ -0,0 +1,100 @@
+/*
+ * 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.saml.saml2.nameid.transcoding.impl;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.core.xml.XMLObject;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.saml.saml2.core.NameID;
+import org.opensaml.saml.saml2.core.NameIDType;
+
+import net.shibboleth.idp.attribute.AttributeEncodingException;
+import net.shibboleth.idp.attribute.IdPAttribute;
+import net.shibboleth.idp.attribute.IdPAttributeValue;
+import net.shibboleth.idp.attribute.ScopedStringAttributeValue;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoder;
+import net.shibboleth.idp.attribute.transcoding.TranscodingRule;
+import net.shibboleth.shared.annotation.constraint.NotEmpty;
+import net.shibboleth.sp.saml.saml2.nameid.transcoding.AbstractSAML2NameIDTranscoder;
+
+/**
+ * {@link AttributeTranscoder} that supports {@link NameID} and {@link ScopedStringAttributeValue} objects.
+ *
+ * <p>The scope is defined to be the NameQualifier or the issuing entityID and must exist.</p>
+ */
+public class SAML2ScopedStringNameIDTranscoder extends AbstractSAML2NameIDTranscoder<ScopedStringAttributeValue> {
+
+ /** The decoding template. */
+ @Nonnull @NotEmpty public static final String PROP_TEMPLATE = "saml2.valueTemplate";
+
+ /** The default template. */
+ @Nonnull @NotEmpty public static final String DEFAULT_TEMPLATE = "$Name";
+
+ /** {@inheritDoc} */
+ @Override
+ protected boolean canEncodeValue(@Nonnull final IdPAttribute attribute, @Nonnull final IdPAttributeValue value) {
+ return value instanceof ScopedStringAttributeValue;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ @Nullable protected XMLObject encodeValue(@Nullable final ProfileRequestContext profileRequestContext,
+ @Nonnull final IdPAttribute attribute, @Nonnull final TranscodingRule rule,
+ @Nonnull final ScopedStringAttributeValue value) throws AttributeEncodingException {
+
+ throw new AttributeEncodingException("NameID transcoders do not support encoding");
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ @Nullable protected IdPAttributeValue decodeValue(@Nullable final ProfileRequestContext profileRequestContext,
+ @Nonnull final NameID nameID, @Nonnull final TranscodingRule rule, @Nullable final XMLObject value) {
+
+ final StringBuilder builder =
+ new StringBuilder(rule.getOrDefault(PROP_TEMPLATE, String.class, DEFAULT_TEMPLATE));
+
+
+ final Boolean flag = rule.getOrDefault(PROP_DEFAULT_QUALIFIERS, Boolean.class, false);
+ assert flag != null;
+
+ int i = builder.indexOf("$Format");
+ if (i >= 0) {
+ final String format = nameID.getFormat();
+ builder.replace(i, i + 7, format != null ? format : NameIDType.UNSPECIFIED);
+ }
+
+ i = builder.indexOf("$SPNameQualifier");
+ if (i >= 0) {
+ builder.replace(i, i + 16, getSPNameQualifier(profileRequestContext, nameID, flag));
+ }
+
+ i = builder.indexOf("$NameQualifier");
+ if (i >= 0) {
+ builder.replace(i, i + 14, getNameQualifier(profileRequestContext, nameID, flag));
+ }
+
+ i = builder.indexOf("$Name");
+ if (i >= 0) {
+ final String val = nameID.getValue();
+ builder.replace(i, i + 5, val != null ? val : "");
+ }
+
+ return new ScopedStringAttributeValue(builder.toString(),
+ getNameQualifier(profileRequestContext, nameID, true));
+ }
+
+}
\ No newline at end of file
diff --git a/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/nameid/transcoding/impl/SAML2StringNameIDTranscoder.java b/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/nameid/transcoding/impl/SAML2StringNameIDTranscoder.java
new file mode 100644
index 0000000..34869c2
--- /dev/null
+++ b/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/nameid/transcoding/impl/SAML2StringNameIDTranscoder.java
@@ -0,0 +1,95 @@
+/*
+ * 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.saml.saml2.nameid.transcoding.impl;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.core.xml.XMLObject;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.saml.saml2.core.NameID;
+import org.opensaml.saml.saml2.core.NameIDType;
+
+import net.shibboleth.idp.attribute.AttributeEncodingException;
+import net.shibboleth.idp.attribute.IdPAttribute;
+import net.shibboleth.idp.attribute.IdPAttributeValue;
+import net.shibboleth.idp.attribute.StringAttributeValue;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoder;
+import net.shibboleth.idp.attribute.transcoding.TranscodingRule;
+import net.shibboleth.shared.annotation.constraint.NotEmpty;
+import net.shibboleth.sp.saml.saml2.nameid.transcoding.AbstractSAML2NameIDTranscoder;
+
+/**
+ * {@link AttributeTranscoder} that supports {@link NameID} and {@link StringAttributeValue} objects.
+ */
+public class SAML2StringNameIDTranscoder extends AbstractSAML2NameIDTranscoder<StringAttributeValue> {
+
+ /** The decoding template. */
+ @Nonnull @NotEmpty public static final String PROP_TEMPLATE = "saml2.valueTemplate";
+
+ /** The default template. */
+ @Nonnull @NotEmpty public static final String DEFAULT_TEMPLATE = "$Name!!$NameQualifier!!$SPNameQualifier";
+
+ /** {@inheritDoc} */
+ @Override
+ protected boolean canEncodeValue(@Nonnull final IdPAttribute attribute, @Nonnull final IdPAttributeValue value) {
+ return value instanceof StringAttributeValue;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ @Nullable protected XMLObject encodeValue(@Nullable final ProfileRequestContext profileRequestContext,
+ @Nonnull final IdPAttribute attribute, @Nonnull final TranscodingRule rule,
+ @Nonnull final StringAttributeValue value) throws AttributeEncodingException {
+
+ throw new AttributeEncodingException("NameID transcoders do not support encoding");
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ @Nullable protected IdPAttributeValue decodeValue(@Nullable final ProfileRequestContext profileRequestContext,
+ @Nonnull final NameID nameID, @Nonnull final TranscodingRule rule, @Nullable final XMLObject value) {
+
+ final StringBuilder builder = new StringBuilder(rule.getOrDefault(PROP_TEMPLATE, String.class, DEFAULT_TEMPLATE));
+
+ final Boolean flag = rule.getOrDefault(PROP_DEFAULT_QUALIFIERS, Boolean.class, false);
+ assert flag != null;
+
+ int i = builder.indexOf("$Format");
+ if (i >= 0) {
+ final String format = nameID.getFormat();
+ builder.replace(i, i + 7, format != null ? format : NameIDType.UNSPECIFIED);
+ }
+
+ i = builder.indexOf("$SPNameQualifier");
+ if (i >= 0) {
+ builder.replace(i, i + 16, getSPNameQualifier(profileRequestContext, nameID, flag));
+ }
+
+ i = builder.indexOf("$NameQualifier");
+ if (i >= 0) {
+ builder.replace(i, i + 14, getNameQualifier(profileRequestContext, nameID, flag));
+ }
+
+ i = builder.indexOf("$Name");
+ if (i >= 0) {
+ final String val = nameID.getValue();
+ builder.replace(i, i + 5, val != null ? val : "");
+ }
+
+ return StringAttributeValue.valueOf(builder.toString());
+ }
+
+}
\ No newline at end of file
diff --git a/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/nameid/transcoding/impl/package-info.java b/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/nameid/transcoding/impl/package-info.java
new file mode 100644
index 0000000..96e9c6f
--- /dev/null
+++ b/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/nameid/transcoding/impl/package-info.java
@@ -0,0 +1,18 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * SAML 2.0 NameID transcoder implementations.
+ */
+package net.shibboleth.sp.saml.saml2.nameid.transcoding.impl;
\ No newline at end of file
diff --git a/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/profile/impl/ExtractSAMLAttributes.java b/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/profile/impl/ExtractSAMLAttributes.java
index 930168c..d56f21c 100644
--- a/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/profile/impl/ExtractSAMLAttributes.java
+++ b/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/profile/impl/ExtractSAMLAttributes.java
@@ -14,6 +14,7 @@
package net.shibboleth.sp.saml.saml2.profile.impl;
+import java.time.Instant;
import java.util.ArrayList;
import java.util.Collection;
import java.util.function.Function;
@@ -39,8 +40,11 @@ import org.opensaml.saml.saml2.core.AuthnContextDeclRef;
import org.opensaml.saml.saml2.core.AuthnStatement;
import org.opensaml.saml.saml2.core.Conditions;
import org.opensaml.saml.saml2.core.Issuer;
+import org.opensaml.saml.saml2.core.NameID;
+import org.opensaml.saml.saml2.core.NameIDType;
import org.opensaml.saml.saml2.core.Response;
import org.opensaml.saml.saml2.core.StatusResponseType;
+import org.opensaml.saml.saml2.core.Subject;
import org.opensaml.saml.saml2.core.SubjectLocality;
import org.slf4j.Logger;
@@ -131,8 +135,10 @@ public class ExtractSAMLAttributes extends AbstractApplicationAction {
relyingPartyContextLookupStrategy = new ChildContextLookup<>(RelyingPartyContext.class);
samlTokenContextLookupStrategy = new ChildContextLookup<>(SAMLTokenContext.class);
- requesterLookupStrategy = new RelyingPartyIdLookupFunction();
- issuerLookupStrategy = new IssuerLookupFunction();
+ // These appear reversed because we're referring to the attribute issuer and requester,
+ // which is the inverse of the usual assignment of these labels on an inbound assertion.
+ requesterLookupStrategy = new IssuerLookupFunction();
+ issuerLookupStrategy = new RelyingPartyIdLookupFunction();
// PRC -> SAMLTokenContext -> AttributeContext
attributeContextCreationStrategy = new ChildContextLookup<>(AttributeContext.class, true).compose(
@@ -164,7 +170,7 @@ public class ExtractSAMLAttributes extends AbstractApplicationAction {
}
/**
- * Set the strategy used to locate the requester ID for filtering.
+ * Set the strategy used to locate the attribute requester ID for filtering.
*
* @param strategy lookup strategy
*/
@@ -174,7 +180,7 @@ public class ExtractSAMLAttributes extends AbstractApplicationAction {
}
/**
- * Set the strategy used to locate the issuer ID for filtering.
+ * Set the strategy used to locate the attribute issuer ID for filtering.
*
* @param strategy lookup strategy
*/
@@ -253,12 +259,11 @@ public class ExtractSAMLAttributes extends AbstractApplicationAction {
processAttributes(profileRequestContext);
- // TODO: NameID handling
-
// Extract standard attributes.
if (standardExtractionStrategy != null &&
profileConfiguration.isExtractStandardAttributes(profileRequestContext)) {
log.debug("{} Extracting standard attributes", getLogPrefix());
+ assert standardExtractionStrategy != null;
final Collection<IdPAttribute> standards = standardExtractionStrategy.apply(samlTokenContext);
if (standards != null && !standards.isEmpty()) {
if (log.isDebugEnabled()) {
@@ -301,13 +306,13 @@ public class ExtractSAMLAttributes extends AbstractApplicationAction {
}
/**
- * Process the inbound SAML Attributes.
+ * Process the inbound SAML Attributes and NameID.
*
* @param profileRequestContext current profile request context
*/
private void processAttributes(@Nonnull final ProfileRequestContext profileRequestContext) {
- log.debug("{} Decoding incoming SAML Attributes", getLogPrefix());
+ log.debug("{} Decoding incoming SAML Attributes and NameID", getLogPrefix());
final Multimap<String,IdPAttribute> mapped = HashMultimap.create();
assert mapped != null;
@@ -316,6 +321,17 @@ public class ExtractSAMLAttributes extends AbstractApplicationAction {
ensureApplication().getAttributeTranscoderRegistry().getServiceableComponent()) {
final Response response = getResponse(profileRequestContext);
for (final Assertion assertion : response.getAssertions()) {
+
+ final Subject subject = assertion.getSubject();
+ final NameID nameID = subject != null ? subject.getNameID() : null;
+ if (nameID != null) {
+ try {
+ decodeNameID(component.getComponent(), profileRequestContext, nameID, mapped);
+ } catch (final AttributeDecodingException e) {
+ log.error("{} Error decoding inbound NameID", getLogPrefix(), e);
+ }
+ }
+
for (final AttributeStatement statement : assertion.getAttributeStatements()) {
for (final Attribute designator : statement.getAttributes()) {
assert designator!=null;
@@ -371,7 +387,38 @@ public class ExtractSAMLAttributes extends AbstractApplicationAction {
}
}
}
-
+
+ /**
+ * Access the registry of transcoding rules to decode the input {@link NameID}.
+ *
+ * @param registry registry of transcoding rules
+ * @param profileRequestContext current profile request context
+ * @param input input object
+ * @param results collection to add results to
+ *
+ * @throws AttributeDecodingException if an error occurs or no results were obtained
+ */
+ private void decodeNameID(@Nonnull final AttributeTranscoderRegistry registry,
+ @Nonnull final ProfileRequestContext profileRequestContext, @Nonnull final NameID input,
+ @Nonnull @Live final Multimap<String,IdPAttribute> results) throws AttributeDecodingException {
+
+ final Collection<TranscodingRule> transcodingRules = registry.getTranscodingRules(input);
+ if (transcodingRules.isEmpty()) {
+ log.info("{} No transcoding rule for NameID (Format: '{}')", getLogPrefix(),
+ input.getFormat() != null ? input.getFormat() : NameIDType.UNSPECIFIED);
+ return;
+ }
+
+ for (final TranscodingRule rules : transcodingRules) {
+ assert rules != null;
+ final AttributeTranscoder<NameID> transcoder = TranscoderSupport.getTranscoder(rules);
+ final IdPAttribute decodedAttribute = transcoder.decode(profileRequestContext, input, rules);
+ if (decodedAttribute != null) {
+ results.put(decodedAttribute.getId(), decodedAttribute);
+ }
+ }
+ }
+
/**
* Check for inbound attributes and apply filtering.
*
@@ -586,51 +633,56 @@ public class ExtractSAMLAttributes extends AbstractApplicationAction {
if (issuerAttributeId != null) {
final Issuer issuer = assertion.getIssuer();
- if (issuer != null && issuer.getValue() != null) {
+ if (issuer != null && issuer.getValue() instanceof String s) {
+ assert issuerAttributeId != null;
final IdPAttribute attr = new IdPAttribute(issuerAttributeId);
- attr.setValues(CollectionSupport.singletonList(new StringAttributeValue(issuer.getValue())));
+ attr.setValues(CollectionSupport.singletonList(new StringAttributeValue(s)));
attributes.add(attr);
}
}
- if (authnInstantAttributeId != null && statement.getAuthnInstant() != null) {
+ if (authnInstantAttributeId != null && statement.getAuthnInstant() instanceof Instant ts) {
+ assert authnInstantAttributeId != null;
final IdPAttribute attr = new IdPAttribute(authnInstantAttributeId);
- attr.setValues(CollectionSupport.singletonList(new DateTimeAttributeValue(statement.getAuthnInstant())));
+ attr.setValues(CollectionSupport.singletonList(new DateTimeAttributeValue(ts)));
attributes.add(attr);
}
final AuthnContext ac = statement.getAuthnContext();
final AuthnContextClassRef classRef = ac != null ? ac.getAuthnContextClassRef() : null;
- if (authnContextClassRefAttributeId != null && classRef != null && classRef.getURI() != null) {
+ if (authnContextClassRefAttributeId != null && classRef != null && classRef.getURI() instanceof String s) {
+ assert authnContextClassRefAttributeId != null;
final IdPAttribute attr = new IdPAttribute(authnContextClassRefAttributeId);
- attr.setValues(CollectionSupport.singletonList(new StringAttributeValue(classRef.getURI())));
+ attr.setValues(CollectionSupport.singletonList(new StringAttributeValue(s)));
attributes.add(attr);
}
final AuthnContextDeclRef declRef = ac != null ? ac.getAuthnContextDeclRef() : null;
- if (authnContextDeclRefAttributeId != null && declRef != null && declRef.getURI() != null) {
+ if (authnContextDeclRefAttributeId != null && declRef != null && declRef.getURI() instanceof String s) {
+ assert authnContextDeclRefAttributeId != null;
final IdPAttribute attr = new IdPAttribute(authnContextDeclRefAttributeId);
- attr.setValues(CollectionSupport.singletonList(new StringAttributeValue(declRef.getURI())));
+ attr.setValues(CollectionSupport.singletonList(new StringAttributeValue(s)));
attributes.add(attr);
}
- if (sessionIndexAttributeId != null && statement.getSessionIndex() != null) {
+ if (sessionIndexAttributeId != null && statement.getSessionIndex() instanceof String s) {
+ assert sessionIndexAttributeId != null;
final IdPAttribute attr = new IdPAttribute(sessionIndexAttributeId);
- attr.setValues(CollectionSupport.singletonList(new StringAttributeValue(statement.getSessionIndex())));
+ attr.setValues(CollectionSupport.singletonList(new StringAttributeValue(s)));
attributes.add(attr);
}
- if (consentAttributeId != null) {
- final String consent = ((StatusResponseType) assertion.getParent()).getConsent();
- if (consent != null) {
- final IdPAttribute attr = new IdPAttribute(consentAttributeId);
- attr.setValues(CollectionSupport.singletonList(new StringAttributeValue(consent)));
- attributes.add(attr);
- }
+ if (consentAttributeId != null && assertion.getParent() instanceof StatusResponseType srt
+ && srt.getConsent() instanceof String consent) {
+ assert consentAttributeId != null;
+ final IdPAttribute attr = new IdPAttribute(consentAttributeId);
+ attr.setValues(CollectionSupport.singletonList(new StringAttributeValue(consent)));
+ attributes.add(attr);
}
if (authorityAttributeId != null && ac != null && !ac.getAuthenticatingAuthorities().isEmpty()) {
+ assert authorityAttributeId != null;
final IdPAttribute attr = new IdPAttribute(authorityAttributeId);
attr.setValues(
ac.getAuthenticatingAuthorities().stream()
@@ -643,36 +695,43 @@ public class ExtractSAMLAttributes extends AbstractApplicationAction {
final Conditions conditions = assertion.getConditions();
- if (notBeforeAttributeId != null && conditions != null && conditions.getNotBefore() != null) {
+ if (notBeforeAttributeId != null && conditions != null
+ && conditions.getNotBefore() instanceof Instant ts) {
+ assert notBeforeAttributeId != null;
final IdPAttribute attr = new IdPAttribute(notBeforeAttributeId);
- attr.setValues(CollectionSupport.singletonList(new DateTimeAttributeValue(conditions.getNotBefore())));
+ attr.setValues(CollectionSupport.singletonList(new DateTimeAttributeValue(ts)));
attributes.add(attr);
}
- if (notOnOrAfterAttributeId != null && conditions != null && conditions.getNotOnOrAfter() != null) {
+ if (notOnOrAfterAttributeId != null && conditions != null
+ && conditions.getNotOnOrAfter() instanceof Instant ts) {
+ assert notOnOrAfterAttributeId != null;
final IdPAttribute attr = new IdPAttribute(notOnOrAfterAttributeId);
- attr.setValues(CollectionSupport.singletonList(new DateTimeAttributeValue(conditions.getNotOnOrAfter())));
+ attr.setValues(CollectionSupport.singletonList(new DateTimeAttributeValue(ts)));
attributes.add(attr);
}
- if (sessionNotOnOrAfterAttributeId != null && statement.getSessionNotOnOrAfter() != null) {
+ if (sessionNotOnOrAfterAttributeId != null
+ && statement.getSessionNotOnOrAfter() instanceof Instant ts) {
+ assert sessionNotOnOrAfterAttributeId != null;
final IdPAttribute attr = new IdPAttribute(sessionNotOnOrAfterAttributeId);
- attr.setValues(CollectionSupport.singletonList(
- new DateTimeAttributeValue(statement.getSessionNotOnOrAfter())));
+ attr.setValues(CollectionSupport.singletonList(new DateTimeAttributeValue(ts)));
attributes.add(attr);
}
final SubjectLocality locality = statement.getSubjectLocality();
- if (addressAttributeId != null && locality != null && locality.getAddress() != null) {
+ if (addressAttributeId != null && locality != null && locality.getAddress() instanceof String s) {
+ assert addressAttributeId != null;
final IdPAttribute attr = new IdPAttribute(addressAttributeId);
- attr.setValues(CollectionSupport.singletonList(new StringAttributeValue(locality.getAddress())));
+ attr.setValues(CollectionSupport.singletonList(new StringAttributeValue(s)));
attributes.add(attr);
}
- if (dnsNameAttributeId != null && locality != null && locality.getDNSName() != null) {
+ if (dnsNameAttributeId != null && locality != null && locality.getDNSName() instanceof String s) {
+ assert dnsNameAttributeId != null;
final IdPAttribute attr = new IdPAttribute(dnsNameAttributeId);
- attr.setValues(CollectionSupport.singletonList(new StringAttributeValue(locality.getDNSName())));
+ attr.setValues(CollectionSupport.singletonList(new StringAttributeValue(s)));
attributes.add(attr);
}
diff --git a/sp-saml-impl/src/test/java/net/shibboleth/sp/saml/saml2/nameid/transcoding/impl/SAML2ScopedStringNameIDTranscoderTest.java b/sp-saml-impl/src/test/java/net/shibboleth/sp/saml/saml2/nameid/transcoding/impl/SAML2ScopedStringNameIDTranscoderTest.java
new file mode 100644
index 0000000..cba604a
--- /dev/null
+++ b/sp-saml-impl/src/test/java/net/shibboleth/sp/saml/saml2/nameid/transcoding/impl/SAML2ScopedStringNameIDTranscoderTest.java
@@ -0,0 +1,144 @@
+/*
+ * 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.saml.saml2.nameid.transcoding.impl;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.core.testing.OpenSAMLInitBaseTestCase;
+import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
+import org.opensaml.saml.common.SAMLObjectBuilder;
+import org.opensaml.saml.saml2.core.NameID;
+import org.testng.Assert;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import net.shibboleth.idp.attribute.AttributeEncodingException;
+import net.shibboleth.idp.attribute.IdPAttribute;
+import net.shibboleth.idp.attribute.ScopedStringAttributeValue;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry;
+import net.shibboleth.idp.attribute.transcoding.BasicNamingFunction;
+import net.shibboleth.idp.attribute.transcoding.TranscoderSupport;
+import net.shibboleth.idp.attribute.transcoding.TranscodingRule;
+import net.shibboleth.idp.attribute.transcoding.impl.AttributeTranscoderRegistryImpl;
+import net.shibboleth.idp.saml.attribute.transcoding.SAML2AttributeTranscoder;
+import net.shibboleth.shared.annotation.constraint.NotEmpty;
+import net.shibboleth.shared.collection.CollectionSupport;
+import net.shibboleth.shared.component.ComponentInitializationException;
+import net.shibboleth.shared.testing.MockApplicationContext;
+import net.shibboleth.sp.saml.saml2.nameid.transcoding.AbstractSAML2NameIDTranscoder;
+
+/** {@link SAML2ScopedStringNameIDTranscoder} unit test. */
+ at SuppressWarnings("javadoc")
+public class SAML2ScopedStringNameIDTranscoderTest extends OpenSAMLInitBaseTestCase {
+
+ private AttributeTranscoderRegistryImpl registry;
+
+ private SAMLObjectBuilder<NameID> nameIDBuilder;
+
+ @Nonnull @NotEmpty private final static String ID = "NameIDAttribute";
+ @Nonnull @NotEmpty private final static String NAMEID_FORMAT = "Format";
+ @Nonnull @NotEmpty private final static String STRING_1 = "Value The First";
+ @Nonnull @NotEmpty private final static String STRING_2 = "Second string the value is";
+
+ @BeforeClass public void setUp() throws ComponentInitializationException {
+ nameIDBuilder = (SAMLObjectBuilder<NameID>)
+ XMLObjectProviderRegistrySupport.getBuilderFactory().<NameID>ensureBuilder(
+ NameID.DEFAULT_ELEMENT_NAME);
+
+ registry = new AttributeTranscoderRegistryImpl();
+ registry.setId("test");
+
+ final SAML2ScopedStringNameIDTranscoder transcoder = new SAML2ScopedStringNameIDTranscoder();
+ transcoder.initialize();
+
+ registry.setNamingRegistry(CollectionSupport.singletonList(
+ new BasicNamingFunction<>(transcoder.getEncodedType(), new AbstractSAML2NameIDTranscoder.NamingFunction())));
+
+ final Map<String,Object> ruleset1 = new HashMap<>();
+ ruleset1.put(AttributeTranscoderRegistry.PROP_ID, ID);
+ ruleset1.put(AttributeTranscoderRegistry.PROP_TRANSCODER, transcoder);
+ ruleset1.put(SAML2AttributeTranscoder.PROP_NAME_FORMAT, NAMEID_FORMAT);
+ ruleset1.put(SAML2StringNameIDTranscoder.PROP_TEMPLATE, "$Name");
+
+ registry.setTranscoderRegistry(CollectionSupport.singletonList(new TranscodingRule(ruleset1)));
+ registry.setApplicationContext(new MockApplicationContext());
+ registry.initialize();
+ }
+
+ @AfterClass public void tearDown() {
+ registry.destroy();
+ registry = null;
+ }
+
+ @Test(expectedExceptions=AttributeEncodingException.class)
+ public void failedEncode() throws Exception {
+ final IdPAttribute inputAttribute = new IdPAttribute(ID);
+
+ final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(inputAttribute, NameID.class);
+ Assert.assertEquals(rulesets.size(), 1);
+ final TranscodingRule ruleset = rulesets.iterator().next();
+ assert ruleset != null;
+
+ TranscoderSupport.<NameID>getTranscoder(ruleset).encode(null, inputAttribute, NameID.class, ruleset);
+ }
+
+ @Test public void emptyDecode() throws Exception {
+
+ final NameID nameID = nameIDBuilder.buildObject();
+ Collection<TranscodingRule> rulesets = registry.getTranscodingRules(nameID);
+ Assert.assertEquals(rulesets.size(), 0);
+
+ nameID.setFormat(NAMEID_FORMAT);
+
+ rulesets = registry.getTranscodingRules(nameID);
+ Assert.assertEquals(rulesets.size(), 1);
+ final TranscodingRule ruleset = rulesets.iterator().next();
+ assert ruleset != null;
+
+ final IdPAttribute attr = TranscoderSupport.<NameID>getTranscoder(ruleset).decode(null, nameID, ruleset);
+ assert attr != null;
+ Assert.assertEquals(attr.getId(), ID);
+ Assert.assertTrue(attr.getValues().isEmpty());
+ }
+
+ @Test public void decode() throws Exception {
+
+ final NameID nameID = nameIDBuilder.buildObject();
+ nameID.setFormat(NAMEID_FORMAT);
+ nameID.setNameQualifier(STRING_2);
+ nameID.setValue(STRING_1);
+
+ final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(nameID);
+ Assert.assertEquals(rulesets.size(), 1);
+ final TranscodingRule ruleset = rulesets.iterator().next();
+ assert ruleset != null;
+
+ final IdPAttribute attr = TranscoderSupport.<NameID>getTranscoder(ruleset).decode(null, nameID, ruleset);
+
+ assert attr != null;
+ Assert.assertEquals(attr.getId(), ID);
+ Assert.assertEquals(attr.getValues().size(), 1);
+
+ final ScopedStringAttributeValue value = (ScopedStringAttributeValue) attr.getValues().get(0);
+ Assert.assertEquals(value.getValue(), STRING_1);
+ Assert.assertEquals(value.getScope(), STRING_2);
+ }
+
+}
\ No newline at end of file
diff --git a/sp-saml-impl/src/test/java/net/shibboleth/sp/saml/saml2/nameid/transcoding/impl/SAML2StringNameIDTranscoderTest.java b/sp-saml-impl/src/test/java/net/shibboleth/sp/saml/saml2/nameid/transcoding/impl/SAML2StringNameIDTranscoderTest.java
new file mode 100644
index 0000000..5f0c784
--- /dev/null
+++ b/sp-saml-impl/src/test/java/net/shibboleth/sp/saml/saml2/nameid/transcoding/impl/SAML2StringNameIDTranscoderTest.java
@@ -0,0 +1,141 @@
+/*
+ * 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.saml.saml2.nameid.transcoding.impl;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.core.testing.OpenSAMLInitBaseTestCase;
+import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
+import org.opensaml.saml.common.SAMLObjectBuilder;
+import org.opensaml.saml.saml2.core.NameID;
+import org.testng.Assert;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import net.shibboleth.idp.attribute.AttributeEncodingException;
+import net.shibboleth.idp.attribute.IdPAttribute;
+import net.shibboleth.idp.attribute.StringAttributeValue;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry;
+import net.shibboleth.idp.attribute.transcoding.BasicNamingFunction;
+import net.shibboleth.idp.attribute.transcoding.TranscoderSupport;
+import net.shibboleth.idp.attribute.transcoding.TranscodingRule;
+import net.shibboleth.idp.attribute.transcoding.impl.AttributeTranscoderRegistryImpl;
+import net.shibboleth.idp.saml.attribute.transcoding.SAML2AttributeTranscoder;
+import net.shibboleth.shared.annotation.constraint.NotEmpty;
+import net.shibboleth.shared.collection.CollectionSupport;
+import net.shibboleth.shared.component.ComponentInitializationException;
+import net.shibboleth.shared.testing.MockApplicationContext;
+import net.shibboleth.sp.saml.saml2.nameid.transcoding.AbstractSAML2NameIDTranscoder;
+
+/** {@link SAML2StringNameIDTranscoder} unit test. */
+ at SuppressWarnings("javadoc")
+public class SAML2StringNameIDTranscoderTest extends OpenSAMLInitBaseTestCase {
+
+ private AttributeTranscoderRegistryImpl registry;
+
+ private SAMLObjectBuilder<NameID> nameIDBuilder;
+
+ @Nonnull @NotEmpty private final static String ID = "NameIDAttribute";
+ @Nonnull @NotEmpty private final static String NAMEID_FORMAT = "Format";
+ @Nonnull @NotEmpty private final static String STRING_1 = "Value The First";
+ @Nonnull @NotEmpty private final static String STRING_2 = "Second string the value is";
+
+ @BeforeClass public void setUp() throws ComponentInitializationException {
+ nameIDBuilder = (SAMLObjectBuilder<NameID>)
+ XMLObjectProviderRegistrySupport.getBuilderFactory().<NameID>ensureBuilder(
+ NameID.DEFAULT_ELEMENT_NAME);
+
+ registry = new AttributeTranscoderRegistryImpl();
+ registry.setId("test");
+
+ final SAML2StringNameIDTranscoder transcoder = new SAML2StringNameIDTranscoder();
+ transcoder.initialize();
+
+ registry.setNamingRegistry(CollectionSupport.singletonList(
+ new BasicNamingFunction<>(transcoder.getEncodedType(), new AbstractSAML2NameIDTranscoder.NamingFunction())));
+
+ final Map<String,Object> ruleset1 = new HashMap<>();
+ ruleset1.put(AttributeTranscoderRegistry.PROP_ID, ID);
+ ruleset1.put(AttributeTranscoderRegistry.PROP_TRANSCODER, transcoder);
+ ruleset1.put(SAML2AttributeTranscoder.PROP_NAME_FORMAT, NAMEID_FORMAT);
+ ruleset1.put(SAML2StringNameIDTranscoder.PROP_TEMPLATE, "$Name!!$NameQualifier");
+
+ registry.setTranscoderRegistry(CollectionSupport.singletonList(new TranscodingRule(ruleset1)));
+ registry.setApplicationContext(new MockApplicationContext());
+ registry.initialize();
+ }
+
+ @AfterClass public void tearDown() {
+ registry.destroy();
+ registry = null;
+ }
+
+ @Test(expectedExceptions=AttributeEncodingException.class)
+ public void failedEncode() throws Exception {
+ final IdPAttribute inputAttribute = new IdPAttribute(ID);
+
+ final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(inputAttribute, NameID.class);
+ Assert.assertEquals(rulesets.size(), 1);
+ final TranscodingRule ruleset = rulesets.iterator().next();
+ assert ruleset != null;
+
+ TranscoderSupport.<NameID>getTranscoder(ruleset).encode(null, inputAttribute, NameID.class, ruleset);
+ }
+
+ @Test public void emptyDecode() throws Exception {
+
+ final NameID nameID = nameIDBuilder.buildObject();
+ Collection<TranscodingRule> rulesets = registry.getTranscodingRules(nameID);
+ Assert.assertEquals(rulesets.size(), 0);
+
+ nameID.setFormat(NAMEID_FORMAT);
+
+ rulesets = registry.getTranscodingRules(nameID);
+ Assert.assertEquals(rulesets.size(), 1);
+ final TranscodingRule ruleset = rulesets.iterator().next();
+ assert ruleset != null;
+
+ final IdPAttribute attr = TranscoderSupport.<NameID>getTranscoder(ruleset).decode(null, nameID, ruleset);
+ assert attr != null;
+ Assert.assertEquals(attr.getId(), ID);
+ Assert.assertTrue(attr.getValues().isEmpty());
+ }
+
+ @Test public void decode() throws Exception {
+
+ final NameID nameID = nameIDBuilder.buildObject();
+ nameID.setFormat(NAMEID_FORMAT);
+ nameID.setNameQualifier(STRING_2);
+ nameID.setValue(STRING_1);
+
+ final Collection<TranscodingRule> rulesets = registry.getTranscodingRules(nameID);
+ Assert.assertEquals(rulesets.size(), 1);
+ final TranscodingRule ruleset = rulesets.iterator().next();
+ assert ruleset != null;
+
+ final IdPAttribute attr = TranscoderSupport.<NameID>getTranscoder(ruleset).decode(null, nameID, ruleset);
+
+ assert attr != null;
+ Assert.assertEquals(attr.getId(), ID);
+ Assert.assertEquals(attr.getValues().size(), 1);
+ Assert.assertEquals(((StringAttributeValue)attr.getValues().get(0)).getValue().toString(), STRING_1 + "!!" + STRING_2);
+ }
+
+}
\ 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