|
|
|
@ -28,7 +28,6 @@ import java.util.LinkedHashMap; |
|
|
|
import java.util.List; |
|
|
|
import java.util.List; |
|
|
|
import java.util.Map; |
|
|
|
import java.util.Map; |
|
|
|
import java.util.Set; |
|
|
|
import java.util.Set; |
|
|
|
import java.util.function.Function; |
|
|
|
|
|
|
|
import javax.annotation.Nonnull; |
|
|
|
import javax.annotation.Nonnull; |
|
|
|
import javax.xml.namespace.QName; |
|
|
|
import javax.xml.namespace.QName; |
|
|
|
|
|
|
|
|
|
|
|
@ -185,8 +184,10 @@ public final class OpenSamlAuthenticationProvider implements AuthenticationProvi |
|
|
|
private GrantedAuthoritiesMapper authoritiesMapper = (a -> a); |
|
|
|
private GrantedAuthoritiesMapper authoritiesMapper = (a -> a); |
|
|
|
private Duration responseTimeValidationSkew = Duration.ofMinutes(5); |
|
|
|
private Duration responseTimeValidationSkew = Duration.ofMinutes(5); |
|
|
|
|
|
|
|
|
|
|
|
private Function<Saml2AuthenticationToken, Converter<Response, AbstractAuthenticationToken>> authenticationConverter = |
|
|
|
private Converter<ResponseToken, ? extends AbstractAuthenticationToken> responseAuthenticationConverter = |
|
|
|
token -> response -> { |
|
|
|
responseToken -> { |
|
|
|
|
|
|
|
Response response = responseToken.response; |
|
|
|
|
|
|
|
Saml2AuthenticationToken token = responseToken.token; |
|
|
|
Assertion assertion = CollectionUtils.firstElement(response.getAssertions()); |
|
|
|
Assertion assertion = CollectionUtils.firstElement(response.getAssertions()); |
|
|
|
String username = assertion.getSubject().getNameID().getValue(); |
|
|
|
String username = assertion.getSubject().getNameID().getValue(); |
|
|
|
Map<String, List<Object>> attributes = getAssertionAttributes(assertion); |
|
|
|
Map<String, List<Object>> attributes = getAssertionAttributes(assertion); |
|
|
|
@ -255,11 +256,42 @@ public final class OpenSamlAuthenticationProvider implements AuthenticationProvi |
|
|
|
this.assertionValidator = assertionValidator; |
|
|
|
this.assertionValidator = assertionValidator; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Set the {@link Converter} to use for converting a validated {@link Response} into |
|
|
|
|
|
|
|
* an {@link AbstractAuthenticationToken}. |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* You can delegate to the default behavior by calling {@link #createDefaultResponseAuthenticationConverter()} |
|
|
|
|
|
|
|
* like so: |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* <pre> |
|
|
|
|
|
|
|
* OpenSamlAuthenticationProvider provider = new OpenSamlAuthenticationProvider(); |
|
|
|
|
|
|
|
* Converter<ResponseToken, Saml2Authentication> authenticationConverter = |
|
|
|
|
|
|
|
* createDefaultResponseAuthenticationConverter(); |
|
|
|
|
|
|
|
* provider.setResponseAuthenticationConverter(responseToken -> { |
|
|
|
|
|
|
|
* Saml2Authentication authentication = authenticationConverter.convert(responseToken); |
|
|
|
|
|
|
|
* User user = myUserRepository.findByUsername(authentication.getName()); |
|
|
|
|
|
|
|
* return new MyAuthentication(authentication, user); |
|
|
|
|
|
|
|
* }); |
|
|
|
|
|
|
|
* </pre> |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* This method takes precedence over {@link #setAuthoritiesExtractor(Converter)} and |
|
|
|
|
|
|
|
* {@link #setAuthoritiesMapper(GrantedAuthoritiesMapper)}. |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @param responseAuthenticationConverter the {@link Converter} to use |
|
|
|
|
|
|
|
* @since 5.4 |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
public void setResponseAuthenticationConverter( |
|
|
|
|
|
|
|
Converter<ResponseToken, ? extends AbstractAuthenticationToken> responseAuthenticationConverter) { |
|
|
|
|
|
|
|
Assert.notNull(responseAuthenticationConverter, "responseAuthenticationConverter cannot be null"); |
|
|
|
|
|
|
|
this.responseAuthenticationConverter = responseAuthenticationConverter; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Sets the {@link Converter} used for extracting assertion attributes that |
|
|
|
* Sets the {@link Converter} used for extracting assertion attributes that |
|
|
|
* can be mapped to authorities. |
|
|
|
* can be mapped to authorities. |
|
|
|
* @param authoritiesExtractor the {@code Converter} used for mapping the |
|
|
|
* @param authoritiesExtractor the {@code Converter} used for mapping the |
|
|
|
* assertion attributes to authorities |
|
|
|
* assertion attributes to authorities |
|
|
|
|
|
|
|
* @deprecated Use {@link #setResponseAuthenticationConverter(Converter)} instead |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public void setAuthoritiesExtractor(Converter<Assertion, Collection<? extends GrantedAuthority>> authoritiesExtractor) { |
|
|
|
public void setAuthoritiesExtractor(Converter<Assertion, Collection<? extends GrantedAuthority>> authoritiesExtractor) { |
|
|
|
Assert.notNull(authoritiesExtractor, "authoritiesExtractor cannot be null"); |
|
|
|
Assert.notNull(authoritiesExtractor, "authoritiesExtractor cannot be null"); |
|
|
|
@ -271,6 +303,7 @@ public final class OpenSamlAuthenticationProvider implements AuthenticationProvi |
|
|
|
* to a new set of authorities which will be associated to the {@link Saml2Authentication}. |
|
|
|
* to a new set of authorities which will be associated to the {@link Saml2Authentication}. |
|
|
|
* Note: This implementation is only retrieving |
|
|
|
* Note: This implementation is only retrieving |
|
|
|
* @param authoritiesMapper the {@link GrantedAuthoritiesMapper} used for mapping the user's authorities |
|
|
|
* @param authoritiesMapper the {@link GrantedAuthoritiesMapper} used for mapping the user's authorities |
|
|
|
|
|
|
|
* @deprecated Use {@link #setResponseAuthenticationConverter(Converter)} instead |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public void setAuthoritiesMapper(GrantedAuthoritiesMapper authoritiesMapper) { |
|
|
|
public void setAuthoritiesMapper(GrantedAuthoritiesMapper authoritiesMapper) { |
|
|
|
notNull(authoritiesMapper, "authoritiesMapper cannot be null"); |
|
|
|
notNull(authoritiesMapper, "authoritiesMapper cannot be null"); |
|
|
|
@ -286,6 +319,27 @@ public final class OpenSamlAuthenticationProvider implements AuthenticationProvi |
|
|
|
this.responseTimeValidationSkew = responseTimeValidationSkew; |
|
|
|
this.responseTimeValidationSkew = responseTimeValidationSkew; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Construct a default strategy for converting a SAML 2.0 Response and {@link Authentication} |
|
|
|
|
|
|
|
* token into a {@link Saml2Authentication} |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @return the default response authentication converter strategy |
|
|
|
|
|
|
|
* @since 5.4 |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
public static Converter<ResponseToken, Saml2Authentication> |
|
|
|
|
|
|
|
createDefaultResponseAuthenticationConverter() { |
|
|
|
|
|
|
|
return responseToken -> { |
|
|
|
|
|
|
|
Saml2AuthenticationToken token = responseToken.token; |
|
|
|
|
|
|
|
Response response = responseToken.response; |
|
|
|
|
|
|
|
Assertion assertion = CollectionUtils.firstElement(response.getAssertions()); |
|
|
|
|
|
|
|
String username = assertion.getSubject().getNameID().getValue(); |
|
|
|
|
|
|
|
Map<String, List<Object>> attributes = getAssertionAttributes(assertion); |
|
|
|
|
|
|
|
return new Saml2Authentication( |
|
|
|
|
|
|
|
new DefaultSaml2AuthenticatedPrincipal(username, attributes), token.getSaml2Response(), |
|
|
|
|
|
|
|
Collections.singleton(new SimpleGrantedAuthority("ROLE_USER"))); |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* @param authentication the authentication request object, must be of type |
|
|
|
* @param authentication the authentication request object, must be of type |
|
|
|
* {@link Saml2AuthenticationToken} |
|
|
|
* {@link Saml2AuthenticationToken} |
|
|
|
@ -300,7 +354,7 @@ public final class OpenSamlAuthenticationProvider implements AuthenticationProvi |
|
|
|
String serializedResponse = token.getSaml2Response(); |
|
|
|
String serializedResponse = token.getSaml2Response(); |
|
|
|
Response response = parse(serializedResponse); |
|
|
|
Response response = parse(serializedResponse); |
|
|
|
process(token, response); |
|
|
|
process(token, response); |
|
|
|
return this.authenticationConverter.apply(token).convert(response); |
|
|
|
return this.responseAuthenticationConverter.convert(new ResponseToken(response, token)); |
|
|
|
} catch (Saml2AuthenticationException e) { |
|
|
|
} catch (Saml2AuthenticationException e) { |
|
|
|
throw e; |
|
|
|
throw e; |
|
|
|
} catch (Exception e) { |
|
|
|
} catch (Exception e) { |
|
|
|
@ -496,7 +550,7 @@ public final class OpenSamlAuthenticationProvider implements AuthenticationProvi |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private Map<String, List<Object>> getAssertionAttributes(Assertion assertion) { |
|
|
|
private static Map<String, List<Object>> getAssertionAttributes(Assertion assertion) { |
|
|
|
Map<String, List<Object>> attributeMap = new LinkedHashMap<>(); |
|
|
|
Map<String, List<Object>> attributeMap = new LinkedHashMap<>(); |
|
|
|
for (AttributeStatement attributeStatement : assertion.getAttributeStatements()) { |
|
|
|
for (AttributeStatement attributeStatement : assertion.getAttributeStatements()) { |
|
|
|
for (Attribute attribute : attributeStatement.getAttributes()) { |
|
|
|
for (Attribute attribute : attributeStatement.getAttributes()) { |
|
|
|
@ -515,7 +569,7 @@ public final class OpenSamlAuthenticationProvider implements AuthenticationProvi |
|
|
|
return attributeMap; |
|
|
|
return attributeMap; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private Object getXmlObjectValue(XMLObject xmlObject) { |
|
|
|
private static Object getXmlObjectValue(XMLObject xmlObject) { |
|
|
|
if (xmlObject instanceof XSAny) { |
|
|
|
if (xmlObject instanceof XSAny) { |
|
|
|
return ((XSAny) xmlObject).getTextContent(); |
|
|
|
return ((XSAny) xmlObject).getTextContent(); |
|
|
|
} |
|
|
|
} |
|
|
|
@ -706,6 +760,29 @@ public final class OpenSamlAuthenticationProvider implements AuthenticationProvi |
|
|
|
return new Saml2AuthenticationException(validationError(code, description), cause); |
|
|
|
return new Saml2AuthenticationException(validationError(code, description), cause); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* A tuple containing an OpenSAML {@link Response} and its associated authentication token. |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @since 5.4 |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
public static class ResponseToken { |
|
|
|
|
|
|
|
private final Saml2AuthenticationToken token; |
|
|
|
|
|
|
|
private final Response response; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ResponseToken(Response response, Saml2AuthenticationToken token) { |
|
|
|
|
|
|
|
this.token = token; |
|
|
|
|
|
|
|
this.response = response; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public Response getResponse() { |
|
|
|
|
|
|
|
return this.response; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public Saml2AuthenticationToken getToken() { |
|
|
|
|
|
|
|
return this.token; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* A tuple containing an OpenSAML {@link Assertion} and its associated authentication token. |
|
|
|
* A tuple containing an OpenSAML {@link Assertion} and its associated authentication token. |
|
|
|
* |
|
|
|
* |
|
|
|
|