sessionIndexes = BaseOpenSamlAuthenticationProvider.getSessionIndexes(assertion);
+ DefaultSaml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal(username, attributes,
+ sessionIndexes);
+ String registrationId = responseToken.token.getRelyingPartyRegistration().getRegistrationId();
+ principal.setRelyingPartyRegistrationId(registrationId);
+ return new Saml2Authentication(principal, token.getSaml2Response(),
+ this.grantedAuthoritiesConverter.convert(assertion));
+ }
+
+ /**
+ * Use this strategy to extract the principal name from the {@link Assertion}. By
+ * default, this will retrieve it from the
+ * {@link org.opensaml.saml.saml2.core.Subject}'s
+ * {@link org.opensaml.saml.saml2.core.NameID} value.
+ *
+ *
+ * Note that because of this, if there is no
+ * {@link org.opensaml.saml.saml2.core.NameID} present, then the default throws an
+ * exception.
+ *
+ * @param principalNameConverter the conversion strategy to use
+ */
+ public void setPrincipalNameConverter(Converter principalNameConverter) {
+ Assert.notNull(principalNameConverter, "principalNameConverter cannot be null");
+ this.principalNameConverter = principalNameConverter;
+ }
+
+ /**
+ * Use this strategy to grant authorities to a principal given the first
+ * {@link Assertion} in the response. By default, this will grant
+ * {@code ROLE_USER}.
+ * @param grantedAuthoritiesConverter the conversion strategy to use
+ */
+ public void setGrantedAuthoritiesConverter(
+ Converter> grantedAuthoritiesConverter) {
+ Assert.notNull(grantedAuthoritiesConverter, "grantedAuthoritiesConverter cannot be null");
+ this.grantedAuthoritiesConverter = grantedAuthoritiesConverter;
+ }
+
+ private static String authenticatedPrincipal(Assertion assertion) {
+ if (!BaseOpenSamlAuthenticationProvider.hasName(assertion)) {
+ throw new Saml2AuthenticationException(new Saml2Error(Saml2ErrorCodes.SUBJECT_NOT_FOUND,
+ "Assertion [" + assertion.getID() + "] is missing a subject"));
+ }
+ return assertion.getSubject().getNameID().getValue();
+ }
+
+ private static Collection grantedAuthorities(Assertion assertion) {
+ return AuthorityUtils.createAuthorityList("ROLE_USER");
+ }
+
+ }
+
}
diff --git a/saml2/saml2-service-provider/src/opensaml5Test/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml5AuthenticationProviderTests.java b/saml2/saml2-service-provider/src/opensaml5Test/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml5AuthenticationProviderTests.java
index 3c324875a9..071a8af128 100644
--- a/saml2/saml2-service-provider/src/opensaml5Test/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml5AuthenticationProviderTests.java
+++ b/saml2/saml2-service-provider/src/opensaml5Test/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml5AuthenticationProviderTests.java
@@ -22,6 +22,7 @@ import java.io.ObjectOutputStream;
import java.time.Duration;
import java.time.Instant;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
@@ -71,12 +72,15 @@ import org.opensaml.xmlsec.signature.support.SignatureConstants;
import org.springframework.core.convert.converter.Converter;
import org.springframework.security.core.Authentication;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.jackson2.SecurityJackson2Modules;
import org.springframework.security.saml2.core.Saml2Error;
import org.springframework.security.saml2.core.Saml2ErrorCodes;
import org.springframework.security.saml2.core.Saml2ResponseValidatorResult;
import org.springframework.security.saml2.core.TestSaml2X509Credentials;
import org.springframework.security.saml2.provider.service.authentication.OpenSaml5AuthenticationProvider.AssertionValidator;
+import org.springframework.security.saml2.provider.service.authentication.OpenSaml5AuthenticationProvider.ResponseAuthenticationConverter;
import org.springframework.security.saml2.provider.service.authentication.OpenSaml5AuthenticationProvider.ResponseToken;
import org.springframework.security.saml2.provider.service.authentication.OpenSaml5AuthenticationProvider.ResponseValidator;
import org.springframework.security.saml2.provider.service.authentication.TestCustomOpenSaml5Objects.CustomOpenSamlObject;
@@ -92,6 +96,7 @@ import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
/**
* Tests for {@link OpenSaml5AuthenticationProvider}
@@ -660,6 +665,47 @@ public class OpenSaml5AuthenticationProviderTests {
verify(authenticationConverter).convert(any());
}
+ @Test
+ public void authenticateWhenResponseAuthenticationConverterComponentConfiguredThenUses() {
+ Converter> grantedAuthoritiesConverter = mock(Converter.class);
+ given(grantedAuthoritiesConverter.convert(any())).willReturn(AuthorityUtils.createAuthorityList("CUSTOM"));
+ ResponseAuthenticationConverter authenticationConverter = new ResponseAuthenticationConverter();
+ authenticationConverter.setGrantedAuthoritiesConverter(grantedAuthoritiesConverter);
+ OpenSaml5AuthenticationProvider provider = new OpenSaml5AuthenticationProvider();
+ provider.setResponseAuthenticationConverter(authenticationConverter);
+ Response response = TestOpenSamlObjects.signedResponseWithOneAssertion();
+ Saml2AuthenticationToken token = token(response, verifying(registration()));
+ Authentication authentication = provider.authenticate(token);
+ assertThat(AuthorityUtils.authorityListToSet(authentication.getAuthorities())).containsExactly("CUSTOM");
+ verify(grantedAuthoritiesConverter).convert(any());
+ }
+
+ @Test
+ public void authenticateWhenValidateResponseAfterAssertionsThenCanHaveResponseAuthenticationConverterThatDoesntNeedANameID() {
+ Converter responseAuthenticationConverter = mock(Converter.class);
+ OpenSaml5AuthenticationProvider provider = new OpenSaml5AuthenticationProvider();
+ provider.setValidateResponseAfterAssertions(true);
+ provider.setResponseAuthenticationConverter(responseAuthenticationConverter);
+ Response response = TestOpenSamlObjects
+ .signedResponseWithOneAssertion((r) -> r.getAssertions().get(0).setSubject(null));
+ Saml2AuthenticationToken token = token(response, verifying(registration()));
+ provider.authenticate(token);
+ verify(responseAuthenticationConverter).convert(any());
+ }
+
+ @Test
+ public void authenticateWhenValidateResponseBeforeAssertionsThenMustHaveNameID() {
+ Converter responseAuthenticationConverter = mock(Converter.class);
+ OpenSaml5AuthenticationProvider provider = new OpenSaml5AuthenticationProvider();
+ provider.setValidateResponseAfterAssertions(false);
+ provider.setResponseAuthenticationConverter(responseAuthenticationConverter);
+ Response response = TestOpenSamlObjects
+ .signedResponseWithOneAssertion((r) -> r.getAssertions().get(0).setSubject(null));
+ Saml2AuthenticationToken token = token(response, verifying(registration()));
+ assertThatExceptionOfType(Saml2AuthenticationException.class).isThrownBy(() -> provider.authenticate(token));
+ verifyNoInteractions(responseAuthenticationConverter);
+ }
+
@Test
public void setResponseAuthenticationConverterWhenNullThenIllegalArgument() {
// @formatter:off