[java-identity-provider] branch main updated: IDP-1729 - Extend attribute-sourced c14n to IdPAttributePrincipal
Scott Cantor
cantor.2 at osu.edu
Wed Dec 30 22:32:30 UTC 2020
This is an automated email from the git hooks/post-receive script.
scantor pushed a commit to branch main
in repository java-identity-provider.
View the commit online:
http://git.shibboleth.net/view/?p=java-identity-provider.git;a=commit;h=fad5d5597fcb425e670289905f31373055f4fd2f
The following commit(s) were added to refs/heads/main by this push:
new fad5d5597 IDP-1729 - Extend attribute-sourced c14n to IdPAttributePrincipal
fad5d5597 is described below
commit fad5d5597fcb425e670289905f31373055f4fd2f
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Wed Dec 30 17:32:24 2020 -0500
IDP-1729 - Extend attribute-sourced c14n to IdPAttributePrincipal
https://issues.shibboleth.net/jira/browse/IDP-1729
---
.../AttributeSourcedSubjectCanonicalization.java | 116 ++++++++++++++++-----
...ttributeSourcedSubjectCanonicalizationTest.java | 102 ++++++++++++++++--
.../c14n/attribute-sourced-subject-c14n-beans.xml | 2 +
.../resources/conf/c14n/subject-c14n.properties | 2 +
4 files changed, 189 insertions(+), 33 deletions(-)
diff --git a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/AttributeSourcedSubjectCanonicalization.java b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/AttributeSourcedSubjectCanonicalization.java
index ff2727d9d..7305440ab 100644
--- a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/AttributeSourcedSubjectCanonicalization.java
+++ b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/AttributeSourcedSubjectCanonicalization.java
@@ -19,11 +19,15 @@ package net.shibboleth.idp.authn.impl;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
+import java.util.Set;
import java.util.function.Function;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
+import javax.security.auth.Subject;
import net.shibboleth.idp.attribute.IdPAttribute;
import net.shibboleth.idp.attribute.IdPAttributeValue;
@@ -34,6 +38,7 @@ import net.shibboleth.idp.authn.AbstractSubjectCanonicalizationAction;
import net.shibboleth.idp.authn.AuthnEventIds;
import net.shibboleth.idp.authn.SubjectCanonicalizationException;
import net.shibboleth.idp.authn.context.SubjectCanonicalizationContext;
+import net.shibboleth.idp.authn.principal.IdPAttributePrincipal;
import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
import net.shibboleth.utilities.java.support.component.ComponentSupport;
@@ -70,6 +75,12 @@ public class AttributeSourcedSubjectCanonicalization extends AbstractSubjectCano
/** Delimiter to use for scoped attribute serialization. */
private char delimiter;
+ /** Whether to also check the original Subject for {@link IdPAttributePrincipal}s. */
+ private boolean resolveFromSubject;
+
+ /** Indexed attributes pulled from subject. */
+ @Nonnull @NonnullElements private Map<String,IdPAttribute> subjectSourcedAttributes;
+
/** Ordered list of attributes to look for and read from. */
@Nonnull @NonnullElements private List<String> attributeSourceIds;
@@ -83,6 +94,7 @@ public class AttributeSourcedSubjectCanonicalization extends AbstractSubjectCano
public AttributeSourcedSubjectCanonicalization() {
delimiter = '@';
attributeSourceIds = Collections.emptyList();
+ subjectSourcedAttributes = Collections.emptyMap();
attributeContextLookupStrategy =
new ChildContextLookup<>(AttributeContext.class).compose(
@@ -100,6 +112,20 @@ public class AttributeSourcedSubjectCanonicalization extends AbstractSubjectCano
delimiter = ch;
}
+ /**
+ * Whether to include any {@link IdPAttributePrincipal} objects found in the input {@link Subject}
+ * when searching for a matching attribute ID.
+ *
+ * @param flag flag to set
+ *
+ * @since 4.1.0
+ */
+ public void setResolveFromSubject(final boolean flag) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+ resolveFromSubject = flag;
+ }
+
/**
* Set the attribute IDs to read from in order of preference.
*
@@ -139,48 +165,52 @@ public class AttributeSourcedSubjectCanonicalization extends AbstractSubjectCano
protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext,
@Nonnull final SubjectCanonicalizationContext c14nContext) {
+ if (!super.doPreExecute(profileRequestContext, c14nContext)) {
+ return false;
+ }
+
+ if (resolveFromSubject) {
+ final Set<IdPAttributePrincipal> subjectSourced =
+ c14nContext.getSubject().getPrincipals(IdPAttributePrincipal.class);
+ if (subjectSourced != null && !subjectSourced.isEmpty()) {
+ subjectSourcedAttributes = new HashMap<>(subjectSourced.size());
+ subjectSourced.forEach(a -> subjectSourcedAttributes.put(a.getAttribute().getId(), a.getAttribute()));
+ }
+ }
+
attributeCtx = attributeContextLookupStrategy.apply(profileRequestContext);
- if (attributeCtx == null || attributeCtx.getIdPAttributes().isEmpty()) {
+ if (subjectSourcedAttributes.isEmpty() && (attributeCtx == null || attributeCtx.getIdPAttributes().isEmpty())) {
log.warn("{} No attributes found, canonicalization not possible", getLogPrefix());
c14nContext.setException(new SubjectCanonicalizationException("No attributes were found"));
ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.INVALID_SUBJECT);
return false;
}
- return super.doPreExecute(profileRequestContext, c14nContext);
+ return true;
}
/** {@inheritDoc} */
- // CheckStyle: ReturnCount OFF
@Override
protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
@Nonnull final SubjectCanonicalizationContext c14nContext) {
for (final String id : attributeSourceIds) {
- final IdPAttribute attr = attributeCtx.getIdPAttributes().get(id);
- if (attr == null) {
- continue;
- }
- for (final IdPAttributeValue val : attr.getValues()) {
- if (val instanceof StringAttributeValue) {
- final StringAttributeValue stringVal = (StringAttributeValue) val;
- if (stringVal.getValue() == null || stringVal.getValue().isEmpty()) {
- log.debug("{} Ignoring null/empty string value", getLogPrefix());
- continue;
- }
- log.debug("{} Using attribute {} string value {} as input to transforms", getLogPrefix(), id,
- stringVal.getValue());
- c14nContext.setPrincipalName(applyTransforms(stringVal.getValue()));
+
+ IdPAttribute attr = subjectSourcedAttributes.get(id);
+ if (attr != null) {
+ final String result = findValue(attr);
+ if (result != null) {
+ c14nContext.setPrincipalName(result);
return;
- } else if (val instanceof ScopedStringAttributeValue) {
- final ScopedStringAttributeValue scoped = (ScopedStringAttributeValue) val;
- final String withScope = scoped.getValue() + delimiter + scoped.getScope();
- log.debug("{} Using attribute {} scoped value {} as input to transforms", getLogPrefix(), id,
- withScope);
- c14nContext.setPrincipalName(applyTransforms(withScope));
- return;
- } else {
- log.warn("{} Unsupported attribute value type: {}", getLogPrefix(), val.getClass().getName());
+ }
+ } else if (attributeCtx != null) {
+ attr = attributeCtx.getIdPAttributes().get(id);
+ if (attr != null) {
+ final String result = findValue(attr);
+ if (result != null) {
+ c14nContext.setPrincipalName(result);
+ return;
+ }
}
}
}
@@ -189,6 +219,38 @@ public class AttributeSourcedSubjectCanonicalization extends AbstractSubjectCano
c14nContext.setException(new SubjectCanonicalizationException("No usable attribute values were found"));
ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.INVALID_SUBJECT);
}
- // CheckStyle: ReturnCount ON
+ /**
+ * Check for a compatible value in the input attribute.
+ *
+ * @param attribute input attribute
+ *
+ * @return value to use for result, or null
+ */
+ @Nullable private String findValue(@Nonnull final IdPAttribute attribute) {
+
+ for (final IdPAttributeValue val : attribute.getValues()) {
+ if (val instanceof StringAttributeValue) {
+ final StringAttributeValue stringVal = (StringAttributeValue) val;
+ if (stringVal.getValue() == null || stringVal.getValue().isEmpty()) {
+ log.debug("{} Ignoring null/empty string value", getLogPrefix());
+ continue;
+ }
+ log.debug("{} Using attribute {} string value {} as input to transforms", getLogPrefix(),
+ attribute.getId(), stringVal.getValue());
+ return applyTransforms(stringVal.getValue());
+ } else if (val instanceof ScopedStringAttributeValue) {
+ final ScopedStringAttributeValue scoped = (ScopedStringAttributeValue) val;
+ final String withScope = scoped.getValue() + delimiter + scoped.getScope();
+ log.debug("{} Using attribute {} scoped value {} as input to transforms", getLogPrefix(),
+ attribute.getId(), withScope);
+ return applyTransforms(withScope);
+ } else {
+ log.warn("{} Unsupported attribute value type: {}", getLogPrefix(), val.getClass().getName());
+ }
+ }
+
+ return null;
+ }
+
}
\ No newline at end of file
diff --git a/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/AttributeSourcedSubjectCanonicalizationTest.java b/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/AttributeSourcedSubjectCanonicalizationTest.java
index 08a4c1891..df0663213 100644
--- a/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/AttributeSourcedSubjectCanonicalizationTest.java
+++ b/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/AttributeSourcedSubjectCanonicalizationTest.java
@@ -17,8 +17,8 @@
package net.shibboleth.idp.authn.impl;
-import java.util.Arrays;
import java.util.Collections;
+import java.util.List;
import javax.security.auth.Subject;
@@ -33,6 +33,7 @@ import net.shibboleth.idp.attribute.context.AttributeContext;
import net.shibboleth.idp.authn.AuthnEventIds;
import net.shibboleth.idp.authn.context.SubjectCanonicalizationContext;
import net.shibboleth.idp.authn.impl.testing.BaseAuthenticationContextTest;
+import net.shibboleth.idp.authn.principal.IdPAttributePrincipal;
import net.shibboleth.idp.profile.testing.ActionTestingSupport;
import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
@@ -46,8 +47,7 @@ public class AttributeSourcedSubjectCanonicalizationTest extends BaseAuthenticat
super.setUp();
action = new AttributeSourcedSubjectCanonicalization();
- action.setAttributeSourceIds(Arrays.asList("attr1", "attr2"));
- action.initialize();
+ action.setAttributeSourceIds(List.of("attr1", "attr2"));
}
@Test(expectedExceptions=ComponentInitializationException.class)
@@ -56,13 +56,30 @@ public class AttributeSourcedSubjectCanonicalizationTest extends BaseAuthenticat
action.initialize();
}
- @Test public void testNoContext() {
+ @Test public void testNoContext() throws ComponentInitializationException {
+ action.initialize();
+
final Event event = action.execute(src);
ActionTestingSupport.assertEvent(event, AuthnEventIds.INVALID_SUBJECT_C14N_CTX);
}
- @Test public void testNoAttributes() {
+ @Test public void testNoAttributes() throws ComponentInitializationException {
+ action.initialize();
+
+ Subject subject = new Subject();
+ prc.getSubcontext(SubjectCanonicalizationContext.class, true).setSubject(subject);
+
+ final Event event = action.execute(src);
+
+ ActionTestingSupport.assertEvent(event, AuthnEventIds.INVALID_SUBJECT);
+ Assert.assertNotNull(prc.getSubcontext(SubjectCanonicalizationContext.class, false).getException());
+ }
+
+ @Test public void testNoSubjectSourcedAttributes() throws ComponentInitializationException {
+ action.setResolveFromSubject(true);
+ action.initialize();
+
Subject subject = new Subject();
prc.getSubcontext(SubjectCanonicalizationContext.class, true).setSubject(subject);
@@ -72,11 +89,15 @@ public class AttributeSourcedSubjectCanonicalizationTest extends BaseAuthenticat
Assert.assertNotNull(prc.getSubcontext(SubjectCanonicalizationContext.class, false).getException());
}
- @Test public void testSuccess() {
+ @Test public void testSuccess() throws ComponentInitializationException {
+ action.initialize();
+
final IdPAttribute inputAttribute = new IdPAttribute("attr2");
inputAttribute.setValues(Collections.singletonList(new StringAttributeValue("foo")));
+
final SubjectCanonicalizationContext sc = prc.getSubcontext(SubjectCanonicalizationContext.class, true);
sc.setSubject(new Subject());
+
sc.getSubcontext(AttributeContext.class, true).setIdPAttributes(Collections.singleton(inputAttribute));
final Event event = action.execute(src);
@@ -85,4 +106,73 @@ public class AttributeSourcedSubjectCanonicalizationTest extends BaseAuthenticat
Assert.assertEquals(sc.getPrincipalName(), "foo");
}
+ @Test public void testSubjectSourcedSuccess() throws ComponentInitializationException {
+ action.setResolveFromSubject(true);
+ action.initialize();
+
+ final IdPAttribute inputAttribute = new IdPAttribute("attr2");
+ inputAttribute.setValues(Collections.singletonList(new StringAttributeValue("foo")));
+ final SubjectCanonicalizationContext sc = prc.getSubcontext(SubjectCanonicalizationContext.class, true);
+ sc.setSubject(new Subject());
+ sc.getSubject().getPrincipals().add(new IdPAttributePrincipal(inputAttribute));
+
+ final Event event = action.execute(src);
+
+ ActionTestingSupport.assertProceedEvent(event);
+ Assert.assertEquals(sc.getPrincipalName(), "foo");
+ }
+
+ @Test public void testDualSubjectSourcedSuccess() throws ComponentInitializationException {
+ action.setResolveFromSubject(true);
+ action.initialize();
+
+ final IdPAttribute attr2 = new IdPAttribute("attr2");
+ attr2.setValues(Collections.singletonList(new StringAttributeValue("foo")));
+
+ final IdPAttribute attr2bar = new IdPAttribute("attr2");
+ attr2bar.setValues(Collections.singletonList(new StringAttributeValue("bar")));
+
+ final SubjectCanonicalizationContext sc = prc.getSubcontext(SubjectCanonicalizationContext.class, true);
+ sc.setSubject(new Subject());
+ sc.getSubject().getPrincipals().add(new IdPAttributePrincipal(attr2));
+
+ sc.getSubcontext(AttributeContext.class, true).setIdPAttributes(Collections.singleton(attr2bar));
+
+ Event event = action.execute(src);
+
+ ActionTestingSupport.assertProceedEvent(event);
+ Assert.assertEquals(sc.getPrincipalName(), "foo");
+
+ sc.getSubject().getPrincipals().clear();
+ sc.getSubject().getPrincipals().add(new IdPAttributePrincipal(attr2bar));
+ sc.getSubcontext(AttributeContext.class, true).setIdPAttributes(Collections.singleton(attr2));
+
+ event = action.execute(src);
+
+ ActionTestingSupport.assertProceedEvent(event);
+ Assert.assertEquals(sc.getPrincipalName(), "bar");
+ }
+
+ @Test public void testDualSubjectSourcedSuccess2() throws ComponentInitializationException {
+ action.setResolveFromSubject(true);
+ action.initialize();
+
+ final IdPAttribute attr2 = new IdPAttribute("attr2");
+ attr2.setValues(Collections.singletonList(new StringAttributeValue("bar")));
+
+ final IdPAttribute attr1 = new IdPAttribute("attr1");
+ attr1.setValues(Collections.singletonList(new StringAttributeValue("foo")));
+
+ final SubjectCanonicalizationContext sc = prc.getSubcontext(SubjectCanonicalizationContext.class, true);
+ sc.setSubject(new Subject());
+ sc.getSubject().getPrincipals().add(new IdPAttributePrincipal(attr2));
+
+ sc.getSubcontext(AttributeContext.class, true).setIdPAttributes(Collections.singleton(attr1));
+
+ final Event event = action.execute(src);
+
+ ActionTestingSupport.assertProceedEvent(event);
+ Assert.assertEquals(sc.getPrincipalName(), "foo");
+ }
+
}
\ No newline at end of file
diff --git a/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/c14n/attribute-sourced-subject-c14n-beans.xml b/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/c14n/attribute-sourced-subject-c14n-beans.xml
index 1131af5c4..d9840ac64 100644
--- a/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/c14n/attribute-sourced-subject-c14n-beans.xml
+++ b/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/c14n/attribute-sourced-subject-c14n-beans.xml
@@ -28,6 +28,7 @@
<bean id="ResolveAttributes" class="net.shibboleth.idp.profile.impl.ResolveAttributes" scope="prototype"
c:resolverService-ref="shibboleth.AttributeResolverService"
+ p:activationCondition="#{getObject('shibboleth.c14n.attribute.ResolutionCondition') ?: getObject('shibboleth.Conditions.TRUE')}"
p:attributesToResolve-ref="shibboleth.c14n.attribute.AttributesToResolve"
p:maskFailures="%{idp.service.attribute.resolver.maskFailures:true}"
p:principalNameLookupStrategy="#{getObject('shibboleth.c14n.attribute.PrincipalNameLookupStrategy')}">
@@ -41,6 +42,7 @@
<bean id="AttributeSourcedSubjectCanonicalization"
class="net.shibboleth.idp.authn.impl.AttributeSourcedSubjectCanonicalization" scope="prototype"
p:attributeSourceIds-ref="shibboleth.c14n.attribute.AttributeSourceIds"
+ p:resolveFromSubject="%{idp.c14n.attribute.resolveFromSubject:false}"
p:lowercase="#{getObject('shibboleth.c14n.attribute.Lowercase') ?: %{idp.c14n.attribute.lowercase:false}}"
p:uppercase="#{getObject('shibboleth.c14n.attribute.Uppercase') ?: %{idp.c14n.attribute.uppercase:false}}"
p:trim="#{getObject('shibboleth.c14n.attribute.Trim') ?: %{idp.c14n.attribute.trim:true}}"
diff --git a/idp-conf/src/main/resources/conf/c14n/subject-c14n.properties b/idp-conf/src/main/resources/conf/c14n/subject-c14n.properties
index 8a2c8f4dc..a25892b1a 100644
--- a/idp-conf/src/main/resources/conf/c14n/subject-c14n.properties
+++ b/idp-conf/src/main/resources/conf/c14n/subject-c14n.properties
@@ -12,6 +12,8 @@
#idp.c14n.attribute.lowercase = false
#idp.c14n.attribute.uppercase = false
#idp.c14n.attribute.trim = true
+# Allows direct use of attributes via SAML/etc. authentication
+#idp.c14n.attribute.resolveFromSubject = false
# Lists of attributes to resolve...
#idp.c14n.attribute.attributesToResolve =
# and then select a principal name from
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list