1
0
mirror of synced 2026-08-05 17:57:15 +00:00

Reformat code using spring-javaformat

Run `./gradlew format` to reformat all java files.

Issue gh-8945
This commit is contained in:
Phillip Webb
2020-08-10 16:39:17 -05:00
committed by Rob Winch
parent 81d9c6cac5
commit b7fc18262d
2487 changed files with 41506 additions and 46548 deletions
@@ -36,17 +36,21 @@ import static java.lang.Boolean.TRUE;
import static org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport.setParserPool;
/**
* An initialization service for initializing OpenSAML. Each Spring Security OpenSAML-based component invokes
* the {@link #initialize()} method at static initialization time.
* An initialization service for initializing OpenSAML. Each Spring Security
* OpenSAML-based component invokes the {@link #initialize()} method at static
* initialization time.
*
* {@link #initialize()} is idempotent and may be safely called in custom classes that need OpenSAML to be
* initialized in order to function correctly. It's recommended that you call this {@link #initialize()} method
* when using Spring Security and OpenSAML instead of OpenSAML's {@link InitializationService#initialize()}.
* {@link #initialize()} is idempotent and may be safely called in custom classes that
* need OpenSAML to be initialized in order to function correctly. It's recommended that
* you call this {@link #initialize()} method when using Spring Security and OpenSAML
* instead of OpenSAML's {@link InitializationService#initialize()}.
*
* The primary purpose of {@link #initialize()} is to prepare OpenSAML's {@link XMLObjectProviderRegistry}
* with some reasonable defaults. Any changes that Spring Security makes to the registry happen in this method.
* The primary purpose of {@link #initialize()} is to prepare OpenSAML's
* {@link XMLObjectProviderRegistry} with some reasonable defaults. Any changes that
* Spring Security makes to the registry happen in this method.
*
* To override those defaults, call {@link #requireInitialize(Consumer)} and change the registry:
* To override those defaults, call {@link #requireInitialize(Consumer)} and change the
* registry:
*
* <pre>
* static {
@@ -59,45 +63,50 @@ import static org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport.setP
*
* {@link #requireInitialize(Consumer)} may only be called once per application.
*
* If the application already initialized OpenSAML before {@link #requireInitialize(Consumer)} was called,
* then the configuration changes will not be applied and an exception will be thrown. The reason for this is to
* alert you to the fact that there are likely some initialization ordering problems in your application that
* would otherwise lead to an unpredictable state.
* If the application already initialized OpenSAML before
* {@link #requireInitialize(Consumer)} was called, then the configuration changes will
* not be applied and an exception will be thrown. The reason for this is to alert you to
* the fact that there are likely some initialization ordering problems in your
* application that would otherwise lead to an unpredictable state.
*
* If you must change the registry's configuration in multiple places in your application, you are expected
* to handle the initialization ordering issues yourself instead of trying to call {@link #requireInitialize(Consumer)}
* multiple times.
* If you must change the registry's configuration in multiple places in your application,
* you are expected to handle the initialization ordering issues yourself instead of
* trying to call {@link #requireInitialize(Consumer)} multiple times.
*
* @author Josh Cummings
* @since 5.4
*/
public class OpenSamlInitializationService {
private static final Log log = LogFactory.getLog(OpenSamlInitializationService.class);
private static final AtomicBoolean initialized = new AtomicBoolean(false);
/**
* Ready OpenSAML for use and configure it with reasonable defaults.
*
* Initialization is guaranteed to happen only once per application. This method will passively return
* {@code false} if initialization already took place earlier in the application.
*
* @return whether or not initialization was performed. The first thread to initialize OpenSAML will
* return {@code true} while the rest will return {@code false}.
* Initialization is guaranteed to happen only once per application. This method will
* passively return {@code false} if initialization already took place earlier in the
* application.
* @return whether or not initialization was performed. The first thread to initialize
* OpenSAML will return {@code true} while the rest will return {@code false}.
* @throws Saml2Exception if OpenSAML failed to initialize
*/
public static boolean initialize() {
return initialize(registry -> {});
return initialize(registry -> {
});
}
/**
* Ready OpenSAML for use, configure it with reasonable defaults, and modify the {@link XMLObjectProviderRegistry}
* using the provided {@link Consumer}.
* Ready OpenSAML for use, configure it with reasonable defaults, and modify the
* {@link XMLObjectProviderRegistry} using the provided {@link Consumer}.
*
* Initialization is guaranteed to happen only once per application. This method will throw an exception
* if initialization already took place earlier in the application.
*
* @param registryConsumer the {@link Consumer} to further configure the {@link XMLObjectProviderRegistry}
* @throws Saml2Exception if initialization already happened previously or if OpenSAML failed to initialize
* Initialization is guaranteed to happen only once per application. This method will
* throw an exception if initialization already took place earlier in the application.
* @param registryConsumer the {@link Consumer} to further configure the
* {@link XMLObjectProviderRegistry}
* @throws Saml2Exception if initialization already happened previously or if OpenSAML
* failed to initialize
*/
public static void requireInitialize(Consumer<XMLObjectProviderRegistry> registryConsumer) {
if (!initialize(registryConsumer)) {
@@ -111,7 +120,8 @@ public class OpenSamlInitializationService {
try {
InitializationService.initialize();
} catch (Exception e) {
}
catch (Exception e) {
throw new Saml2Exception(e);
}
@@ -129,7 +139,8 @@ public class OpenSamlInitializationService {
try {
parserPool.initialize();
} catch (Exception e) {
}
catch (Exception e) {
throw new Saml2Exception(e);
}
setParserPool(parserPool);
@@ -138,9 +149,11 @@ public class OpenSamlInitializationService {
log.debug("Initialized OpenSAML");
return true;
} else {
}
else {
log.debug("Refused to re-initialize OpenSAML");
return false;
}
}
}
@@ -25,21 +25,23 @@ import org.springframework.util.Assert;
* A representation of an SAML 2.0 Error.
*
* <p>
* At a minimum, an error response will contain an error code.
* The commonly used error code are defined in this class
* or a new codes can be defined in the future as arbitrary strings.
* At a minimum, an error response will contain an error code. The commonly used error
* code are defined in this class or a new codes can be defined in the future as arbitrary
* strings.
* </p>
*
* @since 5.2
*/
public class Saml2Error implements Serializable {
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
private final String errorCode;
private final String description;
/**
* Constructs a {@code Saml2Error} using the provided parameters.
*
* @param errorCode the error code
* @param description the error description
*/
@@ -51,7 +53,6 @@ public class Saml2Error implements Serializable {
/**
* Returns the error code.
*
* @return the error code
*/
public final String getErrorCode() {
@@ -60,7 +61,6 @@ public class Saml2Error implements Serializable {
/**
* Returns the error description.
*
* @return the error description
*/
public final String getDescription() {
@@ -69,7 +69,7 @@ public class Saml2Error implements Serializable {
@Override
public String toString() {
return "[" + this.getErrorCode() + "] " +
(this.getDescription() != null ? this.getDescription() : "");
return "[" + this.getErrorCode() + "] " + (this.getDescription() != null ? this.getDescription() : "");
}
}
@@ -22,80 +22,84 @@ package org.springframework.security.saml2.core;
* @since 5.2
*/
public interface Saml2ErrorCodes {
/**
* SAML Data does not represent a SAML 2 Response object.
* A valid XML object was received, but that object was not a
* SAML 2 Response object of type {@code ResponseType} per specification
* SAML Data does not represent a SAML 2 Response object. A valid XML object was
* received, but that object was not a SAML 2 Response object of type
* {@code ResponseType} per specification
* https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf#page=46
*/
String UNKNOWN_RESPONSE_CLASS = "unknown_response_class";
/**
* The response data is malformed or incomplete.
* An invalid XML object was received, and XML unmarshalling failed.
* The response data is malformed or incomplete. An invalid XML object was received,
* and XML unmarshalling failed.
*/
String MALFORMED_RESPONSE_DATA = "malformed_response_data";
/**
* Response destination does not match the request URL.
* A SAML 2 response object was received at a URL that
* did not match the URL stored in the {code Destination} attribute
* in the Response object.
* Response destination does not match the request URL. A SAML 2 response object was
* received at a URL that did not match the URL stored in the {code Destination}
* attribute in the Response object.
* https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf#page=38
*/
String INVALID_DESTINATION = "invalid_destination";
/**
* The assertion was not valid.
* The assertion used for authentication failed validation.
* Details around the failure will be present in the error description.
* The assertion was not valid. The assertion used for authentication failed
* validation. Details around the failure will be present in the error description.
*/
String INVALID_ASSERTION = "invalid_assertion";
/**
* The signature of response or assertion was invalid.
* Either the response or the assertion was missing a signature
* or the signature could not be verified using the system's
* configured credentials. Most commonly the IDP's
* X509 certificate.
* The signature of response or assertion was invalid. Either the response or the
* assertion was missing a signature or the signature could not be verified using the
* system's configured credentials. Most commonly the IDP's X509 certificate.
*/
String INVALID_SIGNATURE = "invalid_signature";
/**
* The assertion did not contain a subject element.
* The subject element, type SubjectType, contains
* a {@code NameID} or an {@code EncryptedID} that is used
* to assign the authenticated principal an identifier,
* typically a username.
* The assertion did not contain a subject element. The subject element, type
* SubjectType, contains a {@code NameID} or an {@code EncryptedID} that is used to
* assign the authenticated principal an identifier, typically a username.
*
* https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf#page=18
*/
String SUBJECT_NOT_FOUND = "subject_not_found";
/**
* The subject did not contain a user identifier
* The assertion contained a subject element, but the subject
* element did not have a {@code NameID} or {@code EncryptedID}
* element
* The subject did not contain a user identifier The assertion contained a subject
* element, but the subject element did not have a {@code NameID} or
* {@code EncryptedID} element
*
* https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf#page=18
*/
String USERNAME_NOT_FOUND = "username_not_found";
/**
* The system failed to decrypt an assertion or a name identifier.
* This error code will be thrown if the decryption of either a
* {@code EncryptedAssertion} or {@code EncryptedID} fails.
* The system failed to decrypt an assertion or a name identifier. This error code
* will be thrown if the decryption of either a {@code EncryptedAssertion} or
* {@code EncryptedID} fails.
* https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf#page=17
*/
String DECRYPTION_ERROR = "decryption_error";
/**
* An Issuer element contained a value that didn't
* https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf#page=15
*/
String INVALID_ISSUER = "invalid_issuer";
/**
* An error happened during validation.
* Used when internal, non classified, errors are caught during the
* authentication process.
* An error happened during validation. Used when internal, non classified, errors are
* caught during the authentication process.
*/
String INTERNAL_VALIDATION_ERROR = "internal_validation_error";
/**
* The relying party registration was not found.
* The registration ID did not correspond to any relying party registration.
* The relying party registration was not found. The registration ID did not
* correspond to any relying party registration.
*/
String RELYING_PARTY_REGISTRATION_NOT_FOUND = "relying_party_registration_not_found";
}
@@ -29,34 +29,35 @@ import static org.springframework.util.Assert.notNull;
import static org.springframework.util.Assert.state;
/**
* An object for holding a public certificate, any associated private key, and its intended
* <a href="https://www.oasis-open.org/committees/download.php/8958/sstc-saml-implementation-guidelines-draft-01.pdf">
* usages
* </a>
* (Line 584, Section 4.3 Credentials).
* An object for holding a public certificate, any associated private key, and its
* intended <a href=
* "https://www.oasis-open.org/committees/download.php/8958/sstc-saml-implementation-guidelines-draft-01.pdf">
* usages </a> (Line 584, Section 4.3 Credentials).
*
* @since 5.4
* @author Filip Hanik
* @author Josh Cummings
*/
public final class Saml2X509Credential {
public enum Saml2X509CredentialType {
VERIFICATION,
ENCRYPTION,
SIGNING,
DECRYPTION,
VERIFICATION, ENCRYPTION, SIGNING, DECRYPTION,
}
private final PrivateKey privateKey;
private final X509Certificate certificate;
private final Set<Saml2X509CredentialType> credentialTypes;
/**
* Creates a {@link Saml2X509Credential} using the provided parameters
*
* @param certificate the credential's public certificiate
* @param types the credential's intended usages, must be one of {@link Saml2X509CredentialType#VERIFICATION} or
* {@link Saml2X509CredentialType#ENCRYPTION} or both.
* @param types the credential's intended usages, must be one of
* {@link Saml2X509CredentialType#VERIFICATION} or
* {@link Saml2X509CredentialType#ENCRYPTION} or both.
*/
public Saml2X509Credential(X509Certificate certificate, Saml2X509CredentialType... types) {
this(null, false, certificate, types);
@@ -65,11 +66,11 @@ public final class Saml2X509Credential {
/**
* Creates a {@link Saml2X509Credential} using the provided parameters
*
* @param privateKey the credential's private key
* @param certificate the credential's public certificate
* @param types the credential's intended usages, must be one of {@link Saml2X509CredentialType#SIGNING} or
* {@link Saml2X509CredentialType#DECRYPTION} or both.
* @param types the credential's intended usages, must be one of
* {@link Saml2X509CredentialType#SIGNING} or
* {@link Saml2X509CredentialType#DECRYPTION} or both.
*/
public Saml2X509Credential(PrivateKey privateKey, X509Certificate certificate, Saml2X509CredentialType... types) {
this(privateKey, true, certificate, types);
@@ -78,7 +79,6 @@ public final class Saml2X509Credential {
/**
* Creates a {@link Saml2X509Credential} using the provided parameters
*
* @param privateKey the credential's private key
* @param certificate the credential's public certificate
* @param types the credential's intended usages
@@ -125,10 +125,7 @@ public final class Saml2X509Credential {
return new Saml2X509Credential(privateKey, certificate, Saml2X509Credential.Saml2X509CredentialType.SIGNING);
}
private Saml2X509Credential(
PrivateKey privateKey,
boolean keyRequired,
X509Certificate certificate,
private Saml2X509Credential(PrivateKey privateKey, boolean keyRequired, X509Certificate certificate,
Saml2X509CredentialType... types) {
notNull(certificate, "certificate cannot be null");
notEmpty(types, "credentials types cannot be empty");
@@ -142,7 +139,6 @@ public final class Saml2X509Credential {
/**
* Get the private key for this credential
*
* @return the private key, may be null
* @see {@link #Saml2X509Credential(PrivateKey, X509Certificate, Saml2X509CredentialType...)}
*/
@@ -152,7 +148,6 @@ public final class Saml2X509Credential {
/**
* Get the public certificate for this credential
*
* @return the public certificate
*/
public X509Certificate getCertificate() {
@@ -161,7 +156,6 @@ public final class Saml2X509Credential {
/**
* Indicate whether this credential can be used for signing
*
* @return true if the credential has a {@link Saml2X509CredentialType#SIGNING} type
*/
public boolean isSigningCredential() {
@@ -170,8 +164,8 @@ public final class Saml2X509Credential {
/**
* Indicate whether this credential can be used for decryption
*
* @return true if the credential has a {@link Saml2X509CredentialType#DECRYPTION} type
* @return true if the credential has a {@link Saml2X509CredentialType#DECRYPTION}
* type
*/
public boolean isDecryptionCredential() {
return getCredentialTypes().contains(Saml2X509CredentialType.DECRYPTION);
@@ -179,8 +173,8 @@ public final class Saml2X509Credential {
/**
* Indicate whether this credential can be used for verification
*
* @return true if the credential has a {@link Saml2X509CredentialType#VERIFICATION} type
* @return true if the credential has a {@link Saml2X509CredentialType#VERIFICATION}
* type
*/
public boolean isVerificationCredential() {
return getCredentialTypes().contains(Saml2X509CredentialType.VERIFICATION);
@@ -188,8 +182,8 @@ public final class Saml2X509Credential {
/**
* Indicate whether this credential can be used for encryption
*
* @return true if the credential has a {@link Saml2X509CredentialType#ENCRYPTION} type
* @return true if the credential has a {@link Saml2X509CredentialType#ENCRYPTION}
* type
*/
public boolean isEncryptionCredential() {
return getCredentialTypes().contains(Saml2X509CredentialType.ENCRYPTION);
@@ -197,7 +191,6 @@ public final class Saml2X509Credential {
/**
* List all this credential's intended usages
*
* @return the set of this credential's intended usages
*/
public Set<Saml2X509CredentialType> getCredentialTypes() {
@@ -206,12 +199,13 @@ public final class Saml2X509Credential {
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
Saml2X509Credential that = (Saml2X509Credential) o;
return Objects.equals(this.privateKey, that.privateKey) &&
this.certificate.equals(that.certificate) &&
this.credentialTypes.equals(that.credentialTypes);
return Objects.equals(this.privateKey, that.privateKey) && this.certificate.equals(that.certificate)
&& this.credentialTypes.equals(that.credentialTypes);
}
@Override
@@ -228,7 +222,8 @@ public final class Saml2X509Credential {
break;
}
}
state(valid, () -> usage +" is not a valid usage for this credential");
state(valid, () -> usage + " is not a valid usage for this credential");
}
}
}
@@ -32,34 +32,41 @@ import static org.springframework.util.Assert.state;
* Saml2X509Credential is meant to hold an X509 certificate, or an X509 certificate and a
* private key. Per:
* https://www.oasis-open.org/committees/download.php/8958/sstc-saml-implementation-guidelines-draft-01.pdf
* Line: 584, Section 4.3 Credentials Used for both signing, signature verification and encryption/decryption
* Line: 584, Section 4.3 Credentials Used for both signing, signature verification and
* encryption/decryption
*
* @since 5.2
* @deprecated Use {@link org.springframework.security.saml2.core.Saml2X509Credential} instead
* @deprecated Use {@link org.springframework.security.saml2.core.Saml2X509Credential}
* instead
*/
@Deprecated
public class Saml2X509Credential {
/**
* @deprecated Use {@link org.springframework.security.saml2.core.Saml2X509Credential.Saml2X509CredentialType} instead
* @deprecated Use
* {@link org.springframework.security.saml2.core.Saml2X509Credential.Saml2X509CredentialType}
* instead
*/
@Deprecated
public enum Saml2X509CredentialType {
VERIFICATION,
ENCRYPTION,
SIGNING,
DECRYPTION,
VERIFICATION, ENCRYPTION, SIGNING, DECRYPTION,
}
private final PrivateKey privateKey;
private final X509Certificate certificate;
private final Set<Saml2X509CredentialType> credentialTypes;
/**
* Creates a Saml2X509Credentials representing Identity Provider credentials for
* verification, encryption or both.
* @param certificate an IDP X509Certificate, cannot be null
* @param types credential types, must be one of {@link Saml2X509CredentialType#VERIFICATION} or
* {@link Saml2X509CredentialType#ENCRYPTION} or both.
* @param types credential types, must be one of
* {@link Saml2X509CredentialType#VERIFICATION} or
* {@link Saml2X509CredentialType#ENCRYPTION} or both.
*/
public Saml2X509Credential(X509Certificate certificate, Saml2X509CredentialType... types) {
this(null, false, certificate, types);
@@ -70,9 +77,11 @@ public class Saml2X509Credential {
* Creates a Saml2X509Credentials representing Service Provider credentials for
* signing, decryption or both.
* @param privateKey a private key used for signing or decryption, cannot be null
* @param certificate an SP X509Certificate shared with identity providers, cannot be null
* @param types credential types, must be one of {@link Saml2X509CredentialType#SIGNING} or
* {@link Saml2X509CredentialType#DECRYPTION} or both.
* @param certificate an SP X509Certificate shared with identity providers, cannot be
* null
* @param types credential types, must be one of
* {@link Saml2X509CredentialType#SIGNING} or
* {@link Saml2X509CredentialType#DECRYPTION} or both.
*/
public Saml2X509Credential(PrivateKey privateKey, X509Certificate certificate, Saml2X509CredentialType... types) {
this(privateKey, true, certificate, types);
@@ -87,10 +96,7 @@ public class Saml2X509Credential {
this.credentialTypes = types;
}
private Saml2X509Credential(
PrivateKey privateKey,
boolean keyRequired,
X509Certificate certificate,
private Saml2X509Credential(PrivateKey privateKey, boolean keyRequired, X509Certificate certificate,
Saml2X509CredentialType... types) {
notNull(certificate, "certificate cannot be null");
notEmpty(types, "credentials types cannot be empty");
@@ -102,10 +108,9 @@ public class Saml2X509Credential {
this.credentialTypes = new LinkedHashSet<>(asList(types));
}
/**
* Returns true if the credential has a private key and can be used for signing, the types will contain
* {@link Saml2X509CredentialType#SIGNING}.
* Returns true if the credential has a private key and can be used for signing, the
* types will contain {@link Saml2X509CredentialType#SIGNING}.
* @return true if the credential is a {@link Saml2X509CredentialType#SIGNING} type
*/
public boolean isSigningCredential() {
@@ -113,8 +118,8 @@ public class Saml2X509Credential {
}
/**
* Returns true if the credential has a private key and can be used for decryption, the types will contain
* {@link Saml2X509CredentialType#DECRYPTION}.
* Returns true if the credential has a private key and can be used for decryption,
* the types will contain {@link Saml2X509CredentialType#DECRYPTION}.
* @return true if the credential is a {@link Saml2X509CredentialType#DECRYPTION} type
*/
public boolean isDecryptionCredential() {
@@ -122,18 +127,20 @@ public class Saml2X509Credential {
}
/**
* Returns true if the credential has a certificate and can be used for signature verification, the types will contain
* {@link Saml2X509CredentialType#VERIFICATION}.
* @return true if the credential is a {@link Saml2X509CredentialType#VERIFICATION} type
* Returns true if the credential has a certificate and can be used for signature
* verification, the types will contain {@link Saml2X509CredentialType#VERIFICATION}.
* @return true if the credential is a {@link Saml2X509CredentialType#VERIFICATION}
* type
*/
public boolean isSignatureVerficationCredential() {
return getCredentialTypes().contains(Saml2X509CredentialType.VERIFICATION);
}
/**
* Returns true if the credential has a certificate and can be used for signature verification, the types will contain
* {@link Saml2X509CredentialType#VERIFICATION}.
* @return true if the credential is a {@link Saml2X509CredentialType#VERIFICATION} type
* Returns true if the credential has a certificate and can be used for signature
* verification, the types will contain {@link Saml2X509CredentialType#VERIFICATION}.
* @return true if the credential is a {@link Saml2X509CredentialType#VERIFICATION}
* type
*/
public boolean isEncryptionCredential() {
return getCredentialTypes().contains(Saml2X509CredentialType.ENCRYPTION);
@@ -166,12 +173,13 @@ public class Saml2X509Credential {
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
Saml2X509Credential that = (Saml2X509Credential) o;
return Objects.equals(this.privateKey, that.privateKey) &&
this.certificate.equals(that.certificate) &&
this.credentialTypes.equals(that.credentialTypes);
return Objects.equals(this.privateKey, that.privateKey) && this.certificate.equals(that.certificate)
&& this.credentialTypes.equals(that.credentialTypes);
}
@Override
@@ -188,7 +196,8 @@ public class Saml2X509Credential {
break;
}
}
state(valid, () -> usage +" is not a valid usage for this credential");
state(valid, () -> usage + " is not a valid usage for this credential");
}
}
}
@@ -23,11 +23,12 @@ import java.nio.charset.Charset;
/**
* Data holder for {@code AuthNRequest} parameters to be sent using either the
* {@link Saml2MessageBinding#POST} or {@link Saml2MessageBinding#REDIRECT} binding.
* Data will be encoded and possibly deflated, but will not be escaped for transport,
* ie URL encoded, {@link org.springframework.web.util.UriUtils#encode(String, Charset)}
* or HTML encoded, {@link org.springframework.web.util.HtmlUtils#htmlEscape(String)}.
* https://www.oasis-open.org/committees/download.php/35711/sstc-saml-core-errata-2.0-wd-06-diff.pdf (line 2031)
* {@link Saml2MessageBinding#POST} or {@link Saml2MessageBinding#REDIRECT} binding. Data
* will be encoded and possibly deflated, but will not be escaped for transport, ie URL
* encoded, {@link org.springframework.web.util.UriUtils#encode(String, Charset)} or HTML
* encoded, {@link org.springframework.web.util.HtmlUtils#htmlEscape(String)}.
* https://www.oasis-open.org/committees/download.php/35711/sstc-saml-core-errata-2.0-wd-06-diff.pdf
* (line 2031)
*
* @see Saml2AuthenticationRequestFactory#createPostAuthenticationRequest(Saml2AuthenticationRequestContext)
* @see Saml2AuthenticationRequestFactory#createRedirectAuthenticationRequest(Saml2AuthenticationRequestContext)
@@ -36,19 +37,20 @@ import java.nio.charset.Charset;
abstract class AbstractSaml2AuthenticationRequest {
private final String samlRequest;
private final String relayState;
private final String authenticationRequestUri;
/**
* Mandatory constructor for the {@link AbstractSaml2AuthenticationRequest}
* @param samlRequest - the SAMLRequest XML data, SAML encoded, cannot be empty or null
* @param samlRequest - the SAMLRequest XML data, SAML encoded, cannot be empty or
* null
* @param relayState - RelayState value that accompanies the request, may be null
* @param authenticationRequestUri - The authenticationRequestUri, a URL, where to send the XML message, cannot be empty or null
* @param authenticationRequestUri - The authenticationRequestUri, a URL, where to
* send the XML message, cannot be empty or null
*/
AbstractSaml2AuthenticationRequest(
String samlRequest,
String relayState,
String authenticationRequestUri) {
AbstractSaml2AuthenticationRequest(String samlRequest, String relayState, String authenticationRequestUri) {
Assert.hasText(samlRequest, "samlRequest cannot be null or empty");
Assert.hasText(authenticationRequestUri, "authenticationRequestUri cannot be null or empty");
this.authenticationRequestUri = authenticationRequestUri;
@@ -57,9 +59,10 @@ abstract class AbstractSaml2AuthenticationRequest {
}
/**
* Returns the AuthNRequest XML value to be sent. This value is already encoded for transport.
* If {@link #getBinding()} is {@link Saml2MessageBinding#REDIRECT} the value is deflated and SAML encoded.
* If {@link #getBinding()} is {@link Saml2MessageBinding#POST} the value is SAML encoded.
* Returns the AuthNRequest XML value to be sent. This value is already encoded for
* transport. If {@link #getBinding()} is {@link Saml2MessageBinding#REDIRECT} the
* value is deflated and SAML encoded. If {@link #getBinding()} is
* {@link Saml2MessageBinding#POST} the value is SAML encoded.
* @return the SAMLRequest parameter value
*/
public String getSamlRequest() {
@@ -83,8 +86,9 @@ abstract class AbstractSaml2AuthenticationRequest {
}
/**
* Returns the binding this AuthNRequest will be sent and
* encoded with. If {@link Saml2MessageBinding#REDIRECT} is used, the DEFLATE encoding will be automatically applied.
* Returns the binding this AuthNRequest will be sent and encoded with. If
* {@link Saml2MessageBinding#REDIRECT} is used, the DEFLATE encoding will be
* automatically applied.
* @return the binding this message will be sent with.
*/
public abstract Saml2MessageBinding getBinding();
@@ -93,8 +97,11 @@ abstract class AbstractSaml2AuthenticationRequest {
* A builder for {@link AbstractSaml2AuthenticationRequest} and its subclasses.
*/
static class Builder<T extends Builder<T>> {
String authenticationRequestUri;
String samlRequest;
String relayState;
protected Builder() {
@@ -109,12 +116,10 @@ abstract class AbstractSaml2AuthenticationRequest {
return (T) this;
}
/**
* Sets the {@code RelayState} parameter that will accompany this AuthNRequest
*
* @param relayState the relay state value, unencoded. if null or empty, the parameter will be removed from the
* map.
* @param relayState the relay state value, unencoded. if null or empty, the
* parameter will be removed from the map.
* @return this object
*/
public T relayState(String relayState) {
@@ -124,7 +129,6 @@ abstract class AbstractSaml2AuthenticationRequest {
/**
* Sets the {@code SAMLRequest} parameter that will accompany this AuthNRequest
*
* @param samlRequest the SAMLRequest parameter.
* @return this object
*/
@@ -134,8 +138,8 @@ abstract class AbstractSaml2AuthenticationRequest {
}
/**
* Sets the {@code authenticationRequestUri}, a URL that will receive the AuthNRequest message
*
* Sets the {@code authenticationRequestUri}, a URL that will receive the
* AuthNRequest message
* @param authenticationRequestUri the relay state value, unencoded.
* @return this object
*/
@@ -143,6 +147,7 @@ abstract class AbstractSaml2AuthenticationRequest {
this.authenticationRequestUri = authenticationRequestUri;
return _this();
}
}
}
@@ -31,6 +31,7 @@ import java.util.Map;
public class DefaultSaml2AuthenticatedPrincipal implements Saml2AuthenticatedPrincipal, Serializable {
private final String name;
private final Map<String, List<Object>> attributes;
public DefaultSaml2AuthenticatedPrincipal(String name, Map<String, List<Object>> attributes) {
@@ -50,4 +51,5 @@ public class DefaultSaml2AuthenticatedPrincipal implements Saml2AuthenticatedPri
public Map<String, List<Object>> getAttributes() {
return this.attributes;
}
}
@@ -129,39 +129,48 @@ import static org.springframework.security.saml2.core.Saml2ErrorCodes.SUBJECT_NO
import static org.springframework.util.Assert.notNull;
/**
* Implementation of {@link AuthenticationProvider} for SAML authentications when receiving a
* {@code Response} object containing an {@code Assertion}. This implementation uses
* the {@code OpenSAML 3} library.
* Implementation of {@link AuthenticationProvider} for SAML authentications when
* receiving a {@code Response} object containing an {@code Assertion}. This
* implementation uses the {@code OpenSAML 3} library.
*
* <p>
* The {@link OpenSamlAuthenticationProvider} supports {@link Saml2AuthenticationToken} objects
* that contain a SAML response in its decoded XML format {@link Saml2AuthenticationToken#getSaml2Response()}
* along with the information about the asserting party, the identity provider (IDP), as well as
* the relying party, the service provider (SP, this application).
* The {@link OpenSamlAuthenticationProvider} supports {@link Saml2AuthenticationToken}
* objects that contain a SAML response in its decoded XML format
* {@link Saml2AuthenticationToken#getSaml2Response()} along with the information about
* the asserting party, the identity provider (IDP), as well as the relying party, the
* service provider (SP, this application).
* </p>
* <p>
* The {@link Saml2AuthenticationToken} will be processed into a SAML Response object.
* The SAML response object can be signed. If the Response is signed, a signature will not be required on the assertion.
* The {@link Saml2AuthenticationToken} will be processed into a SAML Response object. The
* SAML response object can be signed. If the Response is signed, a signature will not be
* required on the assertion.
* </p>
* <p>
* While a response object can contain a list of assertion, this provider will only leverage
* the first valid assertion for the purpose of authentication. Assertions that do not pass validation
* will be ignored. If no valid assertions are found a {@link Saml2AuthenticationException} is thrown.
* While a response object can contain a list of assertion, this provider will only
* leverage the first valid assertion for the purpose of authentication. Assertions that
* do not pass validation will be ignored. If no valid assertions are found a
* {@link Saml2AuthenticationException} is thrown.
* </p>
* <p>
* This provider supports two types of encrypted SAML elements
* <ul>
* <li><a href="https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf#page=17">EncryptedAssertion</a></li>
* <li><a href="https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf#page=14">EncryptedID</a></li>
* </ul>
* If the assertion is encrypted, then signature validation on the assertion is no longer required.
* This provider supports two types of encrypted SAML elements
* <ul>
* <li><a href=
* "https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf#page=17">EncryptedAssertion</a></li>
* <li><a href=
* "https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf#page=14">EncryptedID</a></li>
* </ul>
* If the assertion is encrypted, then signature validation on the assertion is no longer
* required.
* </p>
* <p>
* This provider does not perform an X509 certificate validation on the configured asserting party, IDP, verification
* certificates.
* This provider does not perform an X509 certificate validation on the configured
* asserting party, IDP, verification certificates.
* </p>
*
* @since 5.2
* @see <a href="https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf#page=38">SAML 2 StatusResponse</a>
* @see <a href=
* "https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf#page=38">SAML 2
* StatusResponse</a>
* @see <a href="https://wiki.shibboleth.net/confluence/display/OS30/Home">OpenSAML 3</a>
*/
public final class OpenSamlAuthenticationProvider implements AuthenticationProvider {
@@ -173,32 +182,35 @@ public final class OpenSamlAuthenticationProvider implements AuthenticationProvi
private static Log logger = LogFactory.getLog(OpenSamlAuthenticationProvider.class);
private final XMLObjectProviderRegistry registry;
private final ResponseUnmarshaller responseUnmarshaller;
private final ParserPool parserPool;
private Converter<Assertion, Collection<? extends GrantedAuthority>> authoritiesExtractor =
(a -> singletonList(new SimpleGrantedAuthority("ROLE_USER")));
private Converter<Assertion, Collection<? extends GrantedAuthority>> authoritiesExtractor = (a -> singletonList(
new SimpleGrantedAuthority("ROLE_USER")));
private GrantedAuthoritiesMapper authoritiesMapper = (a -> a);
private Duration responseTimeValidationSkew = Duration.ofMinutes(5);
private Function<Saml2AuthenticationToken, Converter<Response, AbstractAuthenticationToken>> authenticationConverter =
token -> 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(),
this.authoritiesMapper.mapAuthorities(getAssertionAuthorities(assertion)));
};
private Function<Saml2AuthenticationToken, Converter<Response, AbstractAuthenticationToken>> authenticationConverter = token -> 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(), this.authoritiesMapper.mapAuthorities(getAssertionAuthorities(assertion)));
};
private Converter<Saml2AuthenticationToken, SignatureTrustEngine> signatureTrustEngineConverter = new SignatureTrustEngineConverter();
private Converter<Tuple, SAML20AssertionValidator> assertionValidatorConverter = new SAML20AssertionValidatorConverter();
private Collection<ConditionValidator> conditionValidators = Collections
.singleton(new AudienceRestrictionConditionValidator());
private Converter<Tuple, ValidationContext> validationContextConverter = new ValidationContextConverter();
private Converter<Saml2AuthenticationToken, SignatureTrustEngine> signatureTrustEngineConverter =
new SignatureTrustEngineConverter();
private Converter<Tuple, SAML20AssertionValidator> assertionValidatorConverter =
new SAML20AssertionValidatorConverter();
private Collection<ConditionValidator> conditionValidators =
Collections.singleton(new AudienceRestrictionConditionValidator());
private Converter<Tuple, ValidationContext> validationContextConverter =
new ValidationContextConverter();
private Converter<Saml2AuthenticationToken, Decrypter> decrypterConverter = new DecrypterConverter();
/**
@@ -212,48 +224,47 @@ public final class OpenSamlAuthenticationProvider implements AuthenticationProvi
}
/**
* Set the the collection of {@link ConditionValidator}s used when validating an assertion.
*
* Set the the collection of {@link ConditionValidator}s used when validating an
* assertion.
* @param conditionValidators the collection of validators to use
* @since 5.4
*/
public void setConditionValidators(
Collection<ConditionValidator> conditionValidators) {
public void setConditionValidators(Collection<ConditionValidator> conditionValidators) {
Assert.notEmpty(conditionValidators, "conditionValidators cannot be empty");
this.conditionValidators = conditionValidators;
}
/**
* Set the strategy for retrieving the {@link ValidationContext} used when
* validating an assertion.
*
* Set the strategy for retrieving the {@link ValidationContext} used when validating
* an assertion.
* @param validationContextConverter the strategy to use
* @since 5.4
*/
public void setValidationContextConverter(
Converter<Tuple, ValidationContext> validationContextConverter) {
public void setValidationContextConverter(Converter<Tuple, ValidationContext> validationContextConverter) {
Assert.notNull(validationContextConverter, "validationContextConverter cannot be empty");
this.validationContextConverter = validationContextConverter;
}
/**
* Sets the {@link Converter} used for extracting assertion attributes that
* can be mapped to authorities.
* @param authoritiesExtractor the {@code Converter} used for mapping the
* assertion attributes to authorities
* Sets the {@link Converter} used for extracting assertion attributes that can be
* mapped to authorities.
* @param authoritiesExtractor the {@code Converter} used for mapping the assertion
* attributes to authorities
*/
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");
this.authoritiesExtractor = authoritiesExtractor;
}
/**
* Sets the {@link GrantedAuthoritiesMapper} used for mapping assertion attributes
* to a new set of authorities which will be associated to the {@link Saml2Authentication}.
* Note: This implementation is only retrieving
* @param authoritiesMapper the {@link GrantedAuthoritiesMapper} used for mapping the user's authorities
* Sets the {@link GrantedAuthoritiesMapper} used for mapping assertion attributes to
* a new set of authorities which will be associated to the
* {@link Saml2Authentication}. Note: This implementation is only retrieving
* @param authoritiesMapper the {@link GrantedAuthoritiesMapper} used for mapping the
* user's authorities
*/
public void setAuthoritiesMapper(GrantedAuthoritiesMapper authoritiesMapper) {
notNull(authoritiesMapper, "authoritiesMapper cannot be null");
@@ -271,8 +282,7 @@ public final class OpenSamlAuthenticationProvider implements AuthenticationProvi
/**
* @param authentication the authentication request object, must be of type
* {@link Saml2AuthenticationToken}
*
* {@link Saml2AuthenticationToken}
* @return {@link Saml2Authentication} if the assertion is valid
* @throws AuthenticationException if a validation exception occurs
*/
@@ -284,9 +294,11 @@ public final class OpenSamlAuthenticationProvider implements AuthenticationProvi
Response response = parse(serializedResponse);
process(token, response);
return this.authenticationConverter.apply(token).convert(response);
} catch (Saml2AuthenticationException e) {
}
catch (Saml2AuthenticationException e) {
throw e;
} catch (Exception e) {
}
catch (Exception e) {
throw authException(INTERNAL_VALIDATION_ERROR, e.getMessage(), e);
}
}
@@ -305,8 +317,8 @@ public final class OpenSamlAuthenticationProvider implements AuthenticationProvi
private Response parse(String response) throws Saml2Exception, Saml2AuthenticationException {
try {
Document document = this.parserPool.parse(new ByteArrayInputStream(
response.getBytes(StandardCharsets.UTF_8)));
Document document = this.parserPool
.parse(new ByteArrayInputStream(response.getBytes(StandardCharsets.UTF_8)));
Element element = document.getDocumentElement();
return (Response) this.responseUnmarshaller.unmarshall(element);
}
@@ -327,8 +339,8 @@ public final class OpenSamlAuthenticationProvider implements AuthenticationProvi
Decrypter decrypter = this.decrypterConverter.convert(token);
List<Assertion> assertions = decryptAssertions(decrypter, response);
if (!isSigned(responseSigned, assertions)) {
throw authException(INVALID_SIGNATURE, "Either the response or one of the assertions is unsigned. " +
"Please either sign the response or all of the assertions.");
throw authException(INVALID_SIGNATURE, "Either the response or one of the assertions is unsigned. "
+ "Please either sign the response or all of the assertions.");
}
validationExceptions.putAll(validateAssertions(token, response));
@@ -343,12 +355,15 @@ public final class OpenSamlAuthenticationProvider implements AuthenticationProvi
if (logger.isDebugEnabled()) {
logger.debug("Successfully processed SAML Response [" + response.getID() + "]");
}
} else {
}
else {
if (logger.isTraceEnabled()) {
logger.debug("Found " + validationExceptions.size() + " validation errors in SAML response [" + response.getID() + "]: " +
validationExceptions.values());
} else if (logger.isDebugEnabled()) {
logger.debug("Found " + validationExceptions.size() + " validation errors in SAML response [" + response.getID() + "]");
logger.debug("Found " + validationExceptions.size() + " validation errors in SAML response ["
+ response.getID() + "]: " + validationExceptions.values());
}
else if (logger.isDebugEnabled()) {
logger.debug("Found " + validationExceptions.size() + " validation errors in SAML response ["
+ response.getID() + "]");
}
}
@@ -357,8 +372,8 @@ public final class OpenSamlAuthenticationProvider implements AuthenticationProvi
}
}
private Map<String, Saml2AuthenticationException> validateResponse
(Saml2AuthenticationToken token, Response response) {
private Map<String, Saml2AuthenticationException> validateResponse(Saml2AuthenticationToken token,
Response response) {
Map<String, Saml2AuthenticationException> validationExceptions = new HashMap<>();
String issuer = response.getIssuer().getValue();
@@ -367,7 +382,8 @@ public final class OpenSamlAuthenticationProvider implements AuthenticationProvi
SAMLSignatureProfileValidator profileValidator = new SAMLSignatureProfileValidator();
try {
profileValidator.validate(response.getSignature());
} catch (Exception e) {
}
catch (Exception e) {
validationExceptions.put(INVALID_SIGNATURE, authException(INVALID_SIGNATURE,
"Invalid signature for SAML Response [" + response.getID() + "]: ", e));
}
@@ -375,13 +391,15 @@ public final class OpenSamlAuthenticationProvider implements AuthenticationProvi
try {
CriteriaSet criteriaSet = new CriteriaSet();
criteriaSet.add(new EvaluableEntityIDCredentialCriterion(new EntityIdCriterion(issuer)));
criteriaSet.add(new EvaluableProtocolRoleDescriptorCriterion(new ProtocolCriterion(SAMLConstants.SAML20P_NS)));
criteriaSet.add(
new EvaluableProtocolRoleDescriptorCriterion(new ProtocolCriterion(SAMLConstants.SAML20P_NS)));
criteriaSet.add(new EvaluableUsageCredentialCriterion(new UsageCriterion(UsageType.SIGNING)));
if (!this.signatureTrustEngineConverter.convert(token).validate(response.getSignature(), criteriaSet)) {
validationExceptions.put(INVALID_SIGNATURE, authException(INVALID_SIGNATURE,
"Invalid signature for SAML Response [" + response.getID() + "]"));
}
} catch (Exception e) {
}
catch (Exception e) {
validationExceptions.put(INVALID_SIGNATURE, authException(INVALID_SIGNATURE,
"Invalid signature for SAML Response [" + response.getID() + "]: ", e));
}
@@ -403,14 +421,14 @@ public final class OpenSamlAuthenticationProvider implements AuthenticationProvi
return validationExceptions;
}
private List<Assertion> decryptAssertions
(Decrypter decrypter, Response response) {
private List<Assertion> decryptAssertions(Decrypter decrypter, Response response) {
List<Assertion> assertions = new ArrayList<>();
for (EncryptedAssertion encryptedAssertion : response.getEncryptedAssertions()) {
try {
Assertion assertion = decrypter.decrypt(encryptedAssertion);
assertions.add(assertion);
} catch (DecryptionException e) {
}
catch (DecryptionException e) {
throw authException(DECRYPTION_ERROR, e.getMessage(), e);
}
}
@@ -418,8 +436,8 @@ public final class OpenSamlAuthenticationProvider implements AuthenticationProvi
return response.getAssertions();
}
private Map<String, Saml2AuthenticationException> validateAssertions
(Saml2AuthenticationToken token, Response response) {
private Map<String, Saml2AuthenticationException> validateAssertions(Saml2AuthenticationToken token,
Response response) {
List<Assertion> assertions = response.getAssertions();
if (assertions.isEmpty()) {
throw authException(MALFORMED_RESPONSE_DATA, "No assertions found in response.");
@@ -444,10 +462,10 @@ public final class OpenSamlAuthenticationProvider implements AuthenticationProvi
context.getValidationFailureMessage());
validationExceptions.put(INVALID_ASSERTION, authException(INVALID_ASSERTION, message));
}
} catch (Exception e) {
String message = String.format("Invalid assertion [%s] for SAML response [%s]: %s",
assertion.getID(), ((Response) assertion.getParent()).getID(),
e.getMessage());
}
catch (Exception e) {
String message = String.format("Invalid assertion [%s] for SAML response [%s]: %s", assertion.getID(),
((Response) assertion.getParent()).getID(), e.getMessage());
validationExceptions.put(INVALID_ASSERTION, authException(INVALID_ASSERTION, message, e));
}
}
@@ -480,7 +498,8 @@ public final class OpenSamlAuthenticationProvider implements AuthenticationProvi
NameID nameId = (NameID) decrypter.decrypt(assertion.getSubject().getEncryptedID());
assertion.getSubject().setNameID(nameId);
return nameId;
} catch (DecryptionException e) {
}
catch (DecryptionException e) {
throw authException(DECRYPTION_ERROR, e.getMessage(), e);
}
}
@@ -534,19 +553,22 @@ public final class OpenSamlAuthenticationProvider implements AuthenticationProvi
try {
Element element = marshaller.marshall(xsAny);
return SerializeSupport.nodeToString(element);
} catch (MarshallingException e) {
}
catch (MarshallingException e) {
throw new Saml2Exception(e);
}
}
return xsAny.getTextContent();
}
private static class SignatureTrustEngineConverter implements Converter<Saml2AuthenticationToken, SignatureTrustEngine> {
private static class SignatureTrustEngineConverter
implements Converter<Saml2AuthenticationToken, SignatureTrustEngine> {
@Override
public SignatureTrustEngine convert(Saml2AuthenticationToken token) {
Set<Credential> credentials = new HashSet<>();
Collection<Saml2X509Credential> keys = token.getRelyingPartyRegistration().getAssertingPartyDetails().getVerificationX509Credentials();
Collection<Saml2X509Credential> keys = token.getRelyingPartyRegistration().getAssertingPartyDetails()
.getVerificationX509Credentials();
for (Saml2X509Credential key : keys) {
BasicX509Credential cred = new BasicX509Credential(key.getCertificate());
cred.setUsageType(UsageType.SIGNING);
@@ -554,11 +576,10 @@ public final class OpenSamlAuthenticationProvider implements AuthenticationProvi
credentials.add(cred);
}
CredentialResolver credentialsResolver = new CollectionCredentialResolver(credentials);
return new ExplicitKeySignatureTrustEngine(
credentialsResolver,
DefaultSecurityConfigurationBootstrap.buildBasicInlineKeyInfoCredentialResolver()
);
return new ExplicitKeySignatureTrustEngine(credentialsResolver,
DefaultSecurityConfigurationBootstrap.buildBasicInlineKeyInfoCredentialResolver());
}
}
private class ValidationContextConverter implements Converter<Tuple, ValidationContext> {
@@ -571,14 +592,19 @@ public final class OpenSamlAuthenticationProvider implements AuthenticationProvi
params.put(CLOCK_SKEW, OpenSamlAuthenticationProvider.this.responseTimeValidationSkew.toMillis());
params.put(COND_VALID_AUDIENCES, singleton(audience));
params.put(SC_VALID_RECIPIENTS, singleton(recipient));
params.put(SIGNATURE_REQUIRED, false); // this verification is performed earlier
params.put(SIGNATURE_REQUIRED, false); // this verification is performed
// earlier
return new ValidationContext(params);
}
}
private class SAML20AssertionValidatorConverter implements Converter<Tuple, SAML20AssertionValidator> {
private final Collection<SubjectConfirmationValidator> subjects = new ArrayList<>();
private final Collection<StatementValidator> statements = new ArrayList<>();
private final SignaturePrevalidator validator = new SAMLSignatureProfileValidator();
SAML20AssertionValidatorConverter() {
@@ -595,22 +621,19 @@ public final class OpenSamlAuthenticationProvider implements AuthenticationProvi
@Override
public SAML20AssertionValidator convert(Tuple tuple) {
Collection<ConditionValidator> conditions =
OpenSamlAuthenticationProvider.this.conditionValidators;
Collection<ConditionValidator> conditions = OpenSamlAuthenticationProvider.this.conditionValidators;
return new SAML20AssertionValidator(conditions, this.subjects, this.statements,
OpenSamlAuthenticationProvider.this.signatureTrustEngineConverter.convert(tuple.authentication),
this.validator);
}
}
private static class DecrypterConverter implements Converter<Saml2AuthenticationToken, Decrypter> {
private final EncryptedKeyResolver encryptedKeyResolver = new ChainingEncryptedKeyResolver(
asList(
new InlineEncryptedKeyResolver(),
new EncryptedElementTypeEncryptedKeyResolver(),
new SimpleRetrievalMethodEncryptedKeyResolver()
)
);
asList(new InlineEncryptedKeyResolver(), new EncryptedElementTypeEncryptedKeyResolver(),
new SimpleRetrievalMethodEncryptedKeyResolver()));
@Override
public Decrypter convert(Saml2AuthenticationToken token) {
@@ -624,6 +647,7 @@ public final class OpenSamlAuthenticationProvider implements AuthenticationProvi
decrypter.setRootInNewDocument(true);
return decrypter;
}
}
private static Saml2Error validationError(String code, String description) {
@@ -643,12 +667,15 @@ public final class OpenSamlAuthenticationProvider implements AuthenticationProvi
}
/**
* A tuple containing the authentication token and the associated OpenSAML {@link Response}.
* A tuple containing the authentication token and the associated OpenSAML
* {@link Response}.
*
* @since 5.4
*/
public static class Tuple {
private final Saml2AuthenticationToken authentication;
private final Response response;
private Tuple(Saml2AuthenticationToken authentication, Response response) {
@@ -663,5 +690,7 @@ public final class OpenSamlAuthenticationProvider implements AuthenticationProvi
public Response getResponse() {
return this.response;
}
}
}
@@ -70,6 +70,7 @@ import static org.springframework.util.StringUtils.hasText;
* @since 5.2
*/
public class OpenSamlAuthenticationRequestFactory implements Saml2AuthenticationRequestFactory {
static {
OpenSamlInitializationService.initialize();
}
@@ -77,19 +78,20 @@ public class OpenSamlAuthenticationRequestFactory implements Saml2Authentication
private Clock clock = Clock.systemUTC();
private AuthnRequestMarshaller marshaller;
private AuthnRequestBuilder authnRequestBuilder;
private IssuerBuilder issuerBuilder;
private Converter<Saml2AuthenticationRequestContext, String> protocolBindingResolver =
context -> {
if (context == null) {
return SAMLConstants.SAML2_POST_BINDING_URI;
}
return context.getRelyingPartyRegistration().getAssertionConsumerServiceBinding().getUrn();
};
private Converter<Saml2AuthenticationRequestContext, String> protocolBindingResolver = context -> {
if (context == null) {
return SAMLConstants.SAML2_POST_BINDING_URI;
}
return context.getRelyingPartyRegistration().getAssertionConsumerServiceBinding().getUrn();
};
private Function<Saml2AuthenticationRequestContext, Consumer<AuthnRequest>> authnRequestConsumerResolver
= context -> authnRequest -> {};
private Function<Saml2AuthenticationRequestContext, Consumer<AuthnRequest>> authnRequestConsumerResolver = context -> authnRequest -> {
};
/**
* Creates an {@link OpenSamlAuthenticationRequestFactory}
@@ -100,19 +102,18 @@ public class OpenSamlAuthenticationRequestFactory implements Saml2Authentication
.getMarshaller(AuthnRequest.DEFAULT_ELEMENT_NAME);
this.authnRequestBuilder = (AuthnRequestBuilder) registry.getBuilderFactory()
.getBuilder(AuthnRequest.DEFAULT_ELEMENT_NAME);
this.issuerBuilder = (IssuerBuilder) registry.getBuilderFactory()
.getBuilder(Issuer.DEFAULT_ELEMENT_NAME);
this.issuerBuilder = (IssuerBuilder) registry.getBuilderFactory().getBuilder(Issuer.DEFAULT_ELEMENT_NAME);
}
@Override
@Deprecated
public String createAuthenticationRequest(Saml2AuthenticationRequest request) {
AuthnRequest authnRequest = createAuthnRequest(request.getIssuer(),
request.getDestination(), request.getAssertionConsumerServiceUrl(),
this.protocolBindingResolver.convert(null));
AuthnRequest authnRequest = createAuthnRequest(request.getIssuer(), request.getDestination(),
request.getAssertionConsumerServiceUrl(), this.protocolBindingResolver.convert(null));
for (org.springframework.security.saml2.credentials.Saml2X509Credential credential : request.getCredentials()) {
if (credential.isSigningCredential()) {
Credential cred = getSigningCredential(credential.getCertificate(), credential.getPrivateKey(), request.getIssuer());
Credential cred = getSigningCredential(credential.getCertificate(), credential.getPrivateKey(),
request.getIssuer());
return serialize(sign(authnRequest, cred));
}
}
@@ -125,41 +126,34 @@ public class OpenSamlAuthenticationRequestFactory implements Saml2Authentication
@Override
public Saml2PostAuthenticationRequest createPostAuthenticationRequest(Saml2AuthenticationRequestContext context) {
AuthnRequest authnRequest = createAuthnRequest(context);
String xml = context.getRelyingPartyRegistration().getAssertingPartyDetails().getWantAuthnRequestsSigned() ?
serialize(sign(authnRequest, context.getRelyingPartyRegistration())) :
serialize(authnRequest);
String xml = context.getRelyingPartyRegistration().getAssertingPartyDetails().getWantAuthnRequestsSigned()
? serialize(sign(authnRequest, context.getRelyingPartyRegistration())) : serialize(authnRequest);
return Saml2PostAuthenticationRequest.withAuthenticationRequestContext(context)
.samlRequest(samlEncode(xml.getBytes(UTF_8)))
.build();
.samlRequest(samlEncode(xml.getBytes(UTF_8))).build();
}
/**
* {@inheritDoc}
*/
@Override
public Saml2RedirectAuthenticationRequest createRedirectAuthenticationRequest(Saml2AuthenticationRequestContext context) {
public Saml2RedirectAuthenticationRequest createRedirectAuthenticationRequest(
Saml2AuthenticationRequestContext context) {
AuthnRequest authnRequest = createAuthnRequest(context);
String xml = serialize(authnRequest);
Builder result = Saml2RedirectAuthenticationRequest.withAuthenticationRequestContext(context);
String deflatedAndEncoded = samlEncode(samlDeflate(xml));
result.samlRequest(deflatedAndEncoded)
.relayState(context.getRelayState());
result.samlRequest(deflatedAndEncoded).relayState(context.getRelayState());
if (context.getRelyingPartyRegistration().getAssertingPartyDetails().getWantAuthnRequestsSigned()) {
Collection<Saml2X509Credential> signingCredentials = context.getRelyingPartyRegistration().getSigningX509Credentials();
Collection<Saml2X509Credential> signingCredentials = context.getRelyingPartyRegistration()
.getSigningX509Credentials();
for (Saml2X509Credential credential : signingCredentials) {
Credential cred = getSigningCredential(credential.getCertificate(), credential.getPrivateKey(), "");
Map<String, String> signedParams = signQueryParameters(
cred,
deflatedAndEncoded,
Map<String, String> signedParams = signQueryParameters(cred, deflatedAndEncoded,
context.getRelayState());
return result
.samlRequest(signedParams.get("SAMLRequest"))
.relayState(signedParams.get("RelayState"))
.sigAlg(signedParams.get("SigAlg"))
.signature(signedParams.get("Signature"))
.build();
return result.samlRequest(signedParams.get("SAMLRequest")).relayState(signedParams.get("RelayState"))
.sigAlg(signedParams.get("SigAlg")).signature(signedParams.get("Signature")).build();
}
throw new Saml2Exception("No signing credential provided");
}
@@ -168,15 +162,14 @@ public class OpenSamlAuthenticationRequestFactory implements Saml2Authentication
}
private AuthnRequest createAuthnRequest(Saml2AuthenticationRequestContext context) {
AuthnRequest authnRequest = createAuthnRequest(context.getIssuer(),
context.getDestination(), context.getAssertionConsumerServiceUrl(),
this.protocolBindingResolver.convert(context));
AuthnRequest authnRequest = createAuthnRequest(context.getIssuer(), context.getDestination(),
context.getAssertionConsumerServiceUrl(), this.protocolBindingResolver.convert(context));
this.authnRequestConsumerResolver.apply(context).accept(authnRequest);
return authnRequest;
}
private AuthnRequest createAuthnRequest
(String issuer, String destination, String assertionConsumerServiceUrl, String protocolBinding) {
private AuthnRequest createAuthnRequest(String issuer, String destination, String assertionConsumerServiceUrl,
String protocolBinding) {
AuthnRequest auth = this.authnRequestBuilder.buildObject();
auth.setID("ARQ" + UUID.randomUUID().toString().substring(1));
auth.setIssueInstant(new DateTime(this.clock.millis()));
@@ -193,7 +186,6 @@ public class OpenSamlAuthenticationRequestFactory implements Saml2Authentication
/**
* Set the {@link AuthnRequest} post-processor resolver
*
* @param authnRequestConsumerResolver
* @since 5.4
*/
@@ -204,10 +196,7 @@ public class OpenSamlAuthenticationRequestFactory implements Saml2Authentication
}
/**
* '
* Use this {@link Clock} with {@link Instant#now()} for generating
* timestamps
*
* ' Use this {@link Clock} with {@link Instant#now()} for generating timestamps
* @param clock
*/
public void setClock(Clock clock) {
@@ -218,20 +207,20 @@ public class OpenSamlAuthenticationRequestFactory implements Saml2Authentication
/**
* Sets the {@code protocolBinding} to use when generating authentication requests.
* Acceptable values are {@link SAMLConstants#SAML2_POST_BINDING_URI} and
* {@link SAMLConstants#SAML2_REDIRECT_BINDING_URI}
* The IDP will be reading this value in the {@code AuthNRequest} to determine how to
* send the Response/Assertion to the ACS URL, assertion consumer service URL.
*
* {@link SAMLConstants#SAML2_REDIRECT_BINDING_URI} The IDP will be reading this value
* in the {@code AuthNRequest} to determine how to send the Response/Assertion to the
* ACS URL, assertion consumer service URL.
* @param protocolBinding either {@link SAMLConstants#SAML2_POST_BINDING_URI} or
* {@link SAMLConstants#SAML2_REDIRECT_BINDING_URI}
* @throws IllegalArgumentException if the protocolBinding is not valid
* @deprecated Use {@link org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration.Builder#assertionConsumerServiceBinding(Saml2MessageBinding)}
* @deprecated Use
* {@link org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration.Builder#assertionConsumerServiceBinding(Saml2MessageBinding)}
* instead
*/
@Deprecated
public void setProtocolBinding(String protocolBinding) {
boolean isAllowedBinding = SAMLConstants.SAML2_POST_BINDING_URI.equals(protocolBinding) ||
SAMLConstants.SAML2_REDIRECT_BINDING_URI.equals(protocolBinding);
boolean isAllowedBinding = SAMLConstants.SAML2_POST_BINDING_URI.equals(protocolBinding)
|| SAMLConstants.SAML2_REDIRECT_BINDING_URI.equals(protocolBinding);
if (!isAllowedBinding) {
throw new IllegalArgumentException("Invalid protocol binding: " + protocolBinding);
}
@@ -240,8 +229,8 @@ public class OpenSamlAuthenticationRequestFactory implements Saml2Authentication
private AuthnRequest sign(AuthnRequest authnRequest, RelyingPartyRegistration relyingPartyRegistration) {
for (Saml2X509Credential credential : relyingPartyRegistration.getSigningX509Credentials()) {
Credential cred = getSigningCredential(
credential.getCertificate(), credential.getPrivateKey(), relyingPartyRegistration.getEntityId());
Credential cred = getSigningCredential(credential.getCertificate(), credential.getPrivateKey(),
relyingPartyRegistration.getEntityId());
return sign(authnRequest, cred);
}
throw new IllegalArgumentException("No signing credential provided");
@@ -256,7 +245,8 @@ public class OpenSamlAuthenticationRequestFactory implements Saml2Authentication
try {
SignatureSupport.signObject(authnRequest, parameters);
return authnRequest;
} catch (MarshallingException | SignatureException | SecurityException e) {
}
catch (MarshallingException | SignatureException | SecurityException e) {
throw new Saml2Exception(e);
}
}
@@ -268,36 +258,21 @@ public class OpenSamlAuthenticationRequestFactory implements Saml2Authentication
return cred;
}
private Map<String, String> signQueryParameters(
Credential credential,
String samlRequest,
String relayState) {
private Map<String, String> signQueryParameters(Credential credential, String samlRequest, String relayState) {
Assert.notNull(samlRequest, "samlRequest cannot be null");
String algorithmUri = SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256;
StringBuilder queryString = new StringBuilder();
queryString
.append("SAMLRequest")
.append("=")
.append(UriUtils.encode(samlRequest, StandardCharsets.ISO_8859_1))
queryString.append("SAMLRequest").append("=").append(UriUtils.encode(samlRequest, StandardCharsets.ISO_8859_1))
.append("&");
if (hasText(relayState)) {
queryString
.append("RelayState")
.append("=")
.append(UriUtils.encode(relayState, StandardCharsets.ISO_8859_1))
.append("&");
queryString.append("RelayState").append("=")
.append(UriUtils.encode(relayState, StandardCharsets.ISO_8859_1)).append("&");
}
queryString
.append("SigAlg")
.append("=")
.append(UriUtils.encode(algorithmUri, StandardCharsets.ISO_8859_1));
queryString.append("SigAlg").append("=").append(UriUtils.encode(algorithmUri, StandardCharsets.ISO_8859_1));
try {
byte[] rawSignature = XMLSigningUtil.signWithURI(
credential,
algorithmUri,
queryString.toString().getBytes(StandardCharsets.UTF_8)
);
byte[] rawSignature = XMLSigningUtil.signWithURI(credential, algorithmUri,
queryString.toString().getBytes(StandardCharsets.UTF_8));
String b64Signature = Saml2Utils.samlEncode(rawSignature);
Map<String, String> result = new LinkedHashMap<>();
@@ -318,8 +293,10 @@ public class OpenSamlAuthenticationRequestFactory implements Saml2Authentication
try {
Element element = this.marshaller.marshall(authnRequest);
return SerializeSupport.nodeToString(element);
} catch (MarshallingException e) {
}
catch (MarshallingException e) {
throw new Saml2Exception(e);
}
}
}
@@ -31,9 +31,9 @@ import java.util.Map;
* @since 5.2.2
*/
public interface Saml2AuthenticatedPrincipal extends AuthenticatedPrincipal {
/**
* Get the first value of Saml2 token attribute by name
*
* @param name the name of the attribute
* @param <A> the type of the attribute
* @return the first attribute value or {@code null} otherwise
@@ -47,7 +47,6 @@ public interface Saml2AuthenticatedPrincipal extends AuthenticatedPrincipal {
/**
* Get the Saml2 token attribute by name
*
* @param name the name of the attribute
* @param <A> the type of the attribute
* @return the attribute or {@code null} otherwise
@@ -60,11 +59,11 @@ public interface Saml2AuthenticatedPrincipal extends AuthenticatedPrincipal {
/**
* Get the Saml2 token attributes
*
* @return the Saml2 token attributes
* @since 5.4
*/
default Map<String, List<Object>> getAttributes() {
return Collections.emptyMap();
}
}
@@ -25,23 +25,23 @@ import org.springframework.util.Assert;
import java.util.Collection;
/**
* An implementation of an {@link AbstractAuthenticationToken}
* that represents an authenticated SAML 2.0 {@link Authentication}.
* An implementation of an {@link AbstractAuthenticationToken} that represents an
* authenticated SAML 2.0 {@link Authentication}.
* <p>
* The {@link Authentication} associates valid SAML assertion
* data with a Spring Security authentication object
* The complete assertion is contained in the object in String format,
* {@link Saml2Authentication#getSaml2Response()}
* The {@link Authentication} associates valid SAML assertion data with a Spring Security
* authentication object The complete assertion is contained in the object in String
* format, {@link Saml2Authentication#getSaml2Response()}
*
* @since 5.2
* @see AbstractAuthenticationToken
*/
public class Saml2Authentication extends AbstractAuthenticationToken {
private final AuthenticatedPrincipal principal;
private final String saml2Response;
public Saml2Authentication(AuthenticatedPrincipal principal,
String saml2Response,
public Saml2Authentication(AuthenticatedPrincipal principal, String saml2Response,
Collection<? extends GrantedAuthority> authorities) {
super(authorities);
Assert.notNull(principal, "principal cannot be null");
@@ -27,23 +27,23 @@ import org.springframework.util.Assert;
* <p>
* There are a number of scenarios where an error may occur, for example:
* <ul>
* <li>The response or assertion request is missing or malformed</li>
* <li>Missing or invalid subject</li>
* <li>Missing or invalid signatures</li>
* <li>The time period validation for the assertion fails</li>
* <li>One of the assertion conditions was not met</li>
* <li>Decryption failed</li>
* <li>Unable to locate a subject identifier, commonly known as username</li>
* <li>The response or assertion request is missing or malformed</li>
* <li>Missing or invalid subject</li>
* <li>Missing or invalid signatures</li>
* <li>The time period validation for the assertion fails</li>
* <li>One of the assertion conditions was not met</li>
* <li>Decryption failed</li>
* <li>Unable to locate a subject identifier, commonly known as username</li>
* </ul>
*
* @since 5.2
*/
public class Saml2AuthenticationException extends AuthenticationException {
private Saml2Error error;
/**
* Constructs a {@code Saml2AuthenticationException} using the provided parameters.
*
* @param error the {@link Saml2Error SAML 2.0 Error}
*/
public Saml2AuthenticationException(Saml2Error error) {
@@ -52,7 +52,6 @@ public class Saml2AuthenticationException extends AuthenticationException {
/**
* Constructs a {@code Saml2AuthenticationException} using the provided parameters.
*
* @param error the {@link Saml2Error SAML 2.0 Error}
* @param cause the root cause
*/
@@ -62,7 +61,6 @@ public class Saml2AuthenticationException extends AuthenticationException {
/**
* Constructs a {@code Saml2AuthenticationException} using the provided parameters.
*
* @param error the {@link Saml2Error SAML 2.0 Error}
* @param message the detail message
*/
@@ -73,7 +71,6 @@ public class Saml2AuthenticationException extends AuthenticationException {
/**
* Constructs a {@code Saml2AuthenticationException} using the provided parameters.
*
* @param error the {@link Saml2Error SAML 2.0 Error}
* @param message the detail message
* @param cause the root cause
@@ -85,57 +82,69 @@ public class Saml2AuthenticationException extends AuthenticationException {
/**
* Constructs a {@code Saml2AuthenticationException} using the provided parameters.
*
* @param error the {@link org.springframework.security.saml2.provider.service.authentication.Saml2Error SAML 2.0 Error}
* @deprecated Use {@link org.springframework.security.saml2.provider.service.authentication.Saml2Error} constructor instead
* @param error the
* {@link org.springframework.security.saml2.provider.service.authentication.Saml2Error
* SAML 2.0 Error}
* @deprecated Use
* {@link org.springframework.security.saml2.provider.service.authentication.Saml2Error}
* constructor instead
*/
@Deprecated
public Saml2AuthenticationException(org.springframework.security.saml2.provider.service.authentication.Saml2Error error) {
public Saml2AuthenticationException(
org.springframework.security.saml2.provider.service.authentication.Saml2Error error) {
this(error, error.getDescription());
}
/**
* Constructs a {@code Saml2AuthenticationException} using the provided parameters.
*
* @param error the {@link org.springframework.security.saml2.provider.service.authentication.Saml2Error SAML 2.0 Error}
* @param error the
* {@link org.springframework.security.saml2.provider.service.authentication.Saml2Error
* SAML 2.0 Error}
* @param cause the root cause
* @deprecated Use {@link org.springframework.security.saml2.provider.service.authentication.Saml2Error} constructor instead
* @deprecated Use
* {@link org.springframework.security.saml2.provider.service.authentication.Saml2Error}
* constructor instead
*/
@Deprecated
public Saml2AuthenticationException(org.springframework.security.saml2.provider.service.authentication.Saml2Error error, Throwable cause) {
public Saml2AuthenticationException(
org.springframework.security.saml2.provider.service.authentication.Saml2Error error, Throwable cause) {
this(error, cause.getMessage(), cause);
}
/**
* Constructs a {@code Saml2AuthenticationException} using the provided parameters.
*
* @param error the {@link Saml2Error SAML 2.0 Error}
* @param message the detail message
* @deprecated Use {@link Saml2Error} constructor instead
*/
@Deprecated
public Saml2AuthenticationException(org.springframework.security.saml2.provider.service.authentication.Saml2Error error, String message) {
public Saml2AuthenticationException(
org.springframework.security.saml2.provider.service.authentication.Saml2Error error, String message) {
super(message);
this.setError(error);
}
/**
* Constructs a {@code Saml2AuthenticationException} using the provided parameters.
*
* @param error the {@link org.springframework.security.saml2.provider.service.authentication.Saml2Error SAML 2.0 Error}
* @param error the
* {@link org.springframework.security.saml2.provider.service.authentication.Saml2Error
* SAML 2.0 Error}
* @param message the detail message
* @param cause the root cause
* @deprecated Use {@link org.springframework.security.saml2.provider.service.authentication.Saml2Error} constructor instead
* @deprecated Use
* {@link org.springframework.security.saml2.provider.service.authentication.Saml2Error}
* constructor instead
*/
@Deprecated
public Saml2AuthenticationException(org.springframework.security.saml2.provider.service.authentication.Saml2Error error, String message, Throwable cause) {
public Saml2AuthenticationException(
org.springframework.security.saml2.provider.service.authentication.Saml2Error error, String message,
Throwable cause) {
super(message, cause);
this.setError(error);
}
/**
* Get the associated {@link Saml2Error}
*
* @return the associated {@link Saml2Error}
*/
public Saml2Error getSaml2Error() {
@@ -144,7 +153,6 @@ public class Saml2AuthenticationException extends AuthenticationException {
/**
* Returns the {@link Saml2Error SAML 2.0 Error}.
*
* @return the {@link Saml2Error}
* @deprecated Use {@link #getSaml2Error()} instead
*/
@@ -170,4 +178,5 @@ public class Saml2AuthenticationException extends AuthenticationException {
sb.append('}');
return sb.toString();
}
}
@@ -25,24 +25,26 @@ import java.util.List;
import java.util.function.Consumer;
/**
* Data holder for information required to send an {@code AuthNRequest}
* from the service provider to the identity provider
* https://www.oasis-open.org/committees/download.php/35711/sstc-saml-core-errata-2.0-wd-06-diff.pdf (line 2031)
* Data holder for information required to send an {@code AuthNRequest} from the service
* provider to the identity provider
* https://www.oasis-open.org/committees/download.php/35711/sstc-saml-core-errata-2.0-wd-06-diff.pdf
* (line 2031)
*
* @since 5.2
* @deprecated use {@link Saml2AuthenticationRequestContext}
*/
@Deprecated
public final class Saml2AuthenticationRequest {
private final String issuer;
private final List<Saml2X509Credential> credentials;
private final String destination;
private final String assertionConsumerServiceUrl;
private Saml2AuthenticationRequest(
String issuer,
String destination,
String assertionConsumerServiceUrl,
private Saml2AuthenticationRequest(String issuer, String destination, String assertionConsumerServiceUrl,
List<Saml2X509Credential> credentials) {
Assert.hasText(issuer, "issuer cannot be null");
Assert.hasText(destination, "destination cannot be null");
@@ -58,10 +60,9 @@ public final class Saml2AuthenticationRequest {
}
}
/**
* returns the issuer, the local SP entity ID, for this authentication request.
* This property should be used to populate the {@code AuthNRequest.Issuer} XML element.
* returns the issuer, the local SP entity ID, for this authentication request. This
* property should be used to populate the {@code AuthNRequest.Issuer} XML element.
* This value typically is a URI, but can be an arbitrary string.
* @return issuer
*/
@@ -70,8 +71,9 @@ public final class Saml2AuthenticationRequest {
}
/**
* returns the destination, the WEB Single Sign On URI, for this authentication request.
* This property populates the {@code AuthNRequest#Destination} XML attribute.
* returns the destination, the WEB Single Sign On URI, for this authentication
* request. This property populates the {@code AuthNRequest#Destination} XML
* attribute.
* @return destination
*/
public String getDestination() {
@@ -79,9 +81,9 @@ public final class Saml2AuthenticationRequest {
}
/**
* Returns the desired {@code AssertionConsumerServiceUrl} that this SP wishes to receive the
* assertion on. The IDP may or may not honor this request.
* This property populates the {@code AuthNRequest#AssertionConsumerServiceURL} XML attribute.
* Returns the desired {@code AssertionConsumerServiceUrl} that this SP wishes to
* receive the assertion on. The IDP may or may not honor this request. This property
* populates the {@code AuthNRequest#AssertionConsumerServiceURL} XML attribute.
* @return the AssertionConsumerServiceURL value
*/
public String getAssertionConsumerServiceUrl() {
@@ -89,7 +91,8 @@ public final class Saml2AuthenticationRequest {
}
/**
* Returns a list of credentials that can be used to sign the {@code AuthNRequest} object
* Returns a list of credentials that can be used to sign the {@code AuthNRequest}
* object
* @return signing credentials
*/
public List<Saml2X509Credential> getCredentials() {
@@ -97,8 +100,7 @@ public final class Saml2AuthenticationRequest {
}
/**
* A builder for {@link Saml2AuthenticationRequest}.
* returns a builder object
* A builder for {@link Saml2AuthenticationRequest}. returns a builder object
*/
public static Builder builder() {
return new Builder();
@@ -106,25 +108,25 @@ public final class Saml2AuthenticationRequest {
/**
* A builder for {@link Saml2AuthenticationRequest}.
* @param context a context object to copy values from.
* returns a builder object
* @param context a context object to copy values from. returns a builder object
*/
public static Builder withAuthenticationRequestContext(Saml2AuthenticationRequestContext context) {
return new Builder()
.assertionConsumerServiceUrl(context.getAssertionConsumerServiceUrl())
.issuer(context.getIssuer())
.destination(context.getDestination())
.credentials(c -> c.addAll(context.getRelyingPartyRegistration().getCredentials()))
;
return new Builder().assertionConsumerServiceUrl(context.getAssertionConsumerServiceUrl())
.issuer(context.getIssuer()).destination(context.getDestination())
.credentials(c -> c.addAll(context.getRelyingPartyRegistration().getCredentials()));
}
/**
* A builder for {@link Saml2AuthenticationRequest}.
*/
public static class Builder {
private String issuer;
private List<Saml2X509Credential> credentials = new LinkedList<>();
private String destination;
private String assertionConsumerServiceUrl;
private Builder() {
@@ -141,11 +143,9 @@ public final class Saml2AuthenticationRequest {
}
/**
* Modifies the collection of {@link Saml2X509Credential} credentials
* used in communication between IDP and SP, specifically signing the
* authentication request.
* For example:
* <code>
* Modifies the collection of {@link Saml2X509Credential} credentials used in
* communication between IDP and SP, specifically signing the authentication
* request. For example: <code>
* Saml2X509Credential credential = ...;
* return Saml2AuthenticationRequest.withLocalSpEntityId("id")
* .credentials(c -> c.add(credential))
@@ -161,7 +161,8 @@ public final class Saml2AuthenticationRequest {
}
/**
* Sets the Destination for the authentication request. Typically the {@code Service Provider EntityID}
* Sets the Destination for the authentication request. Typically the
* {@code Service Provider EntityID}
* @param destination - a required value
* @return this {@code Builder}
*/
@@ -187,12 +188,10 @@ public final class Saml2AuthenticationRequest {
* @throws {@link IllegalArgumentException} if a required property is not set
*/
public Saml2AuthenticationRequest build() {
return new Saml2AuthenticationRequest(
this.issuer,
this.destination,
this.assertionConsumerServiceUrl,
this.credentials
);
return new Saml2AuthenticationRequest(this.issuer, this.destination, this.assertionConsumerServiceUrl,
this.credentials);
}
}
}
@@ -20,9 +20,9 @@ import org.springframework.security.saml2.provider.service.registration.RelyingP
import org.springframework.util.Assert;
/**
* Data holder for information required to create an {@code AuthNRequest}
* to be sent from the service provider to the identity provider
* <a href="https://www.oasis-open.org/committees/download.php/35711/sstc-saml-core-errata-2.0-wd-06-diff.pdf">
* Data holder for information required to create an {@code AuthNRequest} to be sent from
* the service provider to the identity provider <a href=
* "https://www.oasis-open.org/committees/download.php/35711/sstc-saml-core-errata-2.0-wd-06-diff.pdf">
* Assertions and Protocols for SAML 2 (line 2031)</a>
*
* @see Saml2AuthenticationRequestFactory#createPostAuthenticationRequest(Saml2AuthenticationRequestContext)
@@ -30,16 +30,17 @@ import org.springframework.util.Assert;
* @since 5.3
*/
public class Saml2AuthenticationRequestContext {
private final RelyingPartyRegistration relyingPartyRegistration;
private final String issuer;
private final String assertionConsumerServiceUrl;
private final String relayState;
protected Saml2AuthenticationRequestContext(
RelyingPartyRegistration relyingPartyRegistration,
String issuer,
String assertionConsumerServiceUrl,
String relayState) {
protected Saml2AuthenticationRequestContext(RelyingPartyRegistration relyingPartyRegistration, String issuer,
String assertionConsumerServiceUrl, String relayState) {
Assert.hasText(issuer, "issuer cannot be null or empty");
Assert.notNull(relyingPartyRegistration, "relyingPartyRegistration cannot be null");
Assert.hasText(assertionConsumerServiceUrl, "spAssertionConsumerServiceUrl cannot be null or empty");
@@ -50,7 +51,8 @@ public class Saml2AuthenticationRequestContext {
}
/**
* Returns the {@link RelyingPartyRegistration} configuration for which the AuthNRequest is intended for.
* Returns the {@link RelyingPartyRegistration} configuration for which the
* AuthNRequest is intended for.
* @return the {@link RelyingPartyRegistration} configuration
*/
public RelyingPartyRegistration getRelyingPartyRegistration() {
@@ -59,8 +61,8 @@ public class Saml2AuthenticationRequestContext {
/**
* Returns the {@code Issuer} value to be used in the {@code AuthNRequest} object.
* This property should be used to populate the {@code AuthNRequest.Issuer} XML element.
* This value typically is a URI, but can be an arbitrary string.
* This property should be used to populate the {@code AuthNRequest.Issuer} XML
* element. This value typically is a URI, but can be an arbitrary string.
* @return the Issuer value
*/
public String getIssuer() {
@@ -68,9 +70,9 @@ public class Saml2AuthenticationRequestContext {
}
/**
* Returns the desired {@code AssertionConsumerServiceUrl} that this SP wishes to receive the
* assertion on. The IDP may or may not honor this request.
* This property populates the {@code AuthNRequest.AssertionConsumerServiceURL} XML attribute.
* Returns the desired {@code AssertionConsumerServiceUrl} that this SP wishes to
* receive the assertion on. The IDP may or may not honor this request. This property
* populates the {@code AuthNRequest.AssertionConsumerServiceURL} XML attribute.
* @return the AssertionConsumerServiceURL value
*/
public String getAssertionConsumerServiceUrl() {
@@ -86,8 +88,9 @@ public class Saml2AuthenticationRequestContext {
}
/**
* Returns the {@code Destination}, the WEB Single Sign On URI, for this authentication request.
* This property can also populate the {@code AuthNRequest.Destination} XML attribute.
* Returns the {@code Destination}, the WEB Single Sign On URI, for this
* authentication request. This property can also populate the
* {@code AuthNRequest.Destination} XML attribute.
* @return the Destination value
*/
public String getDestination() {
@@ -106,9 +109,13 @@ public class Saml2AuthenticationRequestContext {
* A builder for {@link Saml2AuthenticationRequestContext}.
*/
public static class Builder {
private String issuer;
private String assertionConsumerServiceUrl;
private String relayState;
private RelyingPartyRegistration relyingPartyRegistration;
private Builder() {
@@ -125,7 +132,8 @@ public class Saml2AuthenticationRequestContext {
}
/**
* Sets the {@link RelyingPartyRegistration} used to build the authentication request.
* Sets the {@link RelyingPartyRegistration} used to build the authentication
* request.
* @param relyingPartyRegistration - a required value
* @return this {@code Builder}
*/
@@ -147,7 +155,8 @@ public class Saml2AuthenticationRequestContext {
/**
* Sets the {@code RelayState} parameter that will accompany this AuthNRequest
* @param relayState the relay state value, unencoded. if null or empty, the parameter will be removed from the map.
* @param relayState the relay state value, unencoded. if null or empty, the
* parameter will be removed from the map.
* @return this object
*/
public Builder relayState(String relayState) {
@@ -161,12 +170,10 @@ public class Saml2AuthenticationRequestContext {
* @throws {@link IllegalArgumentException} if a required property is not set
*/
public Saml2AuthenticationRequestContext build() {
return new Saml2AuthenticationRequestContext(
this.relyingPartyRegistration,
this.issuer,
this.assertionConsumerServiceUrl,
this.relayState
);
return new Saml2AuthenticationRequestContext(this.relyingPartyRegistration, this.issuer,
this.assertionConsumerServiceUrl, this.relayState);
}
}
}
@@ -27,9 +27,9 @@ import static org.springframework.security.saml2.provider.service.authentication
import static org.springframework.security.saml2.provider.service.authentication.Saml2Utils.samlEncode;
/**
* Component that generates AuthenticationRequest, <code>samlp:AuthnRequestType</code> XML, and accompanying
* signature data.
* as defined by https://www.oasis-open.org/committees/download.php/35711/sstc-saml-core-errata-2.0-wd-06-diff.pdf
* Component that generates AuthenticationRequest, <code>samlp:AuthnRequestType</code>
* XML, and accompanying signature data. as defined by
* https://www.oasis-open.org/committees/download.php/35711/sstc-saml-core-errata-2.0-wd-06-diff.pdf
* Page 50, Line 2147
*
* @since 5.2
@@ -37,81 +37,81 @@ import static org.springframework.security.saml2.provider.service.authentication
public interface Saml2AuthenticationRequestFactory {
/**
* Creates an authentication request from the Service Provider, sp, to the Identity Provider, idp.
* The authentication result is an XML string that may be signed, encrypted, both or neither.
* This method only returns the {@code SAMLRequest} string for the request, and for a complete
* set of data parameters please use {@link #createRedirectAuthenticationRequest(Saml2AuthenticationRequestContext)}
* or {@link #createPostAuthenticationRequest(Saml2AuthenticationRequestContext)}
*
* @param request information about the identity provider,
* the recipient of this authentication request and accompanying data
* @return XML data in the format of a String. This data may be signed, encrypted, both signed and encrypted with the
* signature embedded in the XML or neither signed and encrypted
* Creates an authentication request from the Service Provider, sp, to the Identity
* Provider, idp. The authentication result is an XML string that may be signed,
* encrypted, both or neither. This method only returns the {@code SAMLRequest} string
* for the request, and for a complete set of data parameters please use
* {@link #createRedirectAuthenticationRequest(Saml2AuthenticationRequestContext)} or
* {@link #createPostAuthenticationRequest(Saml2AuthenticationRequestContext)}
* @param request information about the identity provider, the recipient of this
* authentication request and accompanying data
* @return XML data in the format of a String. This data may be signed, encrypted,
* both signed and encrypted with the signature embedded in the XML or neither signed
* and encrypted
* @throws Saml2Exception when a SAML library exception occurs
* @since 5.2
* @deprecated please use {@link #createRedirectAuthenticationRequest(Saml2AuthenticationRequestContext)}
* or {@link #createPostAuthenticationRequest(Saml2AuthenticationRequestContext)}
* This method will be removed in future versions of Spring Security
* @deprecated please use
* {@link #createRedirectAuthenticationRequest(Saml2AuthenticationRequestContext)} or
* {@link #createPostAuthenticationRequest(Saml2AuthenticationRequestContext)} This
* method will be removed in future versions of Spring Security
*/
@Deprecated
String createAuthenticationRequest(Saml2AuthenticationRequest request);
/**
* Creates all the necessary AuthNRequest parameters for a REDIRECT binding.
* If the {@link Saml2AuthenticationRequestContext} doesn't contain any {@link Saml2X509CredentialType#SIGNING} credentials
* the result will not contain any signatures.
* The data set will be signed and encoded for REDIRECT binding including the DEFLATE encoding.
* It will contain the following parameters to be sent as part of the query string:
* {@code SAMLRequest, RelayState, SigAlg, Signature}.
* <i>The default implementation, for sake of backwards compatibility, of this method returns the
* SAMLRequest message with an XML signature embedded, that should only be used for the{@link Saml2MessageBinding#POST}
* binding, but works over {@link Saml2MessageBinding#POST} with most providers.</i>
* @param context - information about the identity provider, the recipient of this authentication request and
* accompanying data
* @return a {@link Saml2RedirectAuthenticationRequest} object with applicable http parameters
* necessary to make the AuthNRequest over a POST or REDIRECT binding.
* All parameters will be SAML encoded/deflated, but escaped, ie URI encoded or encoded for Form Data.
* Creates all the necessary AuthNRequest parameters for a REDIRECT binding. If the
* {@link Saml2AuthenticationRequestContext} doesn't contain any
* {@link Saml2X509CredentialType#SIGNING} credentials the result will not contain any
* signatures. The data set will be signed and encoded for REDIRECT binding including
* the DEFLATE encoding. It will contain the following parameters to be sent as part
* of the query string: {@code SAMLRequest, RelayState, SigAlg, Signature}. <i>The
* default implementation, for sake of backwards compatibility, of this method returns
* the SAMLRequest message with an XML signature embedded, that should only be used
* for the{@link Saml2MessageBinding#POST} binding, but works over
* {@link Saml2MessageBinding#POST} with most providers.</i>
* @param context - information about the identity provider, the recipient of this
* authentication request and accompanying data
* @return a {@link Saml2RedirectAuthenticationRequest} object with applicable http
* parameters necessary to make the AuthNRequest over a POST or REDIRECT binding. All
* parameters will be SAML encoded/deflated, but escaped, ie URI encoded or encoded
* for Form Data.
* @throws Saml2Exception when a SAML library exception occurs
* @since 5.3
*/
default Saml2RedirectAuthenticationRequest createRedirectAuthenticationRequest(
Saml2AuthenticationRequestContext context
) {
//backwards compatible with 5.2.x settings
Saml2AuthenticationRequestContext context) {
// backwards compatible with 5.2.x settings
Saml2AuthenticationRequest.Builder resultBuilder = withAuthenticationRequestContext(context);
String samlRequest = createAuthenticationRequest(resultBuilder.build());
samlRequest = samlEncode(samlDeflate(samlRequest));
return Saml2RedirectAuthenticationRequest.withAuthenticationRequestContext(context)
.samlRequest(samlRequest)
return Saml2RedirectAuthenticationRequest.withAuthenticationRequestContext(context).samlRequest(samlRequest)
.build();
}
/**
* Creates all the necessary AuthNRequest parameters for a POST binding.
* If the {@link Saml2AuthenticationRequestContext} doesn't contain any {@link Saml2X509CredentialType#SIGNING} credentials
* the result will not contain any signatures.
* The data set will be signed and encoded for POST binding and if applicable signed with XML signatures.
* will contain the following parameters to be sent as part of the form data: {@code SAMLRequest, RelayState}.
* <i>The default implementation of this method returns the SAMLRequest message with an XML signature embedded,
* that should only be used for the {@link Saml2MessageBinding#POST} binding.</i>
* @param context - information about the identity provider, the recipient of this authentication request and
* accompanying data
* @return a {@link Saml2PostAuthenticationRequest} object with applicable http parameters
* necessary to make the AuthNRequest over a POST binding.
* All parameters will be SAML encoded but not escaped for Form Data.
* Creates all the necessary AuthNRequest parameters for a POST binding. If the
* {@link Saml2AuthenticationRequestContext} doesn't contain any
* {@link Saml2X509CredentialType#SIGNING} credentials the result will not contain any
* signatures. The data set will be signed and encoded for POST binding and if
* applicable signed with XML signatures. will contain the following parameters to be
* sent as part of the form data: {@code SAMLRequest, RelayState}. <i>The default
* implementation of this method returns the SAMLRequest message with an XML signature
* embedded, that should only be used for the {@link Saml2MessageBinding#POST}
* binding.</i>
* @param context - information about the identity provider, the recipient of this
* authentication request and accompanying data
* @return a {@link Saml2PostAuthenticationRequest} object with applicable http
* parameters necessary to make the AuthNRequest over a POST binding. All parameters
* will be SAML encoded but not escaped for Form Data.
* @throws Saml2Exception when a SAML library exception occurs
* @since 5.3
*/
default Saml2PostAuthenticationRequest createPostAuthenticationRequest(
Saml2AuthenticationRequestContext context
) {
//backwards compatible with 5.2.x settings
default Saml2PostAuthenticationRequest createPostAuthenticationRequest(Saml2AuthenticationRequestContext context) {
// backwards compatible with 5.2.x settings
Saml2AuthenticationRequest.Builder resultBuilder = withAuthenticationRequestContext(context);
String samlRequest = createAuthenticationRequest(resultBuilder.build());
samlRequest = samlEncode(samlRequest.getBytes(StandardCharsets.UTF_8));
return Saml2PostAuthenticationRequest.withAuthenticationRequestContext(context)
.samlRequest(samlRequest)
return Saml2PostAuthenticationRequest.withAuthenticationRequestContext(context).samlRequest(samlRequest)
.build();
}
@@ -27,8 +27,8 @@ import org.springframework.util.Assert;
import static org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration.withRegistrationId;
/**
* Represents an incoming SAML 2.0 response containing an assertion that has not been validated.
* {@link Saml2AuthenticationToken#isAuthenticated()} will always return false.
* Represents an incoming SAML 2.0 response containing an assertion that has not been
* validated. {@link Saml2AuthenticationToken#isAuthenticated()} will always return false.
*
* @since 5.2
* @author Filip Hanik
@@ -37,23 +37,23 @@ import static org.springframework.security.saml2.provider.service.registration.R
public class Saml2AuthenticationToken extends AbstractAuthenticationToken {
private final RelyingPartyRegistration relyingPartyRegistration;
private final String saml2Response;
/**
* Creates a {@link Saml2AuthenticationToken} with the provided parameters
*
* Note that the given {@link RelyingPartyRegistration} should have all its
* templates resolved at this point. See
* Note that the given {@link RelyingPartyRegistration} should have all its templates
* resolved at this point. See
* {@link org.springframework.security.saml2.provider.service.servlet.filter.Saml2WebSsoAuthenticationFilter}
* for an example of performing that resolution.
*
* @param relyingPartyRegistration the resolved {@link RelyingPartyRegistration} to use
* @param relyingPartyRegistration the resolved {@link RelyingPartyRegistration} to
* use
* @param saml2Response the SAML 2.0 response to authenticate
*
* @since 5.4
*/
public Saml2AuthenticationToken(RelyingPartyRegistration relyingPartyRegistration,
String saml2Response) {
public Saml2AuthenticationToken(RelyingPartyRegistration relyingPartyRegistration, String saml2Response) {
super(Collections.emptyList());
Assert.notNull(relyingPartyRegistration, "relyingPartyRegistration cannot be null");
@@ -65,26 +65,23 @@ public class Saml2AuthenticationToken extends AbstractAuthenticationToken {
/**
* Creates an authentication token from an incoming SAML 2 Response object
* @param saml2Response inflated and decoded XML representation of the SAML 2 Response
* @param recipientUri the URL that the SAML 2 Response was received at. Used for validation
* @param recipientUri the URL that the SAML 2 Response was received at. Used for
* validation
* @param idpEntityId the entity ID of the asserting entity
* @param localSpEntityId the configured local SP, the relying party, entity ID
* @param credentials the credentials configured for signature verification and decryption
* @deprecated Use {@link Saml2AuthenticationToken(RelyingPartyRegistration, String)} instead
* @param credentials the credentials configured for signature verification and
* decryption
* @deprecated Use {@link Saml2AuthenticationToken(RelyingPartyRegistration, String)}
* instead
*/
@Deprecated
public Saml2AuthenticationToken(String saml2Response,
String recipientUri,
String idpEntityId,
String localSpEntityId,
List<Saml2X509Credential> credentials) {
public Saml2AuthenticationToken(String saml2Response, String recipientUri, String idpEntityId,
String localSpEntityId, List<Saml2X509Credential> credentials) {
super(null);
this.relyingPartyRegistration = withRegistrationId(idpEntityId)
.entityId(localSpEntityId)
.assertionConsumerServiceLocation(recipientUri)
.credentials(c -> c.addAll(credentials))
.assertingPartyDetails(assertingParty -> assertingParty
.entityId(idpEntityId)
.singleSignOnServiceLocation(idpEntityId))
this.relyingPartyRegistration = withRegistrationId(idpEntityId).entityId(localSpEntityId)
.assertionConsumerServiceLocation(recipientUri).credentials(c -> c.addAll(credentials))
.assertingPartyDetails(
assertingParty -> assertingParty.entityId(idpEntityId).singleSignOnServiceLocation(idpEntityId))
.build();
this.saml2Response = saml2Response;
}
@@ -109,7 +106,6 @@ public class Saml2AuthenticationToken extends AbstractAuthenticationToken {
/**
* Get the resolved {@link RelyingPartyRegistration} associated with the request
*
* @return the resolved {@link RelyingPartyRegistration}
* @since 5.4
*/
@@ -128,7 +124,9 @@ public class Saml2AuthenticationToken extends AbstractAuthenticationToken {
/**
* Returns the URI that the SAML 2 Response object came in on
* @return URI as a string
* @deprecated Use {@link #getRelyingPartyRegistration().getAssertionConsumerServiceLocation()} instead
* @deprecated Use
* {@link #getRelyingPartyRegistration().getAssertionConsumerServiceLocation()}
* instead
*/
@Deprecated
public String getRecipientUri() {
@@ -148,7 +146,8 @@ public class Saml2AuthenticationToken extends AbstractAuthenticationToken {
/**
* Returns all the credentials associated with the relying party configuraiton
* @return
* @deprecated Get the credentials through {@link #getRelyingPartyRegistration()} instead
* @deprecated Get the credentials through {@link #getRelyingPartyRegistration()}
* instead
*/
@Deprecated
public List<Saml2X509Credential> getX509Credentials() {
@@ -176,10 +175,13 @@ public class Saml2AuthenticationToken extends AbstractAuthenticationToken {
/**
* Returns the configured IDP, asserting party, entity ID
* @return a string representing the entity ID
* @deprecated Use {@link #getRelyingPartyRegistration().getAssertingPartyDetails().getEntityId()} instead
* @deprecated Use
* {@link #getRelyingPartyRegistration().getAssertingPartyDetails().getEntityId()}
* instead
*/
@Deprecated
public String getIdpEntityId() {
return this.relyingPartyRegistration.getAssertingPartyDetails().getEntityId();
}
}
@@ -24,22 +24,23 @@ import org.springframework.security.core.SpringSecurityCoreVersion;
* A representation of an SAML 2.0 Error.
*
* <p>
* At a minimum, an error response will contain an error code.
* The commonly used error code are defined in this class
* or a new codes can be defined in the future as arbitrary strings.
* At a minimum, an error response will contain an error code. The commonly used error
* code are defined in this class or a new codes can be defined in the future as arbitrary
* strings.
* </p>
*
* @since 5.2
* @deprecated Use {@link org.springframework.security.saml2.core.Saml2Error} instead
*/
@Deprecated
public class Saml2Error implements Serializable {
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
private final org.springframework.security.saml2.core.Saml2Error error;
/**
* Constructs a {@code Saml2Error} using the provided parameters.
*
* @param errorCode the error code
* @param description the error description
*/
@@ -49,7 +50,6 @@ public class Saml2Error implements Serializable {
/**
* Returns the error code.
*
* @return the error code
*/
public final String getErrorCode() {
@@ -58,7 +58,6 @@ public class Saml2Error implements Serializable {
/**
* Returns the error description.
*
* @return the error description
*/
public final String getDescription() {
@@ -67,7 +66,7 @@ public class Saml2Error implements Serializable {
@Override
public String toString() {
return "[" + this.getErrorCode() + "] " +
(this.getDescription() != null ? this.getDescription() : "");
return "[" + this.getErrorCode() + "] " + (this.getDescription() != null ? this.getDescription() : "");
}
}
@@ -24,80 +24,84 @@ package org.springframework.security.saml2.provider.service.authentication;
*/
@Deprecated
public interface Saml2ErrorCodes {
/**
* SAML Data does not represent a SAML 2 Response object.
* A valid XML object was received, but that object was not a
* SAML 2 Response object of type {@code ResponseType} per specification
* SAML Data does not represent a SAML 2 Response object. A valid XML object was
* received, but that object was not a SAML 2 Response object of type
* {@code ResponseType} per specification
* https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf#page=46
*/
String UNKNOWN_RESPONSE_CLASS = org.springframework.security.saml2.core.Saml2ErrorCodes.UNKNOWN_RESPONSE_CLASS;
/**
* The response data is malformed or incomplete.
* An invalid XML object was received, and XML unmarshalling failed.
* The response data is malformed or incomplete. An invalid XML object was received,
* and XML unmarshalling failed.
*/
String MALFORMED_RESPONSE_DATA = org.springframework.security.saml2.core.Saml2ErrorCodes.MALFORMED_RESPONSE_DATA;
/**
* Response destination does not match the request URL.
* A SAML 2 response object was received at a URL that
* did not match the URL stored in the {code Destination} attribute
* in the Response object.
* Response destination does not match the request URL. A SAML 2 response object was
* received at a URL that did not match the URL stored in the {code Destination}
* attribute in the Response object.
* https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf#page=38
*/
String INVALID_DESTINATION = org.springframework.security.saml2.core.Saml2ErrorCodes.INVALID_DESTINATION;
/**
* The assertion was not valid.
* The assertion used for authentication failed validation.
* Details around the failure will be present in the error description.
* The assertion was not valid. The assertion used for authentication failed
* validation. Details around the failure will be present in the error description.
*/
String INVALID_ASSERTION = org.springframework.security.saml2.core.Saml2ErrorCodes.INVALID_ASSERTION;
/**
* The signature of response or assertion was invalid.
* Either the response or the assertion was missing a signature
* or the signature could not be verified using the system's
* configured credentials. Most commonly the IDP's
* X509 certificate.
* The signature of response or assertion was invalid. Either the response or the
* assertion was missing a signature or the signature could not be verified using the
* system's configured credentials. Most commonly the IDP's X509 certificate.
*/
String INVALID_SIGNATURE = org.springframework.security.saml2.core.Saml2ErrorCodes.INVALID_SIGNATURE;
/**
* The assertion did not contain a subject element.
* The subject element, type SubjectType, contains
* a {@code NameID} or an {@code EncryptedID} that is used
* to assign the authenticated principal an identifier,
* typically a username.
* The assertion did not contain a subject element. The subject element, type
* SubjectType, contains a {@code NameID} or an {@code EncryptedID} that is used to
* assign the authenticated principal an identifier, typically a username.
*
* https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf#page=18
*/
String SUBJECT_NOT_FOUND = org.springframework.security.saml2.core.Saml2ErrorCodes.SUBJECT_NOT_FOUND;
/**
* The subject did not contain a user identifier
* The assertion contained a subject element, but the subject
* element did not have a {@code NameID} or {@code EncryptedID}
* element
* The subject did not contain a user identifier The assertion contained a subject
* element, but the subject element did not have a {@code NameID} or
* {@code EncryptedID} element
*
* https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf#page=18
*/
String USERNAME_NOT_FOUND = org.springframework.security.saml2.core.Saml2ErrorCodes.USERNAME_NOT_FOUND;
/**
* The system failed to decrypt an assertion or a name identifier.
* This error code will be thrown if the decryption of either a
* {@code EncryptedAssertion} or {@code EncryptedID} fails.
* The system failed to decrypt an assertion or a name identifier. This error code
* will be thrown if the decryption of either a {@code EncryptedAssertion} or
* {@code EncryptedID} fails.
* https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf#page=17
*/
String DECRYPTION_ERROR = org.springframework.security.saml2.core.Saml2ErrorCodes.DECRYPTION_ERROR;
/**
* An Issuer element contained a value that didn't
* https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf#page=15
*/
String INVALID_ISSUER = org.springframework.security.saml2.core.Saml2ErrorCodes.INVALID_ISSUER;
/**
* An error happened during validation.
* Used when internal, non classified, errors are caught during the
* authentication process.
* An error happened during validation. Used when internal, non classified, errors are
* caught during the authentication process.
*/
String INTERNAL_VALIDATION_ERROR = org.springframework.security.saml2.core.Saml2ErrorCodes.INTERNAL_VALIDATION_ERROR;
/**
* The relying party registration was not found.
* The registration ID did not correspond to any relying party registration.
* The relying party registration was not found. The registration ID did not
* correspond to any relying party registration.
*/
String RELYING_PARTY_REGISTRATION_NOT_FOUND = org.springframework.security.saml2.core.Saml2ErrorCodes.RELYING_PARTY_REGISTRATION_NOT_FOUND;
}
@@ -21,19 +21,17 @@ import org.springframework.security.saml2.provider.service.registration.Saml2Mes
import static org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding.POST;
/**
* Data holder for information required to send an {@code AuthNRequest} over a POST binding
* from the service provider to the identity provider
* https://www.oasis-open.org/committees/download.php/35711/sstc-saml-core-errata-2.0-wd-06-diff.pdf (line 2031)
* Data holder for information required to send an {@code AuthNRequest} over a POST
* binding from the service provider to the identity provider
* https://www.oasis-open.org/committees/download.php/35711/sstc-saml-core-errata-2.0-wd-06-diff.pdf
* (line 2031)
*
* @see Saml2AuthenticationRequestFactory
* @since 5.3
*/
public class Saml2PostAuthenticationRequest extends AbstractSaml2AuthenticationRequest {
private Saml2PostAuthenticationRequest(
String samlRequest,
String relayState,
String authenticationRequestUri) {
private Saml2PostAuthenticationRequest(String samlRequest, String relayState, String authenticationRequestUri) {
super(samlRequest, relayState, authenticationRequestUri);
}
@@ -46,17 +44,16 @@ public class Saml2PostAuthenticationRequest extends AbstractSaml2AuthenticationR
}
/**
* Constructs a {@link Builder} from a {@link Saml2AuthenticationRequestContext} object.
* By default the {@link Saml2PostAuthenticationRequest#getAuthenticationRequestUri()} will be set to the
* {@link Saml2AuthenticationRequestContext#getDestination()} value.
* @param context input providing {@code Destination}, {@code RelayState}, and {@code Issuer} objects.
* Constructs a {@link Builder} from a {@link Saml2AuthenticationRequestContext}
* object. By default the
* {@link Saml2PostAuthenticationRequest#getAuthenticationRequestUri()} will be set to
* the {@link Saml2AuthenticationRequestContext#getDestination()} value.
* @param context input providing {@code Destination}, {@code RelayState}, and
* {@code Issuer} objects.
* @return a modifiable builder object
*/
public static Builder withAuthenticationRequestContext(Saml2AuthenticationRequestContext context) {
return new Builder()
.authenticationRequestUri(context.getDestination())
.relayState(context.getRelayState())
;
return new Builder().authenticationRequestUri(context.getDestination()).relayState(context.getRelayState());
}
/**
@@ -73,13 +70,9 @@ public class Saml2PostAuthenticationRequest extends AbstractSaml2AuthenticationR
* @return an immutable {@link Saml2PostAuthenticationRequest} object.
*/
public Saml2PostAuthenticationRequest build() {
return new Saml2PostAuthenticationRequest(
this.samlRequest,
this.relayState,
this.authenticationRequestUri
);
return new Saml2PostAuthenticationRequest(this.samlRequest, this.relayState, this.authenticationRequestUri);
}
}
}
@@ -21,9 +21,10 @@ import org.springframework.security.saml2.provider.service.registration.Saml2Mes
import static org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding.REDIRECT;
/**
* Data holder for information required to send an {@code AuthNRequest} over a REDIRECT binding
* from the service provider to the identity provider
* https://www.oasis-open.org/committees/download.php/35711/sstc-saml-core-errata-2.0-wd-06-diff.pdf (line 2031)
* Data holder for information required to send an {@code AuthNRequest} over a REDIRECT
* binding from the service provider to the identity provider
* https://www.oasis-open.org/committees/download.php/35711/sstc-saml-core-errata-2.0-wd-06-diff.pdf
* (line 2031)
*
* @see Saml2AuthenticationRequestFactory
* @since 5.3
@@ -31,13 +32,10 @@ import static org.springframework.security.saml2.provider.service.registration.S
public class Saml2RedirectAuthenticationRequest extends AbstractSaml2AuthenticationRequest {
private final String sigAlg;
private final String signature;
private Saml2RedirectAuthenticationRequest(
String samlRequest,
String sigAlg,
String signature,
String relayState,
private Saml2RedirectAuthenticationRequest(String samlRequest, String sigAlg, String signature, String relayState,
String authenticationRequestUri) {
super(samlRequest, relayState, authenticationRequestUri);
this.sigAlg = sigAlg;
@@ -61,7 +59,7 @@ public class Saml2RedirectAuthenticationRequest extends AbstractSaml2Authenticat
}
/**
* @return {@link Saml2MessageBinding#REDIRECT}
* @return {@link Saml2MessageBinding#REDIRECT}
*/
@Override
public Saml2MessageBinding getBinding() {
@@ -69,24 +67,25 @@ public class Saml2RedirectAuthenticationRequest extends AbstractSaml2Authenticat
}
/**
* Constructs a {@link Saml2RedirectAuthenticationRequest.Builder} from a {@link Saml2AuthenticationRequestContext} object.
* By default the {@link Saml2RedirectAuthenticationRequest#getAuthenticationRequestUri()} will be set to the
* {@link Saml2AuthenticationRequestContext#getDestination()} value.
* @param context input providing {@code Destination}, {@code RelayState}, and {@code Issuer} objects.
* Constructs a {@link Saml2RedirectAuthenticationRequest.Builder} from a
* {@link Saml2AuthenticationRequestContext} object. By default the
* {@link Saml2RedirectAuthenticationRequest#getAuthenticationRequestUri()} will be
* set to the {@link Saml2AuthenticationRequestContext#getDestination()} value.
* @param context input providing {@code Destination}, {@code RelayState}, and
* {@code Issuer} objects.
* @return a modifiable builder object
*/
public static Builder withAuthenticationRequestContext(Saml2AuthenticationRequestContext context) {
return new Builder()
.authenticationRequestUri(context.getDestination())
.relayState(context.getRelayState())
;
return new Builder().authenticationRequestUri(context.getDestination()).relayState(context.getRelayState());
}
/**
* Builder class for a {@link Saml2RedirectAuthenticationRequest} object.
*/
public static class Builder extends AbstractSaml2AuthenticationRequest.Builder<Builder> {
private String sigAlg;
private String signature;
private Builder() {
@@ -118,16 +117,10 @@ public class Saml2RedirectAuthenticationRequest extends AbstractSaml2Authenticat
* @return an immutable {@link Saml2RedirectAuthenticationRequest} object.
*/
public Saml2RedirectAuthenticationRequest build() {
return new Saml2RedirectAuthenticationRequest(
this.samlRequest,
this.sigAlg,
this.signature,
this.relayState,
this.authenticationRequestUri
);
return new Saml2RedirectAuthenticationRequest(this.samlRequest, this.sigAlg, this.signature,
this.relayState, this.authenticationRequestUri);
}
}
}
@@ -34,8 +34,7 @@ import static java.util.zip.Deflater.DEFLATED;
*/
final class Saml2Utils {
private static Base64 BASE64 = new Base64(0, new byte[]{'\n'});
private static Base64 BASE64 = new Base64(0, new byte[] { '\n' });
static String samlEncode(byte[] b) {
return BASE64.encodeAsString(b);
@@ -70,4 +69,5 @@ final class Saml2Utils {
throw new Saml2Exception("Unable to inflate string", e);
}
}
}
@@ -47,14 +47,15 @@ import static org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport.getB
import static org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport.getMarshallerFactory;
/**
* Resolves the SAML 2.0 Relying Party Metadata for a given {@link RelyingPartyRegistration}
* using the OpenSAML API.
* Resolves the SAML 2.0 Relying Party Metadata for a given
* {@link RelyingPartyRegistration} using the OpenSAML API.
*
* @author Jakub Kubrynski
* @author Josh Cummings
* @since 5.4
*/
public final class OpenSamlMetadataResolver implements Saml2MetadataResolver {
static {
OpenSamlInitializationService.initialize();
}
@@ -62,8 +63,8 @@ public final class OpenSamlMetadataResolver implements Saml2MetadataResolver {
private final EntityDescriptorMarshaller entityDescriptorMarshaller;
public OpenSamlMetadataResolver() {
this.entityDescriptorMarshaller = (EntityDescriptorMarshaller)
getMarshallerFactory().getMarshaller(EntityDescriptor.DEFAULT_ELEMENT_NAME);
this.entityDescriptorMarshaller = (EntityDescriptorMarshaller) getMarshallerFactory()
.getMarshaller(EntityDescriptor.DEFAULT_ELEMENT_NAME);
Assert.notNull(this.entityDescriptorMarshaller, "entityDescriptorMarshaller cannot be null");
}
@@ -85,10 +86,10 @@ public final class OpenSamlMetadataResolver implements Saml2MetadataResolver {
SPSSODescriptor spSsoDescriptor = build(SPSSODescriptor.DEFAULT_ELEMENT_NAME);
spSsoDescriptor.addSupportedProtocol(SAMLConstants.SAML20P_NS);
spSsoDescriptor.setWantAssertionsSigned(true);
spSsoDescriptor.getKeyDescriptors().addAll(buildKeys(
registration.getSigningX509Credentials(), UsageType.SIGNING));
spSsoDescriptor.getKeyDescriptors().addAll(buildKeys(
registration.getDecryptionX509Credentials(), UsageType.ENCRYPTION));
spSsoDescriptor.getKeyDescriptors()
.addAll(buildKeys(registration.getSigningX509Credentials(), UsageType.SIGNING));
spSsoDescriptor.getKeyDescriptors()
.addAll(buildKeys(registration.getDecryptionX509Credentials(), UsageType.ENCRYPTION));
spSsoDescriptor.getAssertionConsumerServices().add(buildAssertionConsumerService(registration));
return spSsoDescriptor;
}
@@ -110,7 +111,8 @@ public final class OpenSamlMetadataResolver implements Saml2MetadataResolver {
try {
x509Certificate.setValue(new String(Base64.getEncoder().encode(certificate.getEncoded())));
} catch (CertificateEncodingException e) {
}
catch (CertificateEncodingException e) {
throw new Saml2Exception("Cannot encode certificate " + certificate.toString());
}
@@ -139,13 +141,14 @@ public final class OpenSamlMetadataResolver implements Saml2MetadataResolver {
return (T) builder.buildObject(elementName);
}
private String serialize(EntityDescriptor entityDescriptor) {
try {
Element element = this.entityDescriptorMarshaller.marshall(entityDescriptor);
return SerializeSupport.prettyPrintXML(element);
} catch (Exception e) {
}
catch (Exception e) {
throw new Saml2Exception(e);
}
}
}
@@ -19,18 +19,20 @@ package org.springframework.security.saml2.provider.service.metadata;
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
/**
* Resolves the SAML 2.0 Relying Party Metadata for a given {@link RelyingPartyRegistration}
* Resolves the SAML 2.0 Relying Party Metadata for a given
* {@link RelyingPartyRegistration}
*
* @author Jakub Kubrynski
* @author Josh Cummings
* @since 5.4
*/
public interface Saml2MetadataResolver {
/**
* Resolve the given relying party's metadata
*
* @param relyingPartyRegistration the relying party
* @return the relying party's metadata
*/
String resolve(RelyingPartyRegistration relyingPartyRegistration);
}
@@ -46,14 +46,13 @@ public class InMemoryRelyingPartyRegistrationRepository
}
private static Map<String, RelyingPartyRegistration> createMappingToIdentityProvider(
Collection<RelyingPartyRegistration> rps
) {
Collection<RelyingPartyRegistration> rps) {
LinkedHashMap<String, RelyingPartyRegistration> result = new LinkedHashMap<>();
for (RelyingPartyRegistration rp : rps) {
notNull(rp, "relying party collection cannot contain null values");
String key = rp.getRegistrationId();
notNull(rp, "relying party identifier cannot be null");
Assert.isNull(result.get(key), () -> "relying party duplicate identifier '" + key+"' detected.");
Assert.isNull(result.get(key), () -> "relying party duplicate identifier '" + key + "' detected.");
result.put(key, rp);
}
return Collections.unmodifiableMap(result);
@@ -61,7 +60,7 @@ public class InMemoryRelyingPartyRegistrationRepository
@Override
public RelyingPartyRegistration findByRegistrationId(String id) {
return this.byRegistrationId.get(id);
return this.byRegistrationId.get(id);
}
@Override
@@ -54,12 +54,13 @@ import static org.springframework.security.saml2.core.Saml2X509Credential.verifi
import static org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration.withRegistrationId;
/**
* An {@link HttpMessageConverter} that takes an {@code IDPSSODescriptor} in an HTTP response
* and converts it into a {@link RelyingPartyRegistration.Builder}.
* An {@link HttpMessageConverter} that takes an {@code IDPSSODescriptor} in an HTTP
* response and converts it into a {@link RelyingPartyRegistration.Builder}.
*
* The primary use case for this is constructing a {@link RelyingPartyRegistration} for inclusion in a
* {@link RelyingPartyRegistrationRepository}. To do so, you can include an instance of this converter in a
* {@link org.springframework.web.client.RestOperations} like so:
* The primary use case for this is constructing a {@link RelyingPartyRegistration} for
* inclusion in a {@link RelyingPartyRegistrationRepository}. To do so, you can include an
* instance of this converter in a {@link org.springframework.web.client.RestOperations}
* like so:
*
* <pre>
* RestOperations rest = new RestTemplate(Collections.singletonList(
@@ -69,11 +70,12 @@ import static org.springframework.security.saml2.provider.service.registration.R
* RelyingPartyRegistration registration = builder.registrationId("registration-id").build();
* </pre>
*
* Note that this will only configure the asserting party (IDP) half of the {@link RelyingPartyRegistration},
* meaning where and how to send AuthnRequests, how to verify Assertions, etc.
* Note that this will only configure the asserting party (IDP) half of the
* {@link RelyingPartyRegistration}, meaning where and how to send AuthnRequests, how to
* verify Assertions, etc.
*
* To further configure the {@link RelyingPartyRegistration} with relying party (SP) information, you may
* invoke the appropriate methods on the builder.
* To further configure the {@link RelyingPartyRegistration} with relying party (SP)
* information, you may invoke the appropriate methods on the builder.
*
* @author Josh Cummings
* @since 5.4
@@ -86,6 +88,7 @@ public class OpenSamlRelyingPartyRegistrationBuilderHttpMessageConverter
}
private final EntityDescriptorUnmarshaller unmarshaller;
private final ParserPool parserPool;
/**
@@ -126,8 +129,8 @@ public class OpenSamlRelyingPartyRegistrationBuilderHttpMessageConverter
* {@inheritDoc}
*/
@Override
public RelyingPartyRegistration.Builder read(Class<? extends RelyingPartyRegistration.Builder> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException {
public RelyingPartyRegistration.Builder read(Class<? extends RelyingPartyRegistration.Builder> clazz,
HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
EntityDescriptor descriptor = entityDescriptor(inputMessage.getBody());
IDPSSODescriptor idpssoDescriptor = descriptor.getIDPSSODescriptor(SAML20P_NS);
@@ -158,11 +161,11 @@ public class OpenSamlRelyingPartyRegistrationBuilderHttpMessageConverter
}
}
if (verification.isEmpty()) {
throw new Saml2Exception("Metadata response is missing verification certificates, necessary for verifying SAML assertions");
throw new Saml2Exception(
"Metadata response is missing verification certificates, necessary for verifying SAML assertions");
}
RelyingPartyRegistration.Builder builder = withRegistrationId(descriptor.getEntityID())
.assertingPartyDetails(party -> party
.entityId(descriptor.getEntityID())
.assertingPartyDetails(party -> party.entityId(descriptor.getEntityID())
.wantAuthnRequestsSigned(TRUE.equals(idpssoDescriptor.getWantAuthnRequestsSigned()))
.verificationX509Credentials(c -> c.addAll(verification))
.encryptionX509Credentials(c -> c.addAll(encryption)));
@@ -170,23 +173,26 @@ public class OpenSamlRelyingPartyRegistrationBuilderHttpMessageConverter
Saml2MessageBinding binding;
if (singleSignOnService.getBinding().equals(Saml2MessageBinding.POST.getUrn())) {
binding = Saml2MessageBinding.POST;
} else if (singleSignOnService.getBinding().equals(Saml2MessageBinding.REDIRECT.getUrn())) {
}
else if (singleSignOnService.getBinding().equals(Saml2MessageBinding.REDIRECT.getUrn())) {
binding = Saml2MessageBinding.REDIRECT;
} else {
}
else {
continue;
}
builder.assertingPartyDetails(party -> party
.singleSignOnServiceLocation(singleSignOnService.getLocation())
builder.assertingPartyDetails(party -> party.singleSignOnServiceLocation(singleSignOnService.getLocation())
.singleSignOnServiceBinding(binding));
return builder;
}
throw new Saml2Exception("Metadata response is missing a SingleSignOnService, necessary for sending AuthnRequests");
throw new Saml2Exception(
"Metadata response is missing a SingleSignOnService, necessary for sending AuthnRequests");
}
private List<X509Certificate> certificates(KeyDescriptor keyDescriptor) {
try {
return KeyInfoSupport.getCertificates(keyDescriptor.getKeyInfo());
} catch (CertificateException e) {
}
catch (CertificateException e) {
throw new Saml2Exception(e);
}
}
@@ -196,13 +202,16 @@ public class OpenSamlRelyingPartyRegistrationBuilderHttpMessageConverter
Document document = this.parserPool.parse(inputStream);
Element element = document.getDocumentElement();
return (EntityDescriptor) this.unmarshaller.unmarshall(element);
} catch (Exception e) {
}
catch (Exception e) {
throw new Saml2Exception(e);
}
}
@Override
public void write(RelyingPartyRegistration.Builder builder, MediaType contentType, HttpOutputMessage outputMessage) throws HttpMessageNotWritableException {
public void write(RelyingPartyRegistration.Builder builder, MediaType contentType, HttpOutputMessage outputMessage)
throws HttpMessageNotWritableException {
throw new HttpMessageNotWritableException("This converter cannot write a RelyingPartyRegistration.Builder");
}
}
@@ -28,14 +28,18 @@ import java.util.function.Consumer;
import java.util.function.Function;
import org.springframework.security.saml2.core.Saml2X509Credential;
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration.AssertingPartyDetails;
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration.ProviderDetails;
import org.springframework.security.saml2.provider.service.servlet.filter.Saml2WebSsoAuthenticationFilter;
import org.springframework.util.Assert;
/**
* Represents a configured relying party (aka Service Provider) and asserting party (aka Identity Provider) pair.
* Represents a configured relying party (aka Service Provider) and asserting party (aka
* Identity Provider) pair.
*
* <p>
* Each RP/AP pair is uniquely identified using a {@code registrationId}, an arbitrary string.
* Each RP/AP pair is uniquely identified using a {@code registrationId}, an arbitrary
* string.
*
* <p>
* A fully configured registration may look like:
@@ -70,20 +74,23 @@ import org.springframework.util.Assert;
public class RelyingPartyRegistration {
private final String registrationId;
private final String entityId;
private final String assertionConsumerServiceLocation;
private final Saml2MessageBinding assertionConsumerServiceBinding;
private final ProviderDetails providerDetails;
private final List<org.springframework.security.saml2.credentials.Saml2X509Credential> credentials;
private final Collection<Saml2X509Credential> decryptionX509Credentials;
private final Collection<Saml2X509Credential> signingX509Credentials;
private RelyingPartyRegistration(
String registrationId,
String entityId,
String assertionConsumerServiceLocation,
Saml2MessageBinding assertionConsumerServiceBinding,
ProviderDetails providerDetails,
private RelyingPartyRegistration(String registrationId, String entityId, String assertionConsumerServiceLocation,
Saml2MessageBinding assertionConsumerServiceBinding, ProviderDetails providerDetails,
Collection<org.springframework.security.saml2.credentials.Saml2X509Credential> credentials,
Collection<Saml2X509Credential> decryptionX509Credentials,
Collection<Saml2X509Credential> signingX509Credentials) {
@@ -106,8 +113,7 @@ public class RelyingPartyRegistration {
Assert.notNull(signingX509Credentials, "signingX509Credentials cannot be null");
for (Saml2X509Credential c : signingX509Credentials) {
Assert.notNull(c, "signingX509Credentials cannot contain null elements");
Assert.isTrue(c.isSigningCredential(),
"All signingX509Credentials must have a usage of SIGNING set");
Assert.isTrue(c.isSigningCredential(), "All signingX509Credentials must have a usage of SIGNING set");
}
this.registrationId = registrationId;
this.entityId = entityId;
@@ -121,7 +127,6 @@ public class RelyingPartyRegistration {
/**
* Get the unique registration id for this RP/AP pair
*
* @return the unique registration id for this RP/AP pair
*/
public String getRegistrationId() {
@@ -129,18 +134,17 @@ public class RelyingPartyRegistration {
}
/**
* Get the relying party's
* <a href="https://wiki.shibboleth.net/confluence/display/CONCEPT/EntityNaming">EntityID</a>.
* Get the relying party's <a href=
* "https://wiki.shibboleth.net/confluence/display/CONCEPT/EntityNaming">EntityID</a>.
*
* <p>
* Equivalent to the value found in the relying party's
* &lt;EntityDescriptor EntityID="..."/&gt;
* Equivalent to the value found in the relying party's &lt;EntityDescriptor
* EntityID="..."/&gt;
*
* <p>
* This value may contain a number of placeholders, which need to be
* resolved before use. They are {@code baseUrl}, {@code registrationId},
* {@code baseScheme}, {@code baseHost}, and {@code basePort}.
*
* This value may contain a number of placeholders, which need to be resolved before
* use. They are {@code baseUrl}, {@code registrationId}, {@code baseScheme},
* {@code baseHost}, and {@code basePort}.
* @return the relying party's EntityID
* @since 5.4
*/
@@ -149,14 +153,13 @@ public class RelyingPartyRegistration {
}
/**
* Get the AssertionConsumerService Location.
* Equivalent to the value found in &lt;AssertionConsumerService Location="..."/&gt;
* in the relying party's &lt;SPSSODescriptor&gt;.
*
* This value may contain a number of placeholders, which need to be
* resolved before use. They are {@code baseUrl}, {@code registrationId},
* {@code baseScheme}, {@code baseHost}, and {@code basePort}.
* Get the AssertionConsumerService Location. Equivalent to the value found in
* &lt;AssertionConsumerService Location="..."/&gt; in the relying party's
* &lt;SPSSODescriptor&gt;.
*
* This value may contain a number of placeholders, which need to be resolved before
* use. They are {@code baseUrl}, {@code registrationId}, {@code baseScheme},
* {@code baseHost}, and {@code basePort}.
* @return the AssertionConsumerService Location
* @since 5.4
*/
@@ -165,10 +168,9 @@ public class RelyingPartyRegistration {
}
/**
* Get the AssertionConsumerService Binding.
* Equivalent to the value found in &lt;AssertionConsumerService Binding="..."/&gt;
* in the relying party's &lt;SPSSODescriptor&gt;.
*
* Get the AssertionConsumerService Binding. Equivalent to the value found in
* &lt;AssertionConsumerService Binding="..."/&gt; in the relying party's
* &lt;SPSSODescriptor&gt;.
* @return the AssertionConsumerService Binding
* @since 5.4
*/
@@ -177,9 +179,10 @@ public class RelyingPartyRegistration {
}
/**
* Get the {@link Collection} of decryption {@link Saml2X509Credential}s associated with this relying party
*
* @return the {@link Collection} of decryption {@link Saml2X509Credential}s associated with this relying party
* Get the {@link Collection} of decryption {@link Saml2X509Credential}s associated
* with this relying party
* @return the {@link Collection} of decryption {@link Saml2X509Credential}s
* associated with this relying party
* @since 5.4
*/
public Collection<Saml2X509Credential> getDecryptionX509Credentials() {
@@ -187,9 +190,10 @@ public class RelyingPartyRegistration {
}
/**
* Get the {@link Collection} of signing {@link Saml2X509Credential}s associated with this relying party
*
* @return the {@link Collection} of signing {@link Saml2X509Credential}s associated with this relying party
* Get the {@link Collection} of signing {@link Saml2X509Credential}s associated with
* this relying party
* @return the {@link Collection} of signing {@link Saml2X509Credential}s associated
* with this relying party
* @since 5.4
*/
public Collection<Saml2X509Credential> getSigningX509Credentials() {
@@ -198,7 +202,6 @@ public class RelyingPartyRegistration {
/**
* Get the configuration details for the Asserting Party
*
* @return the {@link AssertingPartyDetails}
* @since 5.4
*/
@@ -209,7 +212,8 @@ public class RelyingPartyRegistration {
/**
* Returns the entity ID of the IDP, the asserting party.
* @return entity ID of the asserting party
* @deprecated use {@link AssertingPartyDetails#getEntityId} from {@link #getAssertingPartyDetails}
* @deprecated use {@link AssertingPartyDetails#getEntityId} from
* {@link #getAssertingPartyDetails}
*/
@Deprecated
public String getRemoteIdpEntityId() {
@@ -218,8 +222,8 @@ public class RelyingPartyRegistration {
/**
* returns the URL template for which ACS URL authentication requests should contain
* Possible variables are {@code baseUrl}, {@code registrationId},
* {@code baseScheme}, {@code baseHost}, and {@code basePort}.
* Possible variables are {@code baseUrl}, {@code registrationId}, {@code baseScheme},
* {@code baseHost}, and {@code basePort}.
* @return string containing the ACS URL template, with or without variables present
* @deprecated Use {@link #getAssertionConsumerServiceLocation} instead
*/
@@ -229,10 +233,11 @@ public class RelyingPartyRegistration {
}
/**
* Contains the URL for which to send the SAML 2 Authentication Request to initiate
* a single sign on flow.
* Contains the URL for which to send the SAML 2 Authentication Request to initiate a
* single sign on flow.
* @return a IDP URL that accepts REDIRECT or POST binding for authentication requests
* @deprecated use {@link AssertingPartyDetails#getSingleSignOnServiceLocation} from {@link #getAssertingPartyDetails}
* @deprecated use {@link AssertingPartyDetails#getSingleSignOnServiceLocation} from
* {@link #getAssertingPartyDetails}
*/
@Deprecated
public String getIdpWebSsoUrl() {
@@ -252,8 +257,8 @@ public class RelyingPartyRegistration {
/**
* The local relying party, or Service Provider, can generate it's entity ID based on
* possible variables of {@code baseUrl}, {@code registrationId},
* {@code baseScheme}, {@code baseHost}, and {@code basePort}, for example
* possible variables of {@code baseUrl}, {@code registrationId}, {@code baseScheme},
* {@code baseHost}, and {@code basePort}, for example
* {@code {baseUrl}/saml2/service-provider-metadata/{registrationId}}
* @return a string containing the entity ID or entity ID template
* @deprecated Use {@link #getEntityId} instead
@@ -264,10 +269,11 @@ public class RelyingPartyRegistration {
}
/**
* Returns a list of configured credentials to be used in message exchanges between relying party, SP, and
* asserting party, IDP.
* Returns a list of configured credentials to be used in message exchanges between
* relying party, SP, and asserting party, IDP.
* @return a list of credentials
* @deprecated Instead of retrieving all credentials, use the appropriate method for obtaining the correct type
* @deprecated Instead of retrieving all credentials, use the appropriate method for
* obtaining the correct type
*/
@Deprecated
public List<org.springframework.security.saml2.credentials.Saml2X509Credential> getCredentials() {
@@ -278,11 +284,13 @@ public class RelyingPartyRegistration {
* @return a filtered list containing only credentials of type
* {@link org.springframework.security.saml2.credentials.Saml2X509Credential.Saml2X509CredentialType#VERIFICATION}.
* Returns an empty list of credentials are not found
* @deprecated Use {@link #getAssertingPartyDetails().getSigningX509Credentials()} instead
* @deprecated Use {@link #getAssertingPartyDetails().getSigningX509Credentials()}
* instead
*/
@Deprecated
public List<org.springframework.security.saml2.credentials.Saml2X509Credential> getVerificationCredentials() {
return filterCredentials(org.springframework.security.saml2.credentials.Saml2X509Credential::isSignatureVerficationCredential);
return filterCredentials(
org.springframework.security.saml2.credentials.Saml2X509Credential::isSignatureVerficationCredential);
}
/**
@@ -293,18 +301,21 @@ public class RelyingPartyRegistration {
*/
@Deprecated
public List<org.springframework.security.saml2.credentials.Saml2X509Credential> getSigningCredentials() {
return filterCredentials(org.springframework.security.saml2.credentials.Saml2X509Credential::isSigningCredential);
return filterCredentials(
org.springframework.security.saml2.credentials.Saml2X509Credential::isSigningCredential);
}
/**
* @return a filtered list containing only credentials of type
* {@link org.springframework.security.saml2.credentials.Saml2X509Credential.Saml2X509CredentialType#ENCRYPTION}.
* Returns an empty list of credentials are not found
* @deprecated Use {@link AssertingPartyDetails#getEncryptionX509Credentials()} instead
* @deprecated Use {@link AssertingPartyDetails#getEncryptionX509Credentials()}
* instead
*/
@Deprecated
public List<org.springframework.security.saml2.credentials.Saml2X509Credential> getEncryptionCredentials() {
return filterCredentials(org.springframework.security.saml2.credentials.Saml2X509Credential::isEncryptionCredential);
return filterCredentials(
org.springframework.security.saml2.credentials.Saml2X509Credential::isEncryptionCredential);
}
/**
@@ -315,7 +326,8 @@ public class RelyingPartyRegistration {
*/
@Deprecated
public List<org.springframework.security.saml2.credentials.Saml2X509Credential> getDecryptionCredentials() {
return filterCredentials(org.springframework.security.saml2.credentials.Saml2X509Credential::isDecryptionCredential);
return filterCredentials(
org.springframework.security.saml2.credentials.Saml2X509Credential::isDecryptionCredential);
}
private List<org.springframework.security.saml2.credentials.Saml2X509Credential> filterCredentials(
@@ -331,7 +343,8 @@ public class RelyingPartyRegistration {
}
/**
* Creates a {@code RelyingPartyRegistration} {@link Builder} with a known {@code registrationId}
* Creates a {@code RelyingPartyRegistration} {@link Builder} with a known
* {@code registrationId}
* @param registrationId a string identifier for the {@code RelyingPartyRegistration}
* @return {@code Builder} to create a {@code RelyingPartyRegistration} object
*/
@@ -341,48 +354,53 @@ public class RelyingPartyRegistration {
}
/**
* Creates a {@code RelyingPartyRegistration} {@link Builder} based on an existing object
* Creates a {@code RelyingPartyRegistration} {@link Builder} based on an existing
* object
* @param registration the {@code RelyingPartyRegistration}
* @return {@code Builder} to create a {@code RelyingPartyRegistration} object
*/
public static Builder withRelyingPartyRegistration(RelyingPartyRegistration registration) {
Assert.notNull(registration, "registration cannot be null");
return withRegistrationId(registration.getRegistrationId())
.entityId(registration.getEntityId())
return withRegistrationId(registration.getRegistrationId()).entityId(registration.getEntityId())
.signingX509Credentials(c -> c.addAll(registration.getSigningX509Credentials()))
.decryptionX509Credentials(c -> c.addAll(registration.getDecryptionX509Credentials()))
.assertionConsumerServiceLocation(registration.getAssertionConsumerServiceLocation())
.assertionConsumerServiceBinding(registration.getAssertionConsumerServiceBinding())
.assertingPartyDetails(assertingParty -> assertingParty
.entityId(registration.getAssertingPartyDetails().getEntityId())
.wantAuthnRequestsSigned(registration.getAssertingPartyDetails().getWantAuthnRequestsSigned())
.verificationX509Credentials(c -> c.addAll(registration.getAssertingPartyDetails().getVerificationX509Credentials()))
.encryptionX509Credentials(c -> c.addAll(registration.getAssertingPartyDetails().getEncryptionX509Credentials()))
.singleSignOnServiceLocation(registration.getAssertingPartyDetails().getSingleSignOnServiceLocation())
.singleSignOnServiceBinding(registration.getAssertingPartyDetails().getSingleSignOnServiceBinding())
);
.entityId(registration.getAssertingPartyDetails().getEntityId())
.wantAuthnRequestsSigned(registration.getAssertingPartyDetails().getWantAuthnRequestsSigned())
.verificationX509Credentials(
c -> c.addAll(registration.getAssertingPartyDetails().getVerificationX509Credentials()))
.encryptionX509Credentials(
c -> c.addAll(registration.getAssertingPartyDetails().getEncryptionX509Credentials()))
.singleSignOnServiceLocation(
registration.getAssertingPartyDetails().getSingleSignOnServiceLocation())
.singleSignOnServiceBinding(
registration.getAssertingPartyDetails().getSingleSignOnServiceBinding()));
}
/**
* The configuration metadata of the Asserting party
*
* @since 5.4
*/
public final static class AssertingPartyDetails {
private final String entityId;
private final boolean wantAuthnRequestsSigned;
private final Collection<Saml2X509Credential> verificationX509Credentials;
private final Collection<Saml2X509Credential> encryptionX509Credentials;
private final String singleSignOnServiceLocation;
private final Saml2MessageBinding singleSignOnServiceBinding;
private AssertingPartyDetails(
String entityId,
boolean wantAuthnRequestsSigned,
private AssertingPartyDetails(String entityId, boolean wantAuthnRequestsSigned,
Collection<Saml2X509Credential> verificationX509Credentials,
Collection<Saml2X509Credential> encryptionX509Credentials,
String singleSignOnServiceLocation,
Collection<Saml2X509Credential> encryptionX509Credentials, String singleSignOnServiceLocation,
Saml2MessageBinding singleSignOnServiceBinding) {
Assert.hasText(entityId, "entityId cannot be null or empty");
@@ -409,18 +427,17 @@ public class RelyingPartyRegistration {
}
/**
* Get the asserting party's
* <a href="https://wiki.shibboleth.net/confluence/display/CONCEPT/EntityNaming">EntityID</a>.
* Get the asserting party's <a href=
* "https://wiki.shibboleth.net/confluence/display/CONCEPT/EntityNaming">EntityID</a>.
*
* <p>
* Equivalent to the value found in the asserting party's
* &lt;EntityDescriptor EntityID="..."/&gt;
* Equivalent to the value found in the asserting party's &lt;EntityDescriptor
* EntityID="..."/&gt;
*
* <p>
* This value may contain a number of placeholders, which need to be
* resolved before use. They are {@code baseUrl}, {@code registrationId},
* This value may contain a number of placeholders, which need to be resolved
* before use. They are {@code baseUrl}, {@code registrationId},
* {@code baseScheme}, {@code baseHost}, and {@code basePort}.
*
* @return the asserting party's EntityID
*/
public String getEntityId() {
@@ -428,9 +445,8 @@ public class RelyingPartyRegistration {
}
/**
* Get the WantAuthnRequestsSigned setting, indicating the asserting party's preference that
* relying parties should sign the AuthnRequest before sending.
*
* Get the WantAuthnRequestsSigned setting, indicating the asserting party's
* preference that relying parties should sign the AuthnRequest before sending.
* @return the WantAuthnRequestsSigned value
*/
public boolean getWantAuthnRequestsSigned() {
@@ -438,9 +454,10 @@ public class RelyingPartyRegistration {
}
/**
* Get all verification {@link Saml2X509Credential}s associated with this asserting party
*
* @return all verification {@link Saml2X509Credential}s associated with this asserting party
* Get all verification {@link Saml2X509Credential}s associated with this
* asserting party
* @return all verification {@link Saml2X509Credential}s associated with this
* asserting party
* @since 5.4
*/
public Collection<Saml2X509Credential> getVerificationX509Credentials() {
@@ -448,9 +465,10 @@ public class RelyingPartyRegistration {
}
/**
* Get all encryption {@link Saml2X509Credential}s associated with this asserting party
*
* @return all encryption {@link Saml2X509Credential}s associated with this asserting party
* Get all encryption {@link Saml2X509Credential}s associated with this asserting
* party
* @return all encryption {@link Saml2X509Credential}s associated with this
* asserting party
* @since 5.4
*/
public Collection<Saml2X509Credential> getEncryptionX509Credentials() {
@@ -458,14 +476,13 @@ public class RelyingPartyRegistration {
}
/**
* Get the
* <a href="https://wiki.shibboleth.net/confluence/display/CONCEPT/MetadataForIdP#MetadataForIdP-SingleSign-OnServices">SingleSignOnService</a>
* Get the <a href=
* "https://wiki.shibboleth.net/confluence/display/CONCEPT/MetadataForIdP#MetadataForIdP-SingleSign-OnServices">SingleSignOnService</a>
* Location.
*
* <p>
* Equivalent to the value found in &lt;SingleSignOnService Location="..."/&gt;
* in the asserting party's &lt;IDPSSODescriptor&gt;.
*
* Equivalent to the value found in &lt;SingleSignOnService Location="..."/&gt; in
* the asserting party's &lt;IDPSSODescriptor&gt;.
* @return the SingleSignOnService Location
*/
public String getSingleSignOnServiceLocation() {
@@ -473,14 +490,13 @@ public class RelyingPartyRegistration {
}
/**
* Get the
* <a href="https://wiki.shibboleth.net/confluence/display/CONCEPT/MetadataForIdP#MetadataForIdP-SingleSign-OnServices">SingleSignOnService</a>
* Get the <a href=
* "https://wiki.shibboleth.net/confluence/display/CONCEPT/MetadataForIdP#MetadataForIdP-SingleSign-OnServices">SingleSignOnService</a>
* Binding.
*
* <p>
* Equivalent to the value found in &lt;SingleSignOnService Binding="..."/&gt;
* in the asserting party's &lt;IDPSSODescriptor&gt;.
*
* Equivalent to the value found in &lt;SingleSignOnService Binding="..."/&gt; in
* the asserting party's &lt;IDPSSODescriptor&gt;.
* @return the SingleSignOnService Location
*/
public Saml2MessageBinding getSingleSignOnServiceBinding() {
@@ -488,19 +504,24 @@ public class RelyingPartyRegistration {
}
public final static class Builder {
private String entityId;
private boolean wantAuthnRequestsSigned = true;
private Collection<Saml2X509Credential> verificationX509Credentials = new HashSet<>();
private Collection<Saml2X509Credential> encryptionX509Credentials = new HashSet<>();
private String singleSignOnServiceLocation;
private Saml2MessageBinding singleSignOnServiceBinding = Saml2MessageBinding.REDIRECT;
/**
* Set the asserting party's
* <a href="https://wiki.shibboleth.net/confluence/display/CONCEPT/EntityNaming">EntityID</a>.
* Equivalent to the value found in the asserting party's
* &lt;EntityDescriptor EntityID="..."/&gt;
*
* Set the asserting party's <a href=
* "https://wiki.shibboleth.net/confluence/display/CONCEPT/EntityNaming">EntityID</a>.
* Equivalent to the value found in the asserting party's &lt;EntityDescriptor
* EntityID="..."/&gt;
* @param entityId the asserting party's EntityID
* @return the {@link ProviderDetails.Builder} for further configuration
*/
@@ -510,9 +531,9 @@ public class RelyingPartyRegistration {
}
/**
* Set the WantAuthnRequestsSigned setting, indicating the asserting party's preference that
* relying parties should sign the AuthnRequest before sending.
*
* Set the WantAuthnRequestsSigned setting, indicating the asserting party's
* preference that relying parties should sign the AuthnRequest before
* sending.
* @param wantAuthnRequestsSigned the WantAuthnRequestsSigned setting
* @return the {@link ProviderDetails.Builder} for further configuration
*/
@@ -523,9 +544,10 @@ public class RelyingPartyRegistration {
/**
* Apply this {@link Consumer} to the list of {@link Saml2X509Credential}s
*
* @param credentialsConsumer a {@link Consumer} of the {@link List} of {@link Saml2X509Credential}s
* @return the {@link RelyingPartyRegistration.Builder} for further configuration
* @param credentialsConsumer a {@link Consumer} of the {@link List} of
* {@link Saml2X509Credential}s
* @return the {@link RelyingPartyRegistration.Builder} for further
* configuration
* @since 5.4
*/
public Builder verificationX509Credentials(Consumer<Collection<Saml2X509Credential>> credentialsConsumer) {
@@ -535,9 +557,10 @@ public class RelyingPartyRegistration {
/**
* Apply this {@link Consumer} to the list of {@link Saml2X509Credential}s
*
* @param credentialsConsumer a {@link Consumer} of the {@link List} of {@link Saml2X509Credential}s
* @return the {@link RelyingPartyRegistration.Builder} for further configuration
* @param credentialsConsumer a {@link Consumer} of the {@link List} of
* {@link Saml2X509Credential}s
* @return the {@link RelyingPartyRegistration.Builder} for further
* configuration
* @since 5.4
*/
public Builder encryptionX509Credentials(Consumer<Collection<Saml2X509Credential>> credentialsConsumer) {
@@ -546,14 +569,13 @@ public class RelyingPartyRegistration {
}
/**
* Set the
* <a href="https://wiki.shibboleth.net/confluence/display/CONCEPT/MetadataForIdP#MetadataForIdP-SingleSign-OnServices">SingleSignOnService</a>
* Set the <a href=
* "https://wiki.shibboleth.net/confluence/display/CONCEPT/MetadataForIdP#MetadataForIdP-SingleSign-OnServices">SingleSignOnService</a>
* Location.
*
* <p>
* Equivalent to the value found in &lt;SingleSignOnService Location="..."/&gt;
* in the asserting party's &lt;IDPSSODescriptor&gt;.
*
* Equivalent to the value found in &lt;SingleSignOnService
* Location="..."/&gt; in the asserting party's &lt;IDPSSODescriptor&gt;.
* @param singleSignOnServiceLocation the SingleSignOnService Location
* @return the {@link ProviderDetails.Builder} for further configuration
*/
@@ -563,14 +585,13 @@ public class RelyingPartyRegistration {
}
/**
* Set the
* <a href="https://wiki.shibboleth.net/confluence/display/CONCEPT/MetadataForIdP#MetadataForIdP-SingleSign-OnServices">SingleSignOnService</a>
* Set the <a href=
* "https://wiki.shibboleth.net/confluence/display/CONCEPT/MetadataForIdP#MetadataForIdP-SingleSign-OnServices">SingleSignOnService</a>
* Binding.
*
* <p>
* Equivalent to the value found in &lt;SingleSignOnService Binding="..."/&gt;
* in the asserting party's &lt;IDPSSODescriptor&gt;.
*
* @param singleSignOnServiceBinding the SingleSignOnService Binding
* @return the {@link ProviderDetails.Builder} for further configuration
*/
@@ -580,29 +601,29 @@ public class RelyingPartyRegistration {
}
/**
* Creates an immutable ProviderDetails object representing the configuration for an Identity Provider, IDP
* Creates an immutable ProviderDetails object representing the configuration
* for an Identity Provider, IDP
* @return immutable ProviderDetails object
*/
public AssertingPartyDetails build() {
return new AssertingPartyDetails(
this.entityId,
this.wantAuthnRequestsSigned,
this.verificationX509Credentials,
this.encryptionX509Credentials,
this.singleSignOnServiceLocation,
this.singleSignOnServiceBinding
);
return new AssertingPartyDetails(this.entityId, this.wantAuthnRequestsSigned,
this.verificationX509Credentials, this.encryptionX509Credentials,
this.singleSignOnServiceLocation, this.singleSignOnServiceBinding);
}
}
}
/**
* Configuration for IDP SSO endpoint configuration
*
* @since 5.3
* @deprecated Use {@link AssertingPartyDetails} instead
*/
@Deprecated
public final static class ProviderDetails {
private final AssertingPartyDetails assertingPartyDetails;
private ProviderDetails(AssertingPartyDetails assertingPartyDetails) {
@@ -619,17 +640,18 @@ public class RelyingPartyRegistration {
}
/**
* Contains the URL for which to send the SAML 2 Authentication Request to initiate
* a single sign on flow.
* @return a IDP URL that accepts REDIRECT or POST binding for authentication requests
* Contains the URL for which to send the SAML 2 Authentication Request to
* initiate a single sign on flow.
* @return a IDP URL that accepts REDIRECT or POST binding for authentication
* requests
*/
public String getWebSsoUrl() {
return this.assertingPartyDetails.getSingleSignOnServiceLocation();
}
/**
* @return {@code true} if AuthNRequests from this relying party to the IDP should be signed
* {@code false} if no signature is required.
* @return {@code true} if AuthNRequests from this relying party to the IDP should
* be signed {@code false} if no signature is required.
*/
public boolean isSignAuthNRequest() {
return this.assertingPartyDetails.getWantAuthnRequestsSigned();
@@ -644,20 +666,20 @@ public class RelyingPartyRegistration {
/**
* Builder for IDP SSO endpoint configuration
*
* @since 5.3
* @deprecated Use {@link AssertingPartyDetails.Builder} instead
*/
@Deprecated
public final static class Builder {
private final AssertingPartyDetails.Builder assertingPartyDetailsBuilder =
new AssertingPartyDetails.Builder();
private final AssertingPartyDetails.Builder assertingPartyDetailsBuilder = new AssertingPartyDetails.Builder();
/**
* Set the asserting party's
* <a href="https://wiki.shibboleth.net/confluence/display/CONCEPT/EntityNaming">EntityID</a>.
* Equivalent to the value found in the asserting party's
* &lt;EntityDescriptor EntityID="..."/&gt;
*
* Set the asserting party's <a href=
* "https://wiki.shibboleth.net/confluence/display/CONCEPT/EntityNaming">EntityID</a>.
* Equivalent to the value found in the asserting party's &lt;EntityDescriptor
* EntityID="..."/&gt;
* @param entityId the asserting party's EntityID
* @return the {@link Builder} for further configuration
* @since 5.4
@@ -668,9 +690,10 @@ public class RelyingPartyRegistration {
}
/**
* Sets the {@code SSO URL} for the remote asserting party, the Identity Provider.
*
* @param url - a URL that accepts authentication requests via REDIRECT or POST bindings
* Sets the {@code SSO URL} for the remote asserting party, the Identity
* Provider.
* @param url - a URL that accepts authentication requests via REDIRECT or
* POST bindings
* @return this object
*/
public Builder webSsoUrl(String url) {
@@ -680,7 +703,6 @@ public class RelyingPartyRegistration {
/**
* Set to true if the AuthNRequest message should be signed
*
* @param signAuthNRequest true if the message should be signed
* @return this object
*/
@@ -689,11 +711,10 @@ public class RelyingPartyRegistration {
return this;
}
/**
* Sets the message binding to be used when sending an AuthNRequest message
*
* @param binding either {@link Saml2MessageBinding#POST} or {@link Saml2MessageBinding#REDIRECT}
* @param binding either {@link Saml2MessageBinding#POST} or
* {@link Saml2MessageBinding#REDIRECT}
* @return this object
*/
public Builder binding(Saml2MessageBinding binding) {
@@ -702,30 +723,41 @@ public class RelyingPartyRegistration {
}
/**
* Creates an immutable ProviderDetails object representing the configuration for an Identity Provider, IDP
* Creates an immutable ProviderDetails object representing the configuration
* for an Identity Provider, IDP
* @return immutable ProviderDetails object
*/
public ProviderDetails build() {
return new ProviderDetails(this.assertingPartyDetailsBuilder.build());
}
}
}
public final static class Builder {
private String registrationId;
private String entityId = "{baseUrl}/saml2/service-provider-metadata/{registrationId}";
private Collection<Saml2X509Credential> signingX509Credentials = new HashSet<>();
private Collection<Saml2X509Credential> decryptionX509Credentials = new HashSet<>();
private String assertionConsumerServiceLocation = "{baseUrl}" + Saml2WebSsoAuthenticationFilter.DEFAULT_FILTER_PROCESSES_URI;
private String assertionConsumerServiceLocation = "{baseUrl}"
+ Saml2WebSsoAuthenticationFilter.DEFAULT_FILTER_PROCESSES_URI;
private Saml2MessageBinding assertionConsumerServiceBinding = Saml2MessageBinding.POST;
private ProviderDetails.Builder providerDetails = new ProviderDetails.Builder();
private Collection<org.springframework.security.saml2.credentials.Saml2X509Credential> credentials = new HashSet<>();
private Builder(String registrationId) {
this.registrationId = registrationId;
}
/**
* Sets the {@code registrationId} template. Often be used in URL paths
* @param id registrationId for this object, should be unique
@@ -737,15 +769,14 @@ public class RelyingPartyRegistration {
}
/**
* Set the relying party's
* <a href="https://wiki.shibboleth.net/confluence/display/CONCEPT/EntityNaming">EntityID</a>.
* Equivalent to the value found in the relying party's
* &lt;EntityDescriptor EntityID="..."/&gt;
*
* This value may contain a number of placeholders.
* They are {@code baseUrl}, {@code registrationId},
* {@code baseScheme}, {@code baseHost}, and {@code basePort}.
* Set the relying party's <a href=
* "https://wiki.shibboleth.net/confluence/display/CONCEPT/EntityNaming">EntityID</a>.
* Equivalent to the value found in the relying party's &lt;EntityDescriptor
* EntityID="..."/&gt;
*
* This value may contain a number of placeholders. They are {@code baseUrl},
* {@code registrationId}, {@code baseScheme}, {@code baseHost}, and
* {@code basePort}.
* @return the {@link Builder} for further configuration
* @since 5.4
*/
@@ -755,10 +786,11 @@ public class RelyingPartyRegistration {
}
/**
* Apply this {@link Consumer} to the {@link Collection} of {@link Saml2X509Credential}s
* for the purposes of modifying the {@link Collection}
*
* @param credentialsConsumer - the {@link Consumer} for modifying the {@link Collection}
* Apply this {@link Consumer} to the {@link Collection} of
* {@link Saml2X509Credential}s for the purposes of modifying the
* {@link Collection}
* @param credentialsConsumer - the {@link Consumer} for modifying the
* {@link Collection}
* @return the {@link Builder} for further configuration
* @since 5.4
*/
@@ -768,10 +800,11 @@ public class RelyingPartyRegistration {
}
/**
* Apply this {@link Consumer} to the {@link Collection} of {@link Saml2X509Credential}s
* for the purposes of modifying the {@link Collection}
*
* @param credentialsConsumer - the {@link Consumer} for modifying the {@link Collection}
* Apply this {@link Consumer} to the {@link Collection} of
* {@link Saml2X509Credential}s for the purposes of modifying the
* {@link Collection}
* @param credentialsConsumer - the {@link Consumer} for modifying the
* {@link Collection}
* @return the {@link Builder} for further configuration
* @since 5.4
*/
@@ -781,18 +814,18 @@ public class RelyingPartyRegistration {
}
/**
* Set the <a href="https://wiki.shibboleth.net/confluence/display/CONCEPT/AssertionConsumerService">AssertionConsumerService</a>
* Set the <a href=
* "https://wiki.shibboleth.net/confluence/display/CONCEPT/AssertionConsumerService">AssertionConsumerService</a>
* Location.
*
* <p>
* Equivalent to the value found in &lt;AssertionConsumerService Location="..."/&gt;
* in the relying party's &lt;SPSSODescriptor&gt;
* Equivalent to the value found in &lt;AssertionConsumerService
* Location="..."/&gt; in the relying party's &lt;SPSSODescriptor&gt;
*
* <p>
* This value may contain a number of placeholders.
* They are {@code baseUrl}, {@code registrationId},
* {@code baseScheme}, {@code baseHost}, and {@code basePort}.
*
* This value may contain a number of placeholders. They are {@code baseUrl},
* {@code registrationId}, {@code baseScheme}, {@code baseHost}, and
* {@code basePort}.
* @param assertionConsumerServiceLocation
* @return the {@link Builder} for further configuration
* @since 5.4
@@ -803,13 +836,13 @@ public class RelyingPartyRegistration {
}
/**
* Set the <a href="https://wiki.shibboleth.net/confluence/display/CONCEPT/AssertionConsumerService">AssertionConsumerService</a>
* Set the <a href=
* "https://wiki.shibboleth.net/confluence/display/CONCEPT/AssertionConsumerService">AssertionConsumerService</a>
* Binding.
*
* <p>
* Equivalent to the value found in &lt;AssertionConsumerService Binding="..."/&gt;
* in the relying party's &lt;SPSSODescriptor&gt;
*
* Equivalent to the value found in &lt;AssertionConsumerService
* Binding="..."/&gt; in the relying party's &lt;SPSSODescriptor&gt;
* @param assertionConsumerServiceBinding
* @return the {@link Builder} for further configuration
* @since 5.4
@@ -821,7 +854,6 @@ public class RelyingPartyRegistration {
/**
* Apply this {@link Consumer} to further configure the Asserting Party details
*
* @param assertingPartyDetails The {@link Consumer} to apply
* @return the {@link Builder} for further configuration
* @since 5.4
@@ -832,10 +864,8 @@ public class RelyingPartyRegistration {
}
/**
* Modifies the collection of {@link Saml2X509Credential} objects
* used in communication between IDP and SP
* For example:
* <code>
* Modifies the collection of {@link Saml2X509Credential} objects used in
* communication between IDP and SP For example: <code>
* Saml2X509Credential credential = ...;
* return RelyingPartyRegistration.withRegistrationId("id")
* .credentials(c -> c.add(credential))
@@ -844,22 +874,27 @@ public class RelyingPartyRegistration {
* </code>
* @param credentials - a consumer that can modify the collection of credentials
* @return this object
* @deprecated Use {@link #signingX509Credentials} or {@link #decryptionX509Credentials} instead
* for relying party keys or {@link AssertingPartyDetails.Builder#verificationX509Credentials} or
* {@link AssertingPartyDetails.Builder#encryptionX509Credentials} for asserting party keys
* @deprecated Use {@link #signingX509Credentials} or
* {@link #decryptionX509Credentials} instead for relying party keys or
* {@link AssertingPartyDetails.Builder#verificationX509Credentials} or
* {@link AssertingPartyDetails.Builder#encryptionX509Credentials} for asserting
* party keys
*/
@Deprecated
public Builder credentials(Consumer<Collection<org.springframework.security.saml2.credentials.Saml2X509Credential>> credentials) {
public Builder credentials(
Consumer<Collection<org.springframework.security.saml2.credentials.Saml2X509Credential>> credentials) {
credentials.accept(this.credentials);
return this;
}
/**
* <a href="https://wiki.shibboleth.net/confluence/display/CONCEPT/AssertionConsumerService">Assertion Consumer
* Service</a> URL template. It can contain variables {@code baseUrl}, {@code registrationId},
* {@code baseScheme}, {@code baseHost}, and {@code basePort}.
* @param assertionConsumerServiceUrlTemplate the Assertion Consumer Service URL template (i.e.
* "{baseUrl}/login/saml2/sso/{registrationId}".
* <a href=
* "https://wiki.shibboleth.net/confluence/display/CONCEPT/AssertionConsumerService">Assertion
* Consumer Service</a> URL template. It can contain variables {@code baseUrl},
* {@code registrationId}, {@code baseScheme}, {@code baseHost}, and
* {@code basePort}.
* @param assertionConsumerServiceUrlTemplate the Assertion Consumer Service URL
* template (i.e. "{baseUrl}/login/saml2/sso/{registrationId}".
* @return this object
* @deprecated Use {@link #assertionConsumerServiceLocation} instead.
*/
@@ -870,10 +905,12 @@ public class RelyingPartyRegistration {
}
/**
* Sets the {@code entityId} for the remote asserting party, the Identity Provider.
* Sets the {@code entityId} for the remote asserting party, the Identity
* Provider.
* @param entityId the IDP entityId
* @return this object
* @deprecated use {@link #assertingPartyDetails(Consumer< AssertingPartyDetails.Builder >)}
* @deprecated use {@link #assertingPartyDetails(Consumer<
* AssertingPartyDetails.Builder >)}
*/
@Deprecated
public Builder remoteIdpEntityId(String entityId) {
@@ -883,9 +920,11 @@ public class RelyingPartyRegistration {
/**
* Sets the {@code SSO URL} for the remote asserting party, the Identity Provider.
* @param url - a URL that accepts authentication requests via REDIRECT or POST bindings
* @param url - a URL that accepts authentication requests via REDIRECT or POST
* bindings
* @return this object
* @deprecated use {@link #assertingPartyDetails(Consumer< AssertingPartyDetails.Builder >)}
* @deprecated use {@link #assertingPartyDetails(Consumer<
* AssertingPartyDetails.Builder >)}
*/
@Deprecated
public Builder idpWebSsoUrl(String url) {
@@ -894,9 +933,10 @@ public class RelyingPartyRegistration {
}
/**
* Sets the local relying party, or Service Provider, entity Id template.
* can generate it's entity ID based on possible variables of {@code baseUrl}, {@code registrationId},
* {@code baseScheme}, {@code baseHost}, and {@code basePort}, for example
* Sets the local relying party, or Service Provider, entity Id template. can
* generate it's entity ID based on possible variables of {@code baseUrl},
* {@code registrationId}, {@code baseScheme}, {@code baseHost}, and
* {@code basePort}, for example
* {@code {baseUrl}/saml2/service-provider-metadata/{registrationId}}
* @return a string containing the entity ID or entity ID template
* @deprecated Use {@link #entityId} instead
@@ -920,7 +960,8 @@ public class RelyingPartyRegistration {
}
/**
* Constructs a RelyingPartyRegistration object based on the builder configurations
* Constructs a RelyingPartyRegistration object based on the builder
* configurations
* @return a RelyingPartyRegistration instance
*/
public RelyingPartyRegistration build() {
@@ -933,12 +974,10 @@ public class RelyingPartyRegistration {
decryptionX509Credentials(c -> c.add(mapped));
}
if (credential.isSignatureVerficationCredential()) {
this.providerDetails.assertingPartyDetailsBuilder
.verificationX509Credentials(c -> c.add(mapped));
this.providerDetails.assertingPartyDetailsBuilder.verificationX509Credentials(c -> c.add(mapped));
}
if (credential.isEncryptionCredential()) {
this.providerDetails.assertingPartyDetailsBuilder
.encryptionX509Credentials(c -> c.add(mapped));
this.providerDetails.assertingPartyDetailsBuilder.encryptionX509Credentials(c -> c.add(mapped));
}
}
@@ -955,20 +994,16 @@ public class RelyingPartyRegistration {
this.credentials.add(toDeprecated(credential));
}
return new RelyingPartyRegistration(
this.registrationId,
this.entityId,
this.assertionConsumerServiceLocation,
this.assertionConsumerServiceBinding,
this.providerDetails.build(),
this.credentials,
this.decryptionX509Credentials,
this.signingX509Credentials
);
return new RelyingPartyRegistration(this.registrationId, this.entityId,
this.assertionConsumerServiceLocation, this.assertionConsumerServiceBinding,
this.providerDetails.build(), this.credentials, this.decryptionX509Credentials,
this.signingX509Credentials);
}
}
private static Saml2X509Credential fromDeprecated(org.springframework.security.saml2.credentials.Saml2X509Credential credential) {
private static Saml2X509Credential fromDeprecated(
org.springframework.security.saml2.credentials.Saml2X509Credential credential) {
PrivateKey privateKey = credential.getPrivateKey();
X509Certificate certificate = credential.getCertificate();
Set<Saml2X509Credential.Saml2X509CredentialType> credentialTypes = new HashSet<>();
@@ -987,22 +1022,29 @@ public class RelyingPartyRegistration {
return new Saml2X509Credential(privateKey, certificate, credentialTypes);
}
private static org.springframework.security.saml2.credentials.Saml2X509Credential toDeprecated(Saml2X509Credential credential) {
private static org.springframework.security.saml2.credentials.Saml2X509Credential toDeprecated(
Saml2X509Credential credential) {
PrivateKey privateKey = credential.getPrivateKey();
X509Certificate certificate = credential.getCertificate();
Set<org.springframework.security.saml2.credentials.Saml2X509Credential.Saml2X509CredentialType> credentialTypes = new HashSet<>();
if (credential.isSigningCredential()) {
credentialTypes.add(org.springframework.security.saml2.credentials.Saml2X509Credential.Saml2X509CredentialType.SIGNING);
credentialTypes.add(
org.springframework.security.saml2.credentials.Saml2X509Credential.Saml2X509CredentialType.SIGNING);
}
if (credential.isVerificationCredential()) {
credentialTypes.add(org.springframework.security.saml2.credentials.Saml2X509Credential.Saml2X509CredentialType.VERIFICATION);
credentialTypes.add(
org.springframework.security.saml2.credentials.Saml2X509Credential.Saml2X509CredentialType.VERIFICATION);
}
if (credential.isEncryptionCredential()) {
credentialTypes.add(org.springframework.security.saml2.credentials.Saml2X509Credential.Saml2X509CredentialType.ENCRYPTION);
credentialTypes.add(
org.springframework.security.saml2.credentials.Saml2X509Credential.Saml2X509CredentialType.ENCRYPTION);
}
if (credential.isDecryptionCredential()) {
credentialTypes.add(org.springframework.security.saml2.credentials.Saml2X509Credential.Saml2X509CredentialType.DECRYPTION);
credentialTypes.add(
org.springframework.security.saml2.credentials.Saml2X509Credential.Saml2X509CredentialType.DECRYPTION);
}
return new org.springframework.security.saml2.credentials.Saml2X509Credential(privateKey, certificate, credentialTypes);
return new org.springframework.security.saml2.credentials.Saml2X509Credential(privateKey, certificate,
credentialTypes);
}
}
@@ -25,9 +25,8 @@ package org.springframework.security.saml2.provider.service.registration;
public interface RelyingPartyRegistrationRepository {
/**
* Returns the relying party registration identified by the provided {@code registrationId},
* or {@code null} if not found.
*
* Returns the relying party registration identified by the provided
* {@code registrationId}, or {@code null} if not found.
* @param registrationId the registration identifier
* @return the {@link RelyingPartyRegistration} if found, otherwise {@code null}
*/
@@ -30,12 +30,13 @@ import org.springframework.web.client.RestTemplate;
* @since 5.4
*/
public final class RelyingPartyRegistrations {
private static final RestOperations rest = new RestTemplate
(Arrays.asList(new OpenSamlRelyingPartyRegistrationBuilderHttpMessageConverter()));
private static final RestOperations rest = new RestTemplate(
Arrays.asList(new OpenSamlRelyingPartyRegistrationBuilderHttpMessageConverter()));
/**
* Return a {@link RelyingPartyRegistration.Builder} based off of the given
* SAML 2.0 Asserting Party (IDP) metadata.
* Return a {@link RelyingPartyRegistration.Builder} based off of the given SAML 2.0
* Asserting Party (IDP) metadata.
*
* Note that by default the registrationId is set to be the given metadata location,
* but this will most often not be sufficient. To complete the configuration, most
@@ -48,21 +49,23 @@ public final class RelyingPartyRegistrations {
* .build();
* </pre>
*
* Also note that an {@code IDPSSODescriptor} typically only contains information about
* the asserting party. Thus, you will need to remember to still populate anything about the
* relying party, like any private keys the relying party will use for signing AuthnRequests.
*
* Also note that an {@code IDPSSODescriptor} typically only contains information
* about the asserting party. Thus, you will need to remember to still populate
* anything about the relying party, like any private keys the relying party will use
* for signing AuthnRequests.
* @param metadataLocation
* @return the {@link RelyingPartyRegistration.Builder} for further configuration
*/
public static RelyingPartyRegistration.Builder fromMetadataLocation(String metadataLocation) {
try {
return rest.getForObject(metadataLocation, RelyingPartyRegistration.Builder.class);
} catch (RestClientException e) {
}
catch (RestClientException e) {
if (e.getCause() instanceof Saml2Exception) {
throw (Saml2Exception) e.getCause();
}
throw new Saml2Exception(e);
}
}
}
@@ -17,17 +17,17 @@
package org.springframework.security.saml2.provider.service.registration;
/**
* The type of bindings that messages are exchanged using
* Supported bindings are {@code urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST}
* and {@code urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect}.
* In addition there is support for {@code urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect}
* with an XML signature in the message rather than query parameters.
* The type of bindings that messages are exchanged using Supported bindings are
* {@code urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST} and
* {@code urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect}. In addition there is
* support for {@code urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect} with an XML
* signature in the message rather than query parameters.
* @since 5.3
*/
public enum Saml2MessageBinding {
POST("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"),
REDIRECT("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect");
POST("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"), REDIRECT(
"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect");
private final String urn;
@@ -42,4 +42,5 @@ public enum Saml2MessageBinding {
public String getUrn() {
return urn;
}
}
@@ -40,12 +40,14 @@ import static org.springframework.util.StringUtils.hasText;
public class Saml2WebSsoAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
public static final String DEFAULT_FILTER_PROCESSES_URI = "/login/saml2/sso/{registrationId}";
private final AuthenticationConverter authenticationConverter;
/**
* Creates a {@code Saml2WebSsoAuthenticationFilter} authentication filter that is configured
* to use the {@link #DEFAULT_FILTER_PROCESSES_URI} processing URL
* @param relyingPartyRegistrationRepository - repository of configured SAML 2 entities. Required.
* Creates a {@code Saml2WebSsoAuthenticationFilter} authentication filter that is
* configured to use the {@link #DEFAULT_FILTER_PROCESSES_URI} processing URL
* @param relyingPartyRegistrationRepository - repository of configured SAML 2
* entities. Required.
*/
public Saml2WebSsoAuthenticationFilter(RelyingPartyRegistrationRepository relyingPartyRegistrationRepository) {
this(relyingPartyRegistrationRepository, DEFAULT_FILTER_PROCESSES_URI);
@@ -53,35 +55,32 @@ public class Saml2WebSsoAuthenticationFilter extends AbstractAuthenticationProce
/**
* Creates a {@code Saml2WebSsoAuthenticationFilter} authentication filter
* @param relyingPartyRegistrationRepository - repository of configured SAML 2 entities. Required.
* @param filterProcessesUrl the processing URL, must contain a {registrationId} variable. Required.
* @param relyingPartyRegistrationRepository - repository of configured SAML 2
* entities. Required.
* @param filterProcessesUrl the processing URL, must contain a {registrationId}
* variable. Required.
*/
public Saml2WebSsoAuthenticationFilter(
RelyingPartyRegistrationRepository relyingPartyRegistrationRepository,
public Saml2WebSsoAuthenticationFilter(RelyingPartyRegistrationRepository relyingPartyRegistrationRepository,
String filterProcessesUrl) {
this(new Saml2AuthenticationTokenConverter
(new DefaultRelyingPartyRegistrationResolver(relyingPartyRegistrationRepository)),
filterProcessesUrl);
this(new Saml2AuthenticationTokenConverter(
new DefaultRelyingPartyRegistrationResolver(relyingPartyRegistrationRepository)), filterProcessesUrl);
}
/**
* Creates a {@link Saml2WebSsoAuthenticationFilter} given the provided parameters
*
* @param authenticationConverter the strategy for converting an {@link HttpServletRequest}
* into an {@link Authentication}
* @param filterProcessingUrl the processing URL, must contain a {registrationId} variable
* @param authenticationConverter the strategy for converting an
* {@link HttpServletRequest} into an {@link Authentication}
* @param filterProcessingUrl the processing URL, must contain a {registrationId}
* variable
* @since 5.4
*/
public Saml2WebSsoAuthenticationFilter(
AuthenticationConverter authenticationConverter,
public Saml2WebSsoAuthenticationFilter(AuthenticationConverter authenticationConverter,
String filterProcessingUrl) {
super(filterProcessingUrl);
Assert.notNull(authenticationConverter, "authenticationConverter cannot be null");
Assert.hasText(filterProcessingUrl, "filterProcessesUrl must contain a URL pattern");
Assert.isTrue(
filterProcessingUrl.contains("{registrationId}"),
"filterProcessesUrl must contain a {registrationId} match variable"
);
Assert.isTrue(filterProcessingUrl.contains("{registrationId}"),
"filterProcessesUrl must contain a {registrationId} match variable");
this.authenticationConverter = authenticationConverter;
setAllowSessionCreation(true);
setSessionAuthenticationStrategy(new ChangeSessionIdAuthenticationStrategy());
@@ -103,4 +102,5 @@ public class Saml2WebSsoAuthenticationFilter extends AbstractAuthenticationProce
}
return getAuthenticationManager().authenticate(authentication);
}
}
@@ -47,22 +47,22 @@ import static java.nio.charset.StandardCharsets.ISO_8859_1;
/**
* This {@code Filter} formulates a
* <a href="https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf">SAML 2.0 AuthnRequest</a> (line 1968)
* and redirects to a configured asserting party.
* <a href="https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf">SAML 2.0
* AuthnRequest</a> (line 1968) and redirects to a configured asserting party.
*
* <p>
* It supports the
* <a href="https://docs.oasis-open.org/security/saml/v2.0/saml-bindings-2.0-os.pdf">HTTP-Redirect</a> (line 520)
* and
* <a href="https://docs.oasis-open.org/security/saml/v2.0/saml-bindings-2.0-os.pdf">HTTP-POST</a> (line 753)
* bindings.
* It supports the <a href=
* "https://docs.oasis-open.org/security/saml/v2.0/saml-bindings-2.0-os.pdf">HTTP-Redirect</a>
* (line 520) and <a href=
* "https://docs.oasis-open.org/security/saml/v2.0/saml-bindings-2.0-os.pdf">HTTP-POST</a>
* (line 753) bindings.
*
* <p>
* By default, this {@code Filter} responds to authentication requests
* at the {@code URI} {@code /oauth2/authorization/{registrationId}}.
* The {@code URI} template variable {@code {registrationId}} represents the
* {@link RelyingPartyRegistration#getRegistrationId() registration identifier} of the relying party
* that is used for initiating the authentication request.
* By default, this {@code Filter} responds to authentication requests at the {@code URI}
* {@code /oauth2/authorization/{registrationId}}. The {@code URI} template variable
* {@code {registrationId}} represents the
* {@link RelyingPartyRegistration#getRegistrationId() registration identifier} of the
* relying party that is used for initiating the authentication request.
*
* @since 5.2
* @author Filip Hanik
@@ -71,27 +71,32 @@ import static java.nio.charset.StandardCharsets.ISO_8859_1;
public class Saml2WebSsoAuthenticationRequestFilter extends OncePerRequestFilter {
private final Saml2AuthenticationRequestContextResolver authenticationRequestContextResolver;
private Saml2AuthenticationRequestFactory authenticationRequestFactory;
private RequestMatcher redirectMatcher = new AntPathRequestMatcher("/saml2/authenticate/{registrationId}");
/**
* Construct a {@link Saml2WebSsoAuthenticationRequestFilter} with the provided parameters
*
* @param relyingPartyRegistrationRepository a repository for relying party configurations
* @deprecated use the constructor that takes a {@link Saml2AuthenticationRequestFactory}
* Construct a {@link Saml2WebSsoAuthenticationRequestFilter} with the provided
* parameters
* @param relyingPartyRegistrationRepository a repository for relying party
* configurations
* @deprecated use the constructor that takes a
* {@link Saml2AuthenticationRequestFactory}
*/
@Deprecated
public Saml2WebSsoAuthenticationRequestFilter(RelyingPartyRegistrationRepository relyingPartyRegistrationRepository) {
public Saml2WebSsoAuthenticationRequestFilter(
RelyingPartyRegistrationRepository relyingPartyRegistrationRepository) {
this(new DefaultSaml2AuthenticationRequestContextResolver(
new DefaultRelyingPartyRegistrationResolver(relyingPartyRegistrationRepository)),
new org.springframework.security.saml2.provider.service.authentication.OpenSamlAuthenticationRequestFactory());
}
/**
* Construct a {@link Saml2WebSsoAuthenticationRequestFilter} with the provided parameters
*
* @param authenticationRequestContextResolver a strategy for formulating a {@link Saml2AuthenticationRequestContext}
* Construct a {@link Saml2WebSsoAuthenticationRequestFilter} with the provided
* parameters
* @param authenticationRequestContextResolver a strategy for formulating a
* {@link Saml2AuthenticationRequestContext}
* @since 5.4
*/
public Saml2WebSsoAuthenticationRequestFilter(
@@ -105,9 +110,10 @@ public class Saml2WebSsoAuthenticationRequestFilter extends OncePerRequestFilter
}
/**
* Use the given {@link Saml2AuthenticationRequestFactory} for formulating the SAML 2.0 AuthnRequest
*
* @param authenticationRequestFactory the {@link Saml2AuthenticationRequestFactory} to use
* Use the given {@link Saml2AuthenticationRequestFactory} for formulating the SAML
* 2.0 AuthnRequest
* @param authenticationRequestFactory the {@link Saml2AuthenticationRequestFactory}
* to use
* @deprecated use the constructor instead
*/
@Deprecated
@@ -118,7 +124,6 @@ public class Saml2WebSsoAuthenticationRequestFilter extends OncePerRequestFilter
/**
* Use the given {@link RequestMatcher} that activates this filter for a given request
*
* @param redirectMatcher the {@link RequestMatcher} to use
*/
public void setRedirectMatcher(RequestMatcher redirectMatcher) {
@@ -147,41 +152,36 @@ public class Saml2WebSsoAuthenticationRequestFilter extends OncePerRequestFilter
RelyingPartyRegistration relyingParty = context.getRelyingPartyRegistration();
if (relyingParty.getAssertingPartyDetails().getSingleSignOnServiceBinding() == Saml2MessageBinding.REDIRECT) {
sendRedirect(response, context);
} else {
}
else {
sendPost(response, context);
}
}
private void sendRedirect(HttpServletResponse response, Saml2AuthenticationRequestContext context)
throws IOException {
Saml2RedirectAuthenticationRequest authenticationRequest =
this.authenticationRequestFactory.createRedirectAuthenticationRequest(context);
Saml2RedirectAuthenticationRequest authenticationRequest = this.authenticationRequestFactory
.createRedirectAuthenticationRequest(context);
UriComponentsBuilder uriBuilder = UriComponentsBuilder
.fromUriString(authenticationRequest.getAuthenticationRequestUri());
addParameter("SAMLRequest", authenticationRequest.getSamlRequest(), uriBuilder);
addParameter("RelayState", authenticationRequest.getRelayState(), uriBuilder);
addParameter("SigAlg", authenticationRequest.getSigAlg(), uriBuilder);
addParameter("Signature", authenticationRequest.getSignature(), uriBuilder);
String redirectUrl = uriBuilder
.build(true)
.toUriString();
String redirectUrl = uriBuilder.build(true).toUriString();
response.sendRedirect(redirectUrl);
}
private void addParameter(String name, String value, UriComponentsBuilder builder) {
Assert.hasText(name, "name cannot be empty or null");
if (StringUtils.hasText(value)) {
builder.queryParam(
UriUtils.encode(name, ISO_8859_1),
UriUtils.encode(value, ISO_8859_1)
);
builder.queryParam(UriUtils.encode(name, ISO_8859_1), UriUtils.encode(value, ISO_8859_1));
}
}
private void sendPost(HttpServletResponse response, Saml2AuthenticationRequestContext context)
throws IOException {
Saml2PostAuthenticationRequest authenticationRequest =
this.authenticationRequestFactory.createPostAuthenticationRequest(context);
private void sendPost(HttpServletResponse response, Saml2AuthenticationRequestContext context) throws IOException {
Saml2PostAuthenticationRequest authenticationRequest = this.authenticationRequestFactory
.createPostAuthenticationRequest(context);
String html = createSamlPostRequestFormData(authenticationRequest);
response.setContentType(MediaType.TEXT_HTML_VALUE);
response.getWriter().write(html);
@@ -191,42 +191,26 @@ public class Saml2WebSsoAuthenticationRequestFilter extends OncePerRequestFilter
String authenticationRequestUri = authenticationRequest.getAuthenticationRequestUri();
String relayState = authenticationRequest.getRelayState();
String samlRequest = authenticationRequest.getSamlRequest();
StringBuilder postHtml = new StringBuilder()
.append("<!DOCTYPE html>\n")
.append("<html>\n")
.append(" <head>\n")
.append(" <meta charset=\"utf-8\" />\n")
.append(" </head>\n")
.append(" <body onload=\"document.forms[0].submit()\">\n")
.append(" <noscript>\n")
StringBuilder postHtml = new StringBuilder().append("<!DOCTYPE html>\n").append("<html>\n")
.append(" <head>\n").append(" <meta charset=\"utf-8\" />\n").append(" </head>\n")
.append(" <body onload=\"document.forms[0].submit()\">\n").append(" <noscript>\n")
.append(" <p>\n")
.append(" <strong>Note:</strong> Since your browser does not support JavaScript,\n")
.append(" you must press the Continue button once to proceed.\n")
.append(" </p>\n")
.append(" </noscript>\n")
.append(" \n")
.append(" </p>\n").append(" </noscript>\n").append(" \n")
.append(" <form action=\"").append(authenticationRequestUri).append("\" method=\"post\">\n")
.append(" <div>\n")
.append(" <input type=\"hidden\" name=\"SAMLRequest\" value=\"")
.append(HtmlUtils.htmlEscape(samlRequest))
.append("\"/>\n");
.append(HtmlUtils.htmlEscape(samlRequest)).append("\"/>\n");
if (StringUtils.hasText(relayState)) {
postHtml
.append(" <input type=\"hidden\" name=\"RelayState\" value=\"")
.append(HtmlUtils.htmlEscape(relayState))
.append("\"/>\n");
postHtml.append(" <input type=\"hidden\" name=\"RelayState\" value=\"")
.append(HtmlUtils.htmlEscape(relayState)).append("\"/>\n");
}
postHtml
.append(" </div>\n")
.append(" <noscript>\n")
.append(" <div>\n")
postHtml.append(" </div>\n").append(" <noscript>\n").append(" <div>\n")
.append(" <input type=\"submit\" value=\"Continue\"/>\n")
.append(" </div>\n")
.append(" </noscript>\n")
.append(" </form>\n")
.append(" \n")
.append(" </body>\n")
.append("</html>");
.append(" </div>\n").append(" </noscript>\n").append(" </form>\n")
.append(" \n").append(" </body>\n").append("</html>");
return postHtml.toString();
}
}
@@ -37,8 +37,8 @@ import static org.springframework.web.util.UriComponentsBuilder.fromHttpUrl;
/**
* A {@link Converter} that resolves a {@link RelyingPartyRegistration} by extracting the
* registration id from the request, querying a {@link RelyingPartyRegistrationRepository},
* and resolving any template values.
* registration id from the request, querying a
* {@link RelyingPartyRegistrationRepository}, and resolving any template values.
*
* @since 5.4
* @author Josh Cummings
@@ -49,10 +49,11 @@ public final class DefaultRelyingPartyRegistrationResolver
private static final char PATH_DELIMITER = '/';
private final RelyingPartyRegistrationRepository relyingPartyRegistrationRepository;
private final Converter<HttpServletRequest, String> registrationIdResolver = new RegistrationIdResolver();
public DefaultRelyingPartyRegistrationResolver
(RelyingPartyRegistrationRepository relyingPartyRegistrationRepository) {
public DefaultRelyingPartyRegistrationResolver(
RelyingPartyRegistrationRepository relyingPartyRegistrationRepository) {
Assert.notNull(relyingPartyRegistrationRepository, "relyingPartyRegistrationRepository cannot be null");
this.relyingPartyRegistrationRepository = relyingPartyRegistrationRepository;
@@ -64,8 +65,8 @@ public final class DefaultRelyingPartyRegistrationResolver
if (registrationId == null) {
return null;
}
RelyingPartyRegistration relyingPartyRegistration =
this.relyingPartyRegistrationRepository.findByRegistrationId(registrationId);
RelyingPartyRegistration relyingPartyRegistration = this.relyingPartyRegistrationRepository
.findByRegistrationId(registrationId);
if (relyingPartyRegistration == null) {
return null;
}
@@ -73,12 +74,10 @@ public final class DefaultRelyingPartyRegistrationResolver
String applicationUri = getApplicationUri(request);
Function<String, String> templateResolver = templateResolver(applicationUri, relyingPartyRegistration);
String relyingPartyEntityId = templateResolver.apply(relyingPartyRegistration.getEntityId());
String assertionConsumerServiceLocation = templateResolver.apply(
relyingPartyRegistration.getAssertionConsumerServiceLocation());
return withRelyingPartyRegistration(relyingPartyRegistration)
.entityId(relyingPartyEntityId)
.assertionConsumerServiceLocation(assertionConsumerServiceLocation)
.build();
String assertionConsumerServiceLocation = templateResolver
.apply(relyingPartyRegistration.getAssertionConsumerServiceLocation());
return withRelyingPartyRegistration(relyingPartyRegistration).entityId(relyingPartyEntityId)
.assertionConsumerServiceLocation(assertionConsumerServiceLocation).build();
}
private Function<String, String> templateResolver(String applicationUri, RelyingPartyRegistration relyingParty) {
@@ -89,9 +88,7 @@ public final class DefaultRelyingPartyRegistrationResolver
String entityId = relyingParty.getAssertingPartyDetails().getEntityId();
String registrationId = relyingParty.getRegistrationId();
Map<String, String> uriVariables = new HashMap<>();
UriComponents uriComponents = UriComponentsBuilder.fromHttpUrl(baseUrl)
.replaceQuery(null)
.fragment(null)
UriComponents uriComponents = UriComponentsBuilder.fromHttpUrl(baseUrl).replaceQuery(null).fragment(null)
.build();
String scheme = uriComponents.getScheme();
uriVariables.put("baseScheme", scheme == null ? "" : scheme);
@@ -109,21 +106,17 @@ public final class DefaultRelyingPartyRegistrationResolver
uriVariables.put("entityId", StringUtils.hasText(entityId) ? entityId : "");
uriVariables.put("registrationId", StringUtils.hasText(registrationId) ? registrationId : "");
return UriComponentsBuilder.fromUriString(template)
.buildAndExpand(uriVariables)
.toUriString();
return UriComponentsBuilder.fromUriString(template).buildAndExpand(uriVariables).toUriString();
}
private static String getApplicationUri(HttpServletRequest request) {
UriComponents uriComponents = fromHttpUrl(buildFullRequestUrl(request))
.replacePath(request.getContextPath())
.replaceQuery(null)
.fragment(null)
.build();
UriComponents uriComponents = fromHttpUrl(buildFullRequestUrl(request)).replacePath(request.getContextPath())
.replaceQuery(null).fragment(null).build();
return uriComponents.toUriString();
}
private static class RegistrationIdResolver implements Converter<HttpServletRequest, String> {
private final RequestMatcher requestMatcher = new AntPathRequestMatcher("/**/{registrationId}");
@Override
@@ -131,5 +124,7 @@ public final class DefaultRelyingPartyRegistrationResolver
RequestMatcher.MatchResult result = this.requestMatcher.matcher(request);
return result.getVariables().get("registrationId");
}
}
}
@@ -27,21 +27,23 @@ import org.springframework.security.saml2.provider.service.registration.RelyingP
import org.springframework.util.Assert;
/**
* The default implementation for {@link Saml2AuthenticationRequestContextResolver}
* which uses the current request and given relying party to formulate a {@link Saml2AuthenticationRequestContext}
* The default implementation for {@link Saml2AuthenticationRequestContextResolver} which
* uses the current request and given relying party to formulate a
* {@link Saml2AuthenticationRequestContext}
*
* @author Shazin Sadakath
* @author Josh Cummings
* @since 5.4
*/
public final class DefaultSaml2AuthenticationRequestContextResolver implements Saml2AuthenticationRequestContextResolver {
public final class DefaultSaml2AuthenticationRequestContextResolver
implements Saml2AuthenticationRequestContextResolver {
private final Log logger = LogFactory.getLog(getClass());
private final Converter<HttpServletRequest, RelyingPartyRegistration> relyingPartyRegistrationResolver;
public DefaultSaml2AuthenticationRequestContextResolver
(Converter<HttpServletRequest, RelyingPartyRegistration> relyingPartyRegistrationResolver) {
public DefaultSaml2AuthenticationRequestContextResolver(
Converter<HttpServletRequest, RelyingPartyRegistration> relyingPartyRegistrationResolver) {
this.relyingPartyRegistrationResolver = relyingPartyRegistrationResolver;
}
@@ -56,20 +58,19 @@ public final class DefaultSaml2AuthenticationRequestContextResolver implements S
return null;
}
if (this.logger.isDebugEnabled()) {
this.logger.debug("Creating SAML 2.0 Authentication Request for Asserting Party [" +
relyingParty.getRegistrationId() + "]");
this.logger.debug("Creating SAML 2.0 Authentication Request for Asserting Party ["
+ relyingParty.getRegistrationId() + "]");
}
return createRedirectAuthenticationRequestContext(request, relyingParty);
}
private Saml2AuthenticationRequestContext createRedirectAuthenticationRequestContext(
HttpServletRequest request, RelyingPartyRegistration relyingParty) {
private Saml2AuthenticationRequestContext createRedirectAuthenticationRequestContext(HttpServletRequest request,
RelyingPartyRegistration relyingParty) {
return Saml2AuthenticationRequestContext.builder()
.issuer(relyingParty.getEntityId())
return Saml2AuthenticationRequestContext.builder().issuer(relyingParty.getEntityId())
.relyingPartyRegistration(relyingParty)
.assertionConsumerServiceUrl(relyingParty.getAssertionConsumerServiceLocation())
.relayState(request.getParameter("RelayState"))
.build();
.relayState(request.getParameter("RelayState")).build();
}
}
@@ -22,7 +22,8 @@ import org.springframework.security.saml2.provider.service.authentication.Saml2A
/**
* This {@code Saml2AuthenticationRequestContextResolver} formulates a
* <a href="https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf">SAML 2.0 AuthnRequest</a> (line 1968)
* <a href="https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf">SAML 2.0
* AuthnRequest</a> (line 1968)
*
* @author Shazin Sadakath
* @author Josh Cummings
@@ -31,11 +32,11 @@ import org.springframework.security.saml2.provider.service.authentication.Saml2A
public interface Saml2AuthenticationRequestContextResolver {
/**
* This {@code resolve} method is defined to create a {@link Saml2AuthenticationRequestContext}
*
*
* This {@code resolve} method is defined to create a
* {@link Saml2AuthenticationRequestContext}
* @param request the current request
* @return the created {@link Saml2AuthenticationRequestContext} for the request
*/
Saml2AuthenticationRequestContext resolve(HttpServletRequest request);
}
@@ -35,26 +35,27 @@ import org.springframework.util.Assert;
import static java.nio.charset.StandardCharsets.UTF_8;
/**
* An {@link AuthenticationConverter} that generates a {@link Saml2AuthenticationToken} appropriate
* for authenticated a SAML 2.0 Assertion against an
* An {@link AuthenticationConverter} that generates a {@link Saml2AuthenticationToken}
* appropriate for authenticated a SAML 2.0 Assertion against an
* {@link org.springframework.security.authentication.AuthenticationManager}.
*
* @author Josh Cummings
* @since 5.4
*/
public final class Saml2AuthenticationTokenConverter implements AuthenticationConverter {
private static Base64 BASE64 = new Base64(0, new byte[]{'\n'});
private static Base64 BASE64 = new Base64(0, new byte[] { '\n' });
private final Converter<HttpServletRequest, RelyingPartyRegistration> relyingPartyRegistrationResolver;
/**
* Constructs a {@link Saml2AuthenticationTokenConverter} given a strategy for resolving
* Constructs a {@link Saml2AuthenticationTokenConverter} given a strategy for
* resolving {@link RelyingPartyRegistration}s
* @param relyingPartyRegistrationResolver the strategy for resolving
* {@link RelyingPartyRegistration}s
*
* @param relyingPartyRegistrationResolver the strategy for resolving {@link RelyingPartyRegistration}s
*/
public Saml2AuthenticationTokenConverter
(Converter<HttpServletRequest, RelyingPartyRegistration> relyingPartyRegistrationResolver) {
public Saml2AuthenticationTokenConverter(
Converter<HttpServletRequest, RelyingPartyRegistration> relyingPartyRegistrationResolver) {
Assert.notNull(relyingPartyRegistrationResolver, "relyingPartyRegistrationResolver cannot be null");
this.relyingPartyRegistrationResolver = relyingPartyRegistrationResolver;
}
@@ -102,4 +103,5 @@ public final class Saml2AuthenticationTokenConverter implements AuthenticationCo
throw new Saml2Exception("Unable to inflate string", e);
}
}
}
@@ -42,6 +42,7 @@ import org.springframework.web.filter.OncePerRequestFilter;
public final class Saml2MetadataFilter extends OncePerRequestFilter {
private final Converter<HttpServletRequest, RelyingPartyRegistration> relyingPartyRegistrationConverter;
private final Saml2MetadataResolver saml2MetadataResolver;
private RequestMatcher requestMatcher = new AntPathRequestMatcher(
@@ -65,8 +66,7 @@ public final class Saml2MetadataFilter extends OncePerRequestFilter {
return;
}
RelyingPartyRegistration relyingPartyRegistration =
this.relyingPartyRegistrationConverter.convert(request);
RelyingPartyRegistration relyingPartyRegistration = this.relyingPartyRegistrationConverter.convert(request);
if (relyingPartyRegistration == null) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
return;
@@ -88,13 +88,13 @@ public final class Saml2MetadataFilter extends OncePerRequestFilter {
}
/**
* Set the {@link RequestMatcher} that determines whether this filter should
* handle the incoming {@link HttpServletRequest}
*
* Set the {@link RequestMatcher} that determines whether this filter should handle
* the incoming {@link HttpServletRequest}
* @param requestMatcher
*/
public void setRequestMatcher(RequestMatcher requestMatcher) {
Assert.notNull(requestMatcher, "requestMatcher cannot be null");
this.requestMatcher = requestMatcher;
}
}
@@ -37,8 +37,8 @@ public class OpenSamlInitializationServiceTests {
OpenSamlInitializationService.initialize();
XMLObjectProviderRegistry registry = ConfigurationService.get(XMLObjectProviderRegistry.class);
assertThat(registry.getParserPool()).isNotNull();
assertThatCode(() -> OpenSamlInitializationService.requireInitialize(r -> {}))
.isInstanceOf(Saml2Exception.class)
.hasMessageContaining("OpenSAML was already initialized previously");
assertThatCode(() -> OpenSamlInitializationService.requireInitialize(r -> {
})).isInstanceOf(Saml2Exception.class).hasMessageContaining("OpenSAML was already initialized previously");
}
}
@@ -32,7 +32,7 @@ import static java.util.zip.Deflater.DEFLATED;
public final class Saml2Utils {
private static Base64 BASE64 = new Base64(0, new byte[]{'\n'});
private static Base64 BASE64 = new Base64(0, new byte[] { '\n' });
public static String samlEncode(byte[] b) {
return BASE64.encodeAsString(b);
@@ -67,4 +67,5 @@ public final class Saml2Utils {
throw new Saml2Exception("Unable to inflate string", e);
}
}
}
@@ -40,44 +40,43 @@ public class Saml2X509CredentialTests {
public ExpectedException exception = ExpectedException.none();
private PrivateKey key;
private X509Certificate certificate;
@Before
public void setup() throws Exception {
String keyData = "-----BEGIN PRIVATE KEY-----\n" +
"MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBANG7v8QjQGU3MwQE\n" +
"VUBxvH6Uuiy/MhZT7TV0ZNjyAF2ExA1gpn3aUxx6jYK5UnrpxRRE/KbeLucYbOhK\n" +
"cDECt77Rggz5TStrOta0BQTvfluRyoQtmQ5Nkt6Vqg7O2ZapFt7k64Sal7AftzH6\n" +
"Q2BxWN1y04bLdDrH4jipqRj/2qEFAgMBAAECgYEAj4ExY1jjdN3iEDuOwXuRB+Nn\n" +
"x7pC4TgntE2huzdKvLJdGvIouTArce8A6JM5NlTBvm69mMepvAHgcsiMH1zGr5J5\n" +
"wJz23mGOyhM1veON41/DJTVG+cxq4soUZhdYy3bpOuXGMAaJ8QLMbQQoivllNihd\n" +
"vwH0rNSK8LTYWWPZYIECQQDxct+TFX1VsQ1eo41K0T4fu2rWUaxlvjUGhK6HxTmY\n" +
"8OMJptunGRJL1CUjIb45Uz7SP8TPz5FwhXWsLfS182kRAkEA3l+Qd9C9gdpUh1uX\n" +
"oPSNIxn5hFUrSTW1EwP9QH9vhwb5Vr8Jrd5ei678WYDLjUcx648RjkjhU9jSMzIx\n" +
"EGvYtQJBAMm/i9NR7IVyyNIgZUpz5q4LI21rl1r4gUQuD8vA36zM81i4ROeuCly0\n" +
"KkfdxR4PUfnKcQCX11YnHjk9uTFj75ECQEFY/gBnxDjzqyF35hAzrYIiMPQVfznt\n" +
"YX/sDTE2AdVBVGaMj1Cb51bPHnNC6Q5kXKQnj/YrLqRQND09Q7ParX0CQQC5NxZr\n" +
"9jKqhHj8yQD6PlXTsY4Occ7DH6/IoDenfdEVD5qlet0zmd50HatN2Jiqm5ubN7CM\n" +
"INrtuLp4YHbgk1mi\n" +
"-----END PRIVATE KEY-----";
String keyData = "-----BEGIN PRIVATE KEY-----\n"
+ "MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBANG7v8QjQGU3MwQE\n"
+ "VUBxvH6Uuiy/MhZT7TV0ZNjyAF2ExA1gpn3aUxx6jYK5UnrpxRRE/KbeLucYbOhK\n"
+ "cDECt77Rggz5TStrOta0BQTvfluRyoQtmQ5Nkt6Vqg7O2ZapFt7k64Sal7AftzH6\n"
+ "Q2BxWN1y04bLdDrH4jipqRj/2qEFAgMBAAECgYEAj4ExY1jjdN3iEDuOwXuRB+Nn\n"
+ "x7pC4TgntE2huzdKvLJdGvIouTArce8A6JM5NlTBvm69mMepvAHgcsiMH1zGr5J5\n"
+ "wJz23mGOyhM1veON41/DJTVG+cxq4soUZhdYy3bpOuXGMAaJ8QLMbQQoivllNihd\n"
+ "vwH0rNSK8LTYWWPZYIECQQDxct+TFX1VsQ1eo41K0T4fu2rWUaxlvjUGhK6HxTmY\n"
+ "8OMJptunGRJL1CUjIb45Uz7SP8TPz5FwhXWsLfS182kRAkEA3l+Qd9C9gdpUh1uX\n"
+ "oPSNIxn5hFUrSTW1EwP9QH9vhwb5Vr8Jrd5ei678WYDLjUcx648RjkjhU9jSMzIx\n"
+ "EGvYtQJBAMm/i9NR7IVyyNIgZUpz5q4LI21rl1r4gUQuD8vA36zM81i4ROeuCly0\n"
+ "KkfdxR4PUfnKcQCX11YnHjk9uTFj75ECQEFY/gBnxDjzqyF35hAzrYIiMPQVfznt\n"
+ "YX/sDTE2AdVBVGaMj1Cb51bPHnNC6Q5kXKQnj/YrLqRQND09Q7ParX0CQQC5NxZr\n"
+ "9jKqhHj8yQD6PlXTsY4Occ7DH6/IoDenfdEVD5qlet0zmd50HatN2Jiqm5ubN7CM\n" + "INrtuLp4YHbgk1mi\n"
+ "-----END PRIVATE KEY-----";
key = RsaKeyConverters.pkcs8().convert(new ByteArrayInputStream(keyData.getBytes(UTF_8)));
final CertificateFactory factory = CertificateFactory.getInstance("X.509");
String certificateData = "-----BEGIN CERTIFICATE-----\n" +
"MIICgTCCAeoCCQCuVzyqFgMSyDANBgkqhkiG9w0BAQsFADCBhDELMAkGA1UEBhMC\n" +
"VVMxEzARBgNVBAgMCldhc2hpbmd0b24xEjAQBgNVBAcMCVZhbmNvdXZlcjEdMBsG\n" +
"A1UECgwUU3ByaW5nIFNlY3VyaXR5IFNBTUwxCzAJBgNVBAsMAnNwMSAwHgYDVQQD\n" +
"DBdzcC5zcHJpbmcuc2VjdXJpdHkuc2FtbDAeFw0xODA1MTQxNDMwNDRaFw0yODA1\n" +
"MTExNDMwNDRaMIGEMQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2FzaGluZ3RvbjES\n" +
"MBAGA1UEBwwJVmFuY291dmVyMR0wGwYDVQQKDBRTcHJpbmcgU2VjdXJpdHkgU0FN\n" +
"TDELMAkGA1UECwwCc3AxIDAeBgNVBAMMF3NwLnNwcmluZy5zZWN1cml0eS5zYW1s\n" +
"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDRu7/EI0BlNzMEBFVAcbx+lLos\n" +
"vzIWU+01dGTY8gBdhMQNYKZ92lMceo2CuVJ66cUURPym3i7nGGzoSnAxAre+0YIM\n" +
"+U0razrWtAUE735bkcqELZkOTZLelaoOztmWqRbe5OuEmpewH7cx+kNgcVjdctOG\n" +
"y3Q6x+I4qakY/9qhBQIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAAeViTvHOyQopWEi\n" +
"XOfI2Z9eukwrSknDwq/zscR0YxwwqDBMt/QdAODfSwAfnciiYLkmEjlozWRtOeN+\n" +
"qK7UFgP1bRl5qksrYX5S0z2iGJh0GvonLUt3e20Ssfl5tTEDDnAEUMLfBkyaxEHD\n" +
"RZ/nbTJ7VTeZOSyRoVn5XHhpuJ0B\n" +
"-----END CERTIFICATE-----";
String certificateData = "-----BEGIN CERTIFICATE-----\n"
+ "MIICgTCCAeoCCQCuVzyqFgMSyDANBgkqhkiG9w0BAQsFADCBhDELMAkGA1UEBhMC\n"
+ "VVMxEzARBgNVBAgMCldhc2hpbmd0b24xEjAQBgNVBAcMCVZhbmNvdXZlcjEdMBsG\n"
+ "A1UECgwUU3ByaW5nIFNlY3VyaXR5IFNBTUwxCzAJBgNVBAsMAnNwMSAwHgYDVQQD\n"
+ "DBdzcC5zcHJpbmcuc2VjdXJpdHkuc2FtbDAeFw0xODA1MTQxNDMwNDRaFw0yODA1\n"
+ "MTExNDMwNDRaMIGEMQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2FzaGluZ3RvbjES\n"
+ "MBAGA1UEBwwJVmFuY291dmVyMR0wGwYDVQQKDBRTcHJpbmcgU2VjdXJpdHkgU0FN\n"
+ "TDELMAkGA1UECwwCc3AxIDAeBgNVBAMMF3NwLnNwcmluZy5zZWN1cml0eS5zYW1s\n"
+ "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDRu7/EI0BlNzMEBFVAcbx+lLos\n"
+ "vzIWU+01dGTY8gBdhMQNYKZ92lMceo2CuVJ66cUURPym3i7nGGzoSnAxAre+0YIM\n"
+ "+U0razrWtAUE735bkcqELZkOTZLelaoOztmWqRbe5OuEmpewH7cx+kNgcVjdctOG\n"
+ "y3Q6x+I4qakY/9qhBQIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAAeViTvHOyQopWEi\n"
+ "XOfI2Z9eukwrSknDwq/zscR0YxwwqDBMt/QdAODfSwAfnciiYLkmEjlozWRtOeN+\n"
+ "qK7UFgP1bRl5qksrYX5S0z2iGJh0GvonLUt3e20Ssfl5tTEDDnAEUMLfBkyaxEHD\n"
+ "RZ/nbTJ7VTeZOSyRoVn5XHhpuJ0B\n" + "-----END CERTIFICATE-----";
certificate = (X509Certificate) factory
.generateCertificate(new ByteArrayInputStream(certificateData.getBytes(UTF_8)));
}
@@ -195,4 +194,5 @@ public class Saml2X509CredentialTests {
exception.expect(IllegalArgumentException.class);
Saml2X509Credential.encryption(null);
}
}
@@ -34,6 +34,7 @@ import static org.springframework.security.saml2.core.Saml2X509Credential.Saml2X
import static org.springframework.security.saml2.core.Saml2X509Credential.Saml2X509CredentialType.VERIFICATION;
public final class TestSaml2X509Credentials {
public static Saml2X509Credential assertingPartySigningCredential() {
return new Saml2X509Credential(idpPrivateKey(), idpCertificate(), SIGNING);
}
@@ -61,9 +62,7 @@ public final class TestSaml2X509Credentials {
private static X509Certificate certificate(String cert) {
ByteArrayInputStream certBytes = new ByteArrayInputStream(cert.getBytes());
try {
return (X509Certificate) CertificateFactory
.getInstance("X.509")
.generateCertificate(certBytes);
return (X509Certificate) CertificateFactory.getInstance("X.509").generateCertificate(certBytes);
}
catch (CertificateException e) {
throw new Saml2Exception(e);
@@ -79,101 +78,97 @@ public final class TestSaml2X509Credentials {
}
}
private static X509Certificate idpCertificate() {
return certificate("-----BEGIN CERTIFICATE-----\n"
+ "MIIEEzCCAvugAwIBAgIJAIc1qzLrv+5nMA0GCSqGSIb3DQEBCwUAMIGfMQswCQYD\n"
+ "VQQGEwJVUzELMAkGA1UECAwCQ08xFDASBgNVBAcMC0Nhc3RsZSBSb2NrMRwwGgYD\n"
+ "VQQKDBNTYW1sIFRlc3RpbmcgU2VydmVyMQswCQYDVQQLDAJJVDEgMB4GA1UEAwwX\n"
+ "c2ltcGxlc2FtbHBocC5jZmFwcHMuaW8xIDAeBgkqhkiG9w0BCQEWEWZoYW5pa0Bw\n"
+ "aXZvdGFsLmlvMB4XDTE1MDIyMzIyNDUwM1oXDTI1MDIyMjIyNDUwM1owgZ8xCzAJ\n"
+ "BgNVBAYTAlVTMQswCQYDVQQIDAJDTzEUMBIGA1UEBwwLQ2FzdGxlIFJvY2sxHDAa\n"
+ "BgNVBAoME1NhbWwgVGVzdGluZyBTZXJ2ZXIxCzAJBgNVBAsMAklUMSAwHgYDVQQD\n"
+ "DBdzaW1wbGVzYW1scGhwLmNmYXBwcy5pbzEgMB4GCSqGSIb3DQEJARYRZmhhbmlr\n"
+ "QHBpdm90YWwuaW8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC4cn62\n"
+ "E1xLqpN34PmbrKBbkOXFjzWgJ9b+pXuaRft6A339uuIQeoeH5qeSKRVTl32L0gdz\n"
+ "2ZivLwZXW+cqvftVW1tvEHvzJFyxeTW3fCUeCQsebLnA2qRa07RkxTo6Nf244mWW\n"
+ "RDodcoHEfDUSbxfTZ6IExSojSIU2RnD6WllYWFdD1GFpBJOmQB8rAc8wJIBdHFdQ\n"
+ "nX8Ttl7hZ6rtgqEYMzYVMuJ2F2r1HSU1zSAvwpdYP6rRGFRJEfdA9mm3WKfNLSc5\n"
+ "cljz0X/TXy0vVlAV95l9qcfFzPmrkNIst9FZSwpvB49LyAVke04FQPPwLgVH4gph\n"
+ "iJH3jvZ7I+J5lS8VAgMBAAGjUDBOMB0GA1UdDgQWBBTTyP6Cc5HlBJ5+ucVCwGc5\n"
+ "ogKNGzAfBgNVHSMEGDAWgBTTyP6Cc5HlBJ5+ucVCwGc5ogKNGzAMBgNVHRMEBTAD\n"
+ "AQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAvMS4EQeP/ipV4jOG5lO6/tYCb/iJeAduO\n"
+ "nRhkJk0DbX329lDLZhTTL/x/w/9muCVcvLrzEp6PN+VWfw5E5FWtZN0yhGtP9R+v\n"
+ "ZnrV+oc2zGD+no1/ySFOe3EiJCO5dehxKjYEmBRv5sU/LZFKZpozKN/BMEa6CqLu\n"
+ "xbzb7ykxVr7EVFXwltPxzE9TmL9OACNNyF5eJHWMRMllarUvkcXlh4pux4ks9e6z\n"
+ "V9DQBy2zds9f1I3qxg0eX6JnGrXi/ZiCT+lJgVe3ZFXiejiLAiKB04sXW3ti0LW3\n"
+ "lx13Y1YlQ4/tlpgTgfIJxKV6nyPiLoK0nywbMd+vpAirDt2Oc+hk\n"
+ "-----END CERTIFICATE-----\n");
private static X509Certificate idpCertificate() {
return certificate(
"-----BEGIN CERTIFICATE-----\n" + "MIIEEzCCAvugAwIBAgIJAIc1qzLrv+5nMA0GCSqGSIb3DQEBCwUAMIGfMQswCQYD\n"
+ "VQQGEwJVUzELMAkGA1UECAwCQ08xFDASBgNVBAcMC0Nhc3RsZSBSb2NrMRwwGgYD\n"
+ "VQQKDBNTYW1sIFRlc3RpbmcgU2VydmVyMQswCQYDVQQLDAJJVDEgMB4GA1UEAwwX\n"
+ "c2ltcGxlc2FtbHBocC5jZmFwcHMuaW8xIDAeBgkqhkiG9w0BCQEWEWZoYW5pa0Bw\n"
+ "aXZvdGFsLmlvMB4XDTE1MDIyMzIyNDUwM1oXDTI1MDIyMjIyNDUwM1owgZ8xCzAJ\n"
+ "BgNVBAYTAlVTMQswCQYDVQQIDAJDTzEUMBIGA1UEBwwLQ2FzdGxlIFJvY2sxHDAa\n"
+ "BgNVBAoME1NhbWwgVGVzdGluZyBTZXJ2ZXIxCzAJBgNVBAsMAklUMSAwHgYDVQQD\n"
+ "DBdzaW1wbGVzYW1scGhwLmNmYXBwcy5pbzEgMB4GCSqGSIb3DQEJARYRZmhhbmlr\n"
+ "QHBpdm90YWwuaW8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC4cn62\n"
+ "E1xLqpN34PmbrKBbkOXFjzWgJ9b+pXuaRft6A339uuIQeoeH5qeSKRVTl32L0gdz\n"
+ "2ZivLwZXW+cqvftVW1tvEHvzJFyxeTW3fCUeCQsebLnA2qRa07RkxTo6Nf244mWW\n"
+ "RDodcoHEfDUSbxfTZ6IExSojSIU2RnD6WllYWFdD1GFpBJOmQB8rAc8wJIBdHFdQ\n"
+ "nX8Ttl7hZ6rtgqEYMzYVMuJ2F2r1HSU1zSAvwpdYP6rRGFRJEfdA9mm3WKfNLSc5\n"
+ "cljz0X/TXy0vVlAV95l9qcfFzPmrkNIst9FZSwpvB49LyAVke04FQPPwLgVH4gph\n"
+ "iJH3jvZ7I+J5lS8VAgMBAAGjUDBOMB0GA1UdDgQWBBTTyP6Cc5HlBJ5+ucVCwGc5\n"
+ "ogKNGzAfBgNVHSMEGDAWgBTTyP6Cc5HlBJ5+ucVCwGc5ogKNGzAMBgNVHRMEBTAD\n"
+ "AQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAvMS4EQeP/ipV4jOG5lO6/tYCb/iJeAduO\n"
+ "nRhkJk0DbX329lDLZhTTL/x/w/9muCVcvLrzEp6PN+VWfw5E5FWtZN0yhGtP9R+v\n"
+ "ZnrV+oc2zGD+no1/ySFOe3EiJCO5dehxKjYEmBRv5sU/LZFKZpozKN/BMEa6CqLu\n"
+ "xbzb7ykxVr7EVFXwltPxzE9TmL9OACNNyF5eJHWMRMllarUvkcXlh4pux4ks9e6z\n"
+ "V9DQBy2zds9f1I3qxg0eX6JnGrXi/ZiCT+lJgVe3ZFXiejiLAiKB04sXW3ti0LW3\n"
+ "lx13Y1YlQ4/tlpgTgfIJxKV6nyPiLoK0nywbMd+vpAirDt2Oc+hk\n" + "-----END CERTIFICATE-----\n");
}
private static PrivateKey idpPrivateKey() {
return privateKey("-----BEGIN PRIVATE KEY-----\n"
+ "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC4cn62E1xLqpN3\n"
+ "4PmbrKBbkOXFjzWgJ9b+pXuaRft6A339uuIQeoeH5qeSKRVTl32L0gdz2ZivLwZX\n"
+ "W+cqvftVW1tvEHvzJFyxeTW3fCUeCQsebLnA2qRa07RkxTo6Nf244mWWRDodcoHE\n"
+ "fDUSbxfTZ6IExSojSIU2RnD6WllYWFdD1GFpBJOmQB8rAc8wJIBdHFdQnX8Ttl7h\n"
+ "Z6rtgqEYMzYVMuJ2F2r1HSU1zSAvwpdYP6rRGFRJEfdA9mm3WKfNLSc5cljz0X/T\n"
+ "Xy0vVlAV95l9qcfFzPmrkNIst9FZSwpvB49LyAVke04FQPPwLgVH4gphiJH3jvZ7\n"
+ "I+J5lS8VAgMBAAECggEBAKyxBlIS7mcp3chvq0RF7B3PHFJMMzkwE+t3pLJcs4cZ\n"
+ "nezh/KbREfP70QjXzk/llnZCvxeIs5vRu24vbdBm79qLHqBuHp8XfHHtuo2AfoAQ\n"
+ "l4h047Xc/+TKMivnPQ0jX9qqndKDLqZDf5wnbslDmlskvF0a/MjsLU0TxtOfo+dB\n"
+ "t55FW11cGqxZwhS5Gnr+cbw3OkHz23b9gEOt9qfwPVepeysbmm9FjU+k4yVa7rAN\n"
+ "xcbzVb6Y7GCITe2tgvvEHmjB9BLmWrH3mZ3Af17YU/iN6TrpPd6Sj3QoS+2wGtAe\n"
+ "HbUs3CKJu7bIHcj4poal6Kh8519S+erJTtqQ8M0ZiEECgYEA43hLYAPaUueFkdfh\n"
+ "9K/7ClH6436CUH3VdizwUXi26fdhhV/I/ot6zLfU2mgEHU22LBECWQGtAFm8kv0P\n"
+ "zPn+qjaR3e62l5PIlSYbnkIidzoDZ2ztu4jF5LgStlTJQPteFEGgZVl5o9DaSZOq\n"
+ "Yd7G3XqXuQ1VGMW58G5FYJPtA1cCgYEAz5TPUtK+R2KXHMjUwlGY9AefQYRYmyX2\n"
+ "Tn/OFgKvY8lpAkMrhPKONq7SMYc8E9v9G7A0dIOXvW7QOYSapNhKU+np3lUafR5F\n"
+ "4ZN0bxZ9qjHbn3AMYeraKjeutHvlLtbHdIc1j3sxe/EzltRsYmiqLdEBW0p6hwWg\n"
+ "tyGhYWVyaXMCgYAfDOKtHpmEy5nOCLwNXKBWDk7DExfSyPqEgSnk1SeS1HP5ctPK\n"
+ "+1st6sIhdiVpopwFc+TwJWxqKdW18tlfT5jVv1E2DEnccw3kXilS9xAhWkfwrEvf\n"
+ "V5I74GydewFl32o+NZ8hdo9GL1I8zO1rIq/et8dSOWGuWf9BtKu/vTGTTQKBgFxU\n"
+ "VjsCnbvmsEwPUAL2hE/WrBFaKocnxXx5AFNt8lEyHtDwy4Sg1nygGcIJ4sD6koQk\n"
+ "RdClT3LkvR04TAiSY80bN/i6ZcPNGUwSaDGZEWAIOSWbkwZijZNFnSGOEgxZX/IG\n"
+ "yd39766vREEMTwEeiMNEOZQ/dmxkJm4OOVe25cLdAoGACOtPnq1Fxay80UYBf4rQ\n"
+ "+bJ9yX1ulB8WIree1hD7OHSB2lRHxrVYWrglrTvkh63Lgx+EcsTV788OsvAVfPPz\n"
+ "BZrn8SdDlQqalMxUBYEFwnsYD3cQ8yOUnijFVC4xNcdDv8OIqVgSk4KKxU5AshaA\n" + "xk6Mox+u8Cc2eAK12H13i+8=\n"
+ "-----END PRIVATE KEY-----\n");
return privateKey(
"-----BEGIN PRIVATE KEY-----\n" + "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC4cn62E1xLqpN3\n"
+ "4PmbrKBbkOXFjzWgJ9b+pXuaRft6A339uuIQeoeH5qeSKRVTl32L0gdz2ZivLwZX\n"
+ "W+cqvftVW1tvEHvzJFyxeTW3fCUeCQsebLnA2qRa07RkxTo6Nf244mWWRDodcoHE\n"
+ "fDUSbxfTZ6IExSojSIU2RnD6WllYWFdD1GFpBJOmQB8rAc8wJIBdHFdQnX8Ttl7h\n"
+ "Z6rtgqEYMzYVMuJ2F2r1HSU1zSAvwpdYP6rRGFRJEfdA9mm3WKfNLSc5cljz0X/T\n"
+ "Xy0vVlAV95l9qcfFzPmrkNIst9FZSwpvB49LyAVke04FQPPwLgVH4gphiJH3jvZ7\n"
+ "I+J5lS8VAgMBAAECggEBAKyxBlIS7mcp3chvq0RF7B3PHFJMMzkwE+t3pLJcs4cZ\n"
+ "nezh/KbREfP70QjXzk/llnZCvxeIs5vRu24vbdBm79qLHqBuHp8XfHHtuo2AfoAQ\n"
+ "l4h047Xc/+TKMivnPQ0jX9qqndKDLqZDf5wnbslDmlskvF0a/MjsLU0TxtOfo+dB\n"
+ "t55FW11cGqxZwhS5Gnr+cbw3OkHz23b9gEOt9qfwPVepeysbmm9FjU+k4yVa7rAN\n"
+ "xcbzVb6Y7GCITe2tgvvEHmjB9BLmWrH3mZ3Af17YU/iN6TrpPd6Sj3QoS+2wGtAe\n"
+ "HbUs3CKJu7bIHcj4poal6Kh8519S+erJTtqQ8M0ZiEECgYEA43hLYAPaUueFkdfh\n"
+ "9K/7ClH6436CUH3VdizwUXi26fdhhV/I/ot6zLfU2mgEHU22LBECWQGtAFm8kv0P\n"
+ "zPn+qjaR3e62l5PIlSYbnkIidzoDZ2ztu4jF5LgStlTJQPteFEGgZVl5o9DaSZOq\n"
+ "Yd7G3XqXuQ1VGMW58G5FYJPtA1cCgYEAz5TPUtK+R2KXHMjUwlGY9AefQYRYmyX2\n"
+ "Tn/OFgKvY8lpAkMrhPKONq7SMYc8E9v9G7A0dIOXvW7QOYSapNhKU+np3lUafR5F\n"
+ "4ZN0bxZ9qjHbn3AMYeraKjeutHvlLtbHdIc1j3sxe/EzltRsYmiqLdEBW0p6hwWg\n"
+ "tyGhYWVyaXMCgYAfDOKtHpmEy5nOCLwNXKBWDk7DExfSyPqEgSnk1SeS1HP5ctPK\n"
+ "+1st6sIhdiVpopwFc+TwJWxqKdW18tlfT5jVv1E2DEnccw3kXilS9xAhWkfwrEvf\n"
+ "V5I74GydewFl32o+NZ8hdo9GL1I8zO1rIq/et8dSOWGuWf9BtKu/vTGTTQKBgFxU\n"
+ "VjsCnbvmsEwPUAL2hE/WrBFaKocnxXx5AFNt8lEyHtDwy4Sg1nygGcIJ4sD6koQk\n"
+ "RdClT3LkvR04TAiSY80bN/i6ZcPNGUwSaDGZEWAIOSWbkwZijZNFnSGOEgxZX/IG\n"
+ "yd39766vREEMTwEeiMNEOZQ/dmxkJm4OOVe25cLdAoGACOtPnq1Fxay80UYBf4rQ\n"
+ "+bJ9yX1ulB8WIree1hD7OHSB2lRHxrVYWrglrTvkh63Lgx+EcsTV788OsvAVfPPz\n"
+ "BZrn8SdDlQqalMxUBYEFwnsYD3cQ8yOUnijFVC4xNcdDv8OIqVgSk4KKxU5AshaA\n"
+ "xk6Mox+u8Cc2eAK12H13i+8=\n" + "-----END PRIVATE KEY-----\n");
}
private static X509Certificate spCertificate() {
return certificate("-----BEGIN CERTIFICATE-----\n" +
"MIICgTCCAeoCCQCuVzyqFgMSyDANBgkqhkiG9w0BAQsFADCBhDELMAkGA1UEBhMC\n" +
"VVMxEzARBgNVBAgMCldhc2hpbmd0b24xEjAQBgNVBAcMCVZhbmNvdXZlcjEdMBsG\n" +
"A1UECgwUU3ByaW5nIFNlY3VyaXR5IFNBTUwxCzAJBgNVBAsMAnNwMSAwHgYDVQQD\n" +
"DBdzcC5zcHJpbmcuc2VjdXJpdHkuc2FtbDAeFw0xODA1MTQxNDMwNDRaFw0yODA1\n" +
"MTExNDMwNDRaMIGEMQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2FzaGluZ3RvbjES\n" +
"MBAGA1UEBwwJVmFuY291dmVyMR0wGwYDVQQKDBRTcHJpbmcgU2VjdXJpdHkgU0FN\n" +
"TDELMAkGA1UECwwCc3AxIDAeBgNVBAMMF3NwLnNwcmluZy5zZWN1cml0eS5zYW1s\n" +
"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDRu7/EI0BlNzMEBFVAcbx+lLos\n" +
"vzIWU+01dGTY8gBdhMQNYKZ92lMceo2CuVJ66cUURPym3i7nGGzoSnAxAre+0YIM\n" +
"+U0razrWtAUE735bkcqELZkOTZLelaoOztmWqRbe5OuEmpewH7cx+kNgcVjdctOG\n" +
"y3Q6x+I4qakY/9qhBQIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAAeViTvHOyQopWEi\n" +
"XOfI2Z9eukwrSknDwq/zscR0YxwwqDBMt/QdAODfSwAfnciiYLkmEjlozWRtOeN+\n" +
"qK7UFgP1bRl5qksrYX5S0z2iGJh0GvonLUt3e20Ssfl5tTEDDnAEUMLfBkyaxEHD\n" +
"RZ/nbTJ7VTeZOSyRoVn5XHhpuJ0B\n" +
"-----END CERTIFICATE-----");
return certificate(
"-----BEGIN CERTIFICATE-----\n" + "MIICgTCCAeoCCQCuVzyqFgMSyDANBgkqhkiG9w0BAQsFADCBhDELMAkGA1UEBhMC\n"
+ "VVMxEzARBgNVBAgMCldhc2hpbmd0b24xEjAQBgNVBAcMCVZhbmNvdXZlcjEdMBsG\n"
+ "A1UECgwUU3ByaW5nIFNlY3VyaXR5IFNBTUwxCzAJBgNVBAsMAnNwMSAwHgYDVQQD\n"
+ "DBdzcC5zcHJpbmcuc2VjdXJpdHkuc2FtbDAeFw0xODA1MTQxNDMwNDRaFw0yODA1\n"
+ "MTExNDMwNDRaMIGEMQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2FzaGluZ3RvbjES\n"
+ "MBAGA1UEBwwJVmFuY291dmVyMR0wGwYDVQQKDBRTcHJpbmcgU2VjdXJpdHkgU0FN\n"
+ "TDELMAkGA1UECwwCc3AxIDAeBgNVBAMMF3NwLnNwcmluZy5zZWN1cml0eS5zYW1s\n"
+ "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDRu7/EI0BlNzMEBFVAcbx+lLos\n"
+ "vzIWU+01dGTY8gBdhMQNYKZ92lMceo2CuVJ66cUURPym3i7nGGzoSnAxAre+0YIM\n"
+ "+U0razrWtAUE735bkcqELZkOTZLelaoOztmWqRbe5OuEmpewH7cx+kNgcVjdctOG\n"
+ "y3Q6x+I4qakY/9qhBQIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAAeViTvHOyQopWEi\n"
+ "XOfI2Z9eukwrSknDwq/zscR0YxwwqDBMt/QdAODfSwAfnciiYLkmEjlozWRtOeN+\n"
+ "qK7UFgP1bRl5qksrYX5S0z2iGJh0GvonLUt3e20Ssfl5tTEDDnAEUMLfBkyaxEHD\n"
+ "RZ/nbTJ7VTeZOSyRoVn5XHhpuJ0B\n" + "-----END CERTIFICATE-----");
}
private static PrivateKey spPrivateKey() {
return privateKey("-----BEGIN PRIVATE KEY-----\n" +
"MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBANG7v8QjQGU3MwQE\n" +
"VUBxvH6Uuiy/MhZT7TV0ZNjyAF2ExA1gpn3aUxx6jYK5UnrpxRRE/KbeLucYbOhK\n" +
"cDECt77Rggz5TStrOta0BQTvfluRyoQtmQ5Nkt6Vqg7O2ZapFt7k64Sal7AftzH6\n" +
"Q2BxWN1y04bLdDrH4jipqRj/2qEFAgMBAAECgYEAj4ExY1jjdN3iEDuOwXuRB+Nn\n" +
"x7pC4TgntE2huzdKvLJdGvIouTArce8A6JM5NlTBvm69mMepvAHgcsiMH1zGr5J5\n" +
"wJz23mGOyhM1veON41/DJTVG+cxq4soUZhdYy3bpOuXGMAaJ8QLMbQQoivllNihd\n" +
"vwH0rNSK8LTYWWPZYIECQQDxct+TFX1VsQ1eo41K0T4fu2rWUaxlvjUGhK6HxTmY\n" +
"8OMJptunGRJL1CUjIb45Uz7SP8TPz5FwhXWsLfS182kRAkEA3l+Qd9C9gdpUh1uX\n" +
"oPSNIxn5hFUrSTW1EwP9QH9vhwb5Vr8Jrd5ei678WYDLjUcx648RjkjhU9jSMzIx\n" +
"EGvYtQJBAMm/i9NR7IVyyNIgZUpz5q4LI21rl1r4gUQuD8vA36zM81i4ROeuCly0\n" +
"KkfdxR4PUfnKcQCX11YnHjk9uTFj75ECQEFY/gBnxDjzqyF35hAzrYIiMPQVfznt\n" +
"YX/sDTE2AdVBVGaMj1Cb51bPHnNC6Q5kXKQnj/YrLqRQND09Q7ParX0CQQC5NxZr\n" +
"9jKqhHj8yQD6PlXTsY4Occ7DH6/IoDenfdEVD5qlet0zmd50HatN2Jiqm5ubN7CM\n" +
"INrtuLp4YHbgk1mi\n" +
"-----END PRIVATE KEY-----");
return privateKey(
"-----BEGIN PRIVATE KEY-----\n" + "MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBANG7v8QjQGU3MwQE\n"
+ "VUBxvH6Uuiy/MhZT7TV0ZNjyAF2ExA1gpn3aUxx6jYK5UnrpxRRE/KbeLucYbOhK\n"
+ "cDECt77Rggz5TStrOta0BQTvfluRyoQtmQ5Nkt6Vqg7O2ZapFt7k64Sal7AftzH6\n"
+ "Q2BxWN1y04bLdDrH4jipqRj/2qEFAgMBAAECgYEAj4ExY1jjdN3iEDuOwXuRB+Nn\n"
+ "x7pC4TgntE2huzdKvLJdGvIouTArce8A6JM5NlTBvm69mMepvAHgcsiMH1zGr5J5\n"
+ "wJz23mGOyhM1veON41/DJTVG+cxq4soUZhdYy3bpOuXGMAaJ8QLMbQQoivllNihd\n"
+ "vwH0rNSK8LTYWWPZYIECQQDxct+TFX1VsQ1eo41K0T4fu2rWUaxlvjUGhK6HxTmY\n"
+ "8OMJptunGRJL1CUjIb45Uz7SP8TPz5FwhXWsLfS182kRAkEA3l+Qd9C9gdpUh1uX\n"
+ "oPSNIxn5hFUrSTW1EwP9QH9vhwb5Vr8Jrd5ei678WYDLjUcx648RjkjhU9jSMzIx\n"
+ "EGvYtQJBAMm/i9NR7IVyyNIgZUpz5q4LI21rl1r4gUQuD8vA36zM81i4ROeuCly0\n"
+ "KkfdxR4PUfnKcQCX11YnHjk9uTFj75ECQEFY/gBnxDjzqyF35hAzrYIiMPQVfznt\n"
+ "YX/sDTE2AdVBVGaMj1Cb51bPHnNC6Q5kXKQnj/YrLqRQND09Q7ParX0CQQC5NxZr\n"
+ "9jKqhHj8yQD6PlXTsY4Occ7DH6/IoDenfdEVD5qlet0zmd50HatN2Jiqm5ubN7CM\n" + "INrtuLp4YHbgk1mi\n"
+ "-----END PRIVATE KEY-----");
}
}
@@ -40,45 +40,45 @@ public class Saml2X509CredentialTests {
public ExpectedException exception = ExpectedException.none();
private Saml2X509Credential credential;
private PrivateKey key;
private X509Certificate certificate;
@Before
public void setup() throws Exception {
String keyData = "-----BEGIN PRIVATE KEY-----\n" +
"MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBANG7v8QjQGU3MwQE\n" +
"VUBxvH6Uuiy/MhZT7TV0ZNjyAF2ExA1gpn3aUxx6jYK5UnrpxRRE/KbeLucYbOhK\n" +
"cDECt77Rggz5TStrOta0BQTvfluRyoQtmQ5Nkt6Vqg7O2ZapFt7k64Sal7AftzH6\n" +
"Q2BxWN1y04bLdDrH4jipqRj/2qEFAgMBAAECgYEAj4ExY1jjdN3iEDuOwXuRB+Nn\n" +
"x7pC4TgntE2huzdKvLJdGvIouTArce8A6JM5NlTBvm69mMepvAHgcsiMH1zGr5J5\n" +
"wJz23mGOyhM1veON41/DJTVG+cxq4soUZhdYy3bpOuXGMAaJ8QLMbQQoivllNihd\n" +
"vwH0rNSK8LTYWWPZYIECQQDxct+TFX1VsQ1eo41K0T4fu2rWUaxlvjUGhK6HxTmY\n" +
"8OMJptunGRJL1CUjIb45Uz7SP8TPz5FwhXWsLfS182kRAkEA3l+Qd9C9gdpUh1uX\n" +
"oPSNIxn5hFUrSTW1EwP9QH9vhwb5Vr8Jrd5ei678WYDLjUcx648RjkjhU9jSMzIx\n" +
"EGvYtQJBAMm/i9NR7IVyyNIgZUpz5q4LI21rl1r4gUQuD8vA36zM81i4ROeuCly0\n" +
"KkfdxR4PUfnKcQCX11YnHjk9uTFj75ECQEFY/gBnxDjzqyF35hAzrYIiMPQVfznt\n" +
"YX/sDTE2AdVBVGaMj1Cb51bPHnNC6Q5kXKQnj/YrLqRQND09Q7ParX0CQQC5NxZr\n" +
"9jKqhHj8yQD6PlXTsY4Occ7DH6/IoDenfdEVD5qlet0zmd50HatN2Jiqm5ubN7CM\n" +
"INrtuLp4YHbgk1mi\n" +
"-----END PRIVATE KEY-----";
String keyData = "-----BEGIN PRIVATE KEY-----\n"
+ "MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBANG7v8QjQGU3MwQE\n"
+ "VUBxvH6Uuiy/MhZT7TV0ZNjyAF2ExA1gpn3aUxx6jYK5UnrpxRRE/KbeLucYbOhK\n"
+ "cDECt77Rggz5TStrOta0BQTvfluRyoQtmQ5Nkt6Vqg7O2ZapFt7k64Sal7AftzH6\n"
+ "Q2BxWN1y04bLdDrH4jipqRj/2qEFAgMBAAECgYEAj4ExY1jjdN3iEDuOwXuRB+Nn\n"
+ "x7pC4TgntE2huzdKvLJdGvIouTArce8A6JM5NlTBvm69mMepvAHgcsiMH1zGr5J5\n"
+ "wJz23mGOyhM1veON41/DJTVG+cxq4soUZhdYy3bpOuXGMAaJ8QLMbQQoivllNihd\n"
+ "vwH0rNSK8LTYWWPZYIECQQDxct+TFX1VsQ1eo41K0T4fu2rWUaxlvjUGhK6HxTmY\n"
+ "8OMJptunGRJL1CUjIb45Uz7SP8TPz5FwhXWsLfS182kRAkEA3l+Qd9C9gdpUh1uX\n"
+ "oPSNIxn5hFUrSTW1EwP9QH9vhwb5Vr8Jrd5ei678WYDLjUcx648RjkjhU9jSMzIx\n"
+ "EGvYtQJBAMm/i9NR7IVyyNIgZUpz5q4LI21rl1r4gUQuD8vA36zM81i4ROeuCly0\n"
+ "KkfdxR4PUfnKcQCX11YnHjk9uTFj75ECQEFY/gBnxDjzqyF35hAzrYIiMPQVfznt\n"
+ "YX/sDTE2AdVBVGaMj1Cb51bPHnNC6Q5kXKQnj/YrLqRQND09Q7ParX0CQQC5NxZr\n"
+ "9jKqhHj8yQD6PlXTsY4Occ7DH6/IoDenfdEVD5qlet0zmd50HatN2Jiqm5ubN7CM\n" + "INrtuLp4YHbgk1mi\n"
+ "-----END PRIVATE KEY-----";
key = RsaKeyConverters.pkcs8().convert(new ByteArrayInputStream(keyData.getBytes(UTF_8)));
final CertificateFactory factory = CertificateFactory.getInstance("X.509");
String certificateData = "-----BEGIN CERTIFICATE-----\n" +
"MIICgTCCAeoCCQCuVzyqFgMSyDANBgkqhkiG9w0BAQsFADCBhDELMAkGA1UEBhMC\n" +
"VVMxEzARBgNVBAgMCldhc2hpbmd0b24xEjAQBgNVBAcMCVZhbmNvdXZlcjEdMBsG\n" +
"A1UECgwUU3ByaW5nIFNlY3VyaXR5IFNBTUwxCzAJBgNVBAsMAnNwMSAwHgYDVQQD\n" +
"DBdzcC5zcHJpbmcuc2VjdXJpdHkuc2FtbDAeFw0xODA1MTQxNDMwNDRaFw0yODA1\n" +
"MTExNDMwNDRaMIGEMQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2FzaGluZ3RvbjES\n" +
"MBAGA1UEBwwJVmFuY291dmVyMR0wGwYDVQQKDBRTcHJpbmcgU2VjdXJpdHkgU0FN\n" +
"TDELMAkGA1UECwwCc3AxIDAeBgNVBAMMF3NwLnNwcmluZy5zZWN1cml0eS5zYW1s\n" +
"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDRu7/EI0BlNzMEBFVAcbx+lLos\n" +
"vzIWU+01dGTY8gBdhMQNYKZ92lMceo2CuVJ66cUURPym3i7nGGzoSnAxAre+0YIM\n" +
"+U0razrWtAUE735bkcqELZkOTZLelaoOztmWqRbe5OuEmpewH7cx+kNgcVjdctOG\n" +
"y3Q6x+I4qakY/9qhBQIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAAeViTvHOyQopWEi\n" +
"XOfI2Z9eukwrSknDwq/zscR0YxwwqDBMt/QdAODfSwAfnciiYLkmEjlozWRtOeN+\n" +
"qK7UFgP1bRl5qksrYX5S0z2iGJh0GvonLUt3e20Ssfl5tTEDDnAEUMLfBkyaxEHD\n" +
"RZ/nbTJ7VTeZOSyRoVn5XHhpuJ0B\n" +
"-----END CERTIFICATE-----";
String certificateData = "-----BEGIN CERTIFICATE-----\n"
+ "MIICgTCCAeoCCQCuVzyqFgMSyDANBgkqhkiG9w0BAQsFADCBhDELMAkGA1UEBhMC\n"
+ "VVMxEzARBgNVBAgMCldhc2hpbmd0b24xEjAQBgNVBAcMCVZhbmNvdXZlcjEdMBsG\n"
+ "A1UECgwUU3ByaW5nIFNlY3VyaXR5IFNBTUwxCzAJBgNVBAsMAnNwMSAwHgYDVQQD\n"
+ "DBdzcC5zcHJpbmcuc2VjdXJpdHkuc2FtbDAeFw0xODA1MTQxNDMwNDRaFw0yODA1\n"
+ "MTExNDMwNDRaMIGEMQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2FzaGluZ3RvbjES\n"
+ "MBAGA1UEBwwJVmFuY291dmVyMR0wGwYDVQQKDBRTcHJpbmcgU2VjdXJpdHkgU0FN\n"
+ "TDELMAkGA1UECwwCc3AxIDAeBgNVBAMMF3NwLnNwcmluZy5zZWN1cml0eS5zYW1s\n"
+ "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDRu7/EI0BlNzMEBFVAcbx+lLos\n"
+ "vzIWU+01dGTY8gBdhMQNYKZ92lMceo2CuVJ66cUURPym3i7nGGzoSnAxAre+0YIM\n"
+ "+U0razrWtAUE735bkcqELZkOTZLelaoOztmWqRbe5OuEmpewH7cx+kNgcVjdctOG\n"
+ "y3Q6x+I4qakY/9qhBQIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAAeViTvHOyQopWEi\n"
+ "XOfI2Z9eukwrSknDwq/zscR0YxwwqDBMt/QdAODfSwAfnciiYLkmEjlozWRtOeN+\n"
+ "qK7UFgP1bRl5qksrYX5S0z2iGJh0GvonLUt3e20Ssfl5tTEDDnAEUMLfBkyaxEHD\n"
+ "RZ/nbTJ7VTeZOSyRoVn5XHhpuJ0B\n" + "-----END CERTIFICATE-----";
certificate = (X509Certificate) factory
.generateCertificate(new ByteArrayInputStream(certificateData.getBytes(UTF_8)));
}
@@ -145,5 +145,4 @@ public class Saml2X509CredentialTests {
new Saml2X509Credential(certificate, DECRYPTION);
}
}
@@ -34,6 +34,7 @@ import static org.springframework.security.saml2.credentials.Saml2X509Credential
import static org.springframework.security.saml2.credentials.Saml2X509Credential.Saml2X509CredentialType.VERIFICATION;
public final class TestSaml2X509Credentials {
public static Saml2X509Credential assertingPartySigningCredential() {
return new Saml2X509Credential(idpPrivateKey(), idpCertificate(), SIGNING);
}
@@ -61,9 +62,7 @@ public final class TestSaml2X509Credentials {
private static X509Certificate certificate(String cert) {
ByteArrayInputStream certBytes = new ByteArrayInputStream(cert.getBytes());
try {
return (X509Certificate) CertificateFactory
.getInstance("X.509")
.generateCertificate(certBytes);
return (X509Certificate) CertificateFactory.getInstance("X.509").generateCertificate(certBytes);
}
catch (CertificateException e) {
throw new Saml2Exception(e);
@@ -79,101 +78,97 @@ public final class TestSaml2X509Credentials {
}
}
private static X509Certificate idpCertificate() {
return certificate("-----BEGIN CERTIFICATE-----\n"
+ "MIIEEzCCAvugAwIBAgIJAIc1qzLrv+5nMA0GCSqGSIb3DQEBCwUAMIGfMQswCQYD\n"
+ "VQQGEwJVUzELMAkGA1UECAwCQ08xFDASBgNVBAcMC0Nhc3RsZSBSb2NrMRwwGgYD\n"
+ "VQQKDBNTYW1sIFRlc3RpbmcgU2VydmVyMQswCQYDVQQLDAJJVDEgMB4GA1UEAwwX\n"
+ "c2ltcGxlc2FtbHBocC5jZmFwcHMuaW8xIDAeBgkqhkiG9w0BCQEWEWZoYW5pa0Bw\n"
+ "aXZvdGFsLmlvMB4XDTE1MDIyMzIyNDUwM1oXDTI1MDIyMjIyNDUwM1owgZ8xCzAJ\n"
+ "BgNVBAYTAlVTMQswCQYDVQQIDAJDTzEUMBIGA1UEBwwLQ2FzdGxlIFJvY2sxHDAa\n"
+ "BgNVBAoME1NhbWwgVGVzdGluZyBTZXJ2ZXIxCzAJBgNVBAsMAklUMSAwHgYDVQQD\n"
+ "DBdzaW1wbGVzYW1scGhwLmNmYXBwcy5pbzEgMB4GCSqGSIb3DQEJARYRZmhhbmlr\n"
+ "QHBpdm90YWwuaW8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC4cn62\n"
+ "E1xLqpN34PmbrKBbkOXFjzWgJ9b+pXuaRft6A339uuIQeoeH5qeSKRVTl32L0gdz\n"
+ "2ZivLwZXW+cqvftVW1tvEHvzJFyxeTW3fCUeCQsebLnA2qRa07RkxTo6Nf244mWW\n"
+ "RDodcoHEfDUSbxfTZ6IExSojSIU2RnD6WllYWFdD1GFpBJOmQB8rAc8wJIBdHFdQ\n"
+ "nX8Ttl7hZ6rtgqEYMzYVMuJ2F2r1HSU1zSAvwpdYP6rRGFRJEfdA9mm3WKfNLSc5\n"
+ "cljz0X/TXy0vVlAV95l9qcfFzPmrkNIst9FZSwpvB49LyAVke04FQPPwLgVH4gph\n"
+ "iJH3jvZ7I+J5lS8VAgMBAAGjUDBOMB0GA1UdDgQWBBTTyP6Cc5HlBJ5+ucVCwGc5\n"
+ "ogKNGzAfBgNVHSMEGDAWgBTTyP6Cc5HlBJ5+ucVCwGc5ogKNGzAMBgNVHRMEBTAD\n"
+ "AQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAvMS4EQeP/ipV4jOG5lO6/tYCb/iJeAduO\n"
+ "nRhkJk0DbX329lDLZhTTL/x/w/9muCVcvLrzEp6PN+VWfw5E5FWtZN0yhGtP9R+v\n"
+ "ZnrV+oc2zGD+no1/ySFOe3EiJCO5dehxKjYEmBRv5sU/LZFKZpozKN/BMEa6CqLu\n"
+ "xbzb7ykxVr7EVFXwltPxzE9TmL9OACNNyF5eJHWMRMllarUvkcXlh4pux4ks9e6z\n"
+ "V9DQBy2zds9f1I3qxg0eX6JnGrXi/ZiCT+lJgVe3ZFXiejiLAiKB04sXW3ti0LW3\n"
+ "lx13Y1YlQ4/tlpgTgfIJxKV6nyPiLoK0nywbMd+vpAirDt2Oc+hk\n"
+ "-----END CERTIFICATE-----\n");
private static X509Certificate idpCertificate() {
return certificate(
"-----BEGIN CERTIFICATE-----\n" + "MIIEEzCCAvugAwIBAgIJAIc1qzLrv+5nMA0GCSqGSIb3DQEBCwUAMIGfMQswCQYD\n"
+ "VQQGEwJVUzELMAkGA1UECAwCQ08xFDASBgNVBAcMC0Nhc3RsZSBSb2NrMRwwGgYD\n"
+ "VQQKDBNTYW1sIFRlc3RpbmcgU2VydmVyMQswCQYDVQQLDAJJVDEgMB4GA1UEAwwX\n"
+ "c2ltcGxlc2FtbHBocC5jZmFwcHMuaW8xIDAeBgkqhkiG9w0BCQEWEWZoYW5pa0Bw\n"
+ "aXZvdGFsLmlvMB4XDTE1MDIyMzIyNDUwM1oXDTI1MDIyMjIyNDUwM1owgZ8xCzAJ\n"
+ "BgNVBAYTAlVTMQswCQYDVQQIDAJDTzEUMBIGA1UEBwwLQ2FzdGxlIFJvY2sxHDAa\n"
+ "BgNVBAoME1NhbWwgVGVzdGluZyBTZXJ2ZXIxCzAJBgNVBAsMAklUMSAwHgYDVQQD\n"
+ "DBdzaW1wbGVzYW1scGhwLmNmYXBwcy5pbzEgMB4GCSqGSIb3DQEJARYRZmhhbmlr\n"
+ "QHBpdm90YWwuaW8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC4cn62\n"
+ "E1xLqpN34PmbrKBbkOXFjzWgJ9b+pXuaRft6A339uuIQeoeH5qeSKRVTl32L0gdz\n"
+ "2ZivLwZXW+cqvftVW1tvEHvzJFyxeTW3fCUeCQsebLnA2qRa07RkxTo6Nf244mWW\n"
+ "RDodcoHEfDUSbxfTZ6IExSojSIU2RnD6WllYWFdD1GFpBJOmQB8rAc8wJIBdHFdQ\n"
+ "nX8Ttl7hZ6rtgqEYMzYVMuJ2F2r1HSU1zSAvwpdYP6rRGFRJEfdA9mm3WKfNLSc5\n"
+ "cljz0X/TXy0vVlAV95l9qcfFzPmrkNIst9FZSwpvB49LyAVke04FQPPwLgVH4gph\n"
+ "iJH3jvZ7I+J5lS8VAgMBAAGjUDBOMB0GA1UdDgQWBBTTyP6Cc5HlBJ5+ucVCwGc5\n"
+ "ogKNGzAfBgNVHSMEGDAWgBTTyP6Cc5HlBJ5+ucVCwGc5ogKNGzAMBgNVHRMEBTAD\n"
+ "AQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAvMS4EQeP/ipV4jOG5lO6/tYCb/iJeAduO\n"
+ "nRhkJk0DbX329lDLZhTTL/x/w/9muCVcvLrzEp6PN+VWfw5E5FWtZN0yhGtP9R+v\n"
+ "ZnrV+oc2zGD+no1/ySFOe3EiJCO5dehxKjYEmBRv5sU/LZFKZpozKN/BMEa6CqLu\n"
+ "xbzb7ykxVr7EVFXwltPxzE9TmL9OACNNyF5eJHWMRMllarUvkcXlh4pux4ks9e6z\n"
+ "V9DQBy2zds9f1I3qxg0eX6JnGrXi/ZiCT+lJgVe3ZFXiejiLAiKB04sXW3ti0LW3\n"
+ "lx13Y1YlQ4/tlpgTgfIJxKV6nyPiLoK0nywbMd+vpAirDt2Oc+hk\n" + "-----END CERTIFICATE-----\n");
}
private static PrivateKey idpPrivateKey() {
return privateKey("-----BEGIN PRIVATE KEY-----\n"
+ "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC4cn62E1xLqpN3\n"
+ "4PmbrKBbkOXFjzWgJ9b+pXuaRft6A339uuIQeoeH5qeSKRVTl32L0gdz2ZivLwZX\n"
+ "W+cqvftVW1tvEHvzJFyxeTW3fCUeCQsebLnA2qRa07RkxTo6Nf244mWWRDodcoHE\n"
+ "fDUSbxfTZ6IExSojSIU2RnD6WllYWFdD1GFpBJOmQB8rAc8wJIBdHFdQnX8Ttl7h\n"
+ "Z6rtgqEYMzYVMuJ2F2r1HSU1zSAvwpdYP6rRGFRJEfdA9mm3WKfNLSc5cljz0X/T\n"
+ "Xy0vVlAV95l9qcfFzPmrkNIst9FZSwpvB49LyAVke04FQPPwLgVH4gphiJH3jvZ7\n"
+ "I+J5lS8VAgMBAAECggEBAKyxBlIS7mcp3chvq0RF7B3PHFJMMzkwE+t3pLJcs4cZ\n"
+ "nezh/KbREfP70QjXzk/llnZCvxeIs5vRu24vbdBm79qLHqBuHp8XfHHtuo2AfoAQ\n"
+ "l4h047Xc/+TKMivnPQ0jX9qqndKDLqZDf5wnbslDmlskvF0a/MjsLU0TxtOfo+dB\n"
+ "t55FW11cGqxZwhS5Gnr+cbw3OkHz23b9gEOt9qfwPVepeysbmm9FjU+k4yVa7rAN\n"
+ "xcbzVb6Y7GCITe2tgvvEHmjB9BLmWrH3mZ3Af17YU/iN6TrpPd6Sj3QoS+2wGtAe\n"
+ "HbUs3CKJu7bIHcj4poal6Kh8519S+erJTtqQ8M0ZiEECgYEA43hLYAPaUueFkdfh\n"
+ "9K/7ClH6436CUH3VdizwUXi26fdhhV/I/ot6zLfU2mgEHU22LBECWQGtAFm8kv0P\n"
+ "zPn+qjaR3e62l5PIlSYbnkIidzoDZ2ztu4jF5LgStlTJQPteFEGgZVl5o9DaSZOq\n"
+ "Yd7G3XqXuQ1VGMW58G5FYJPtA1cCgYEAz5TPUtK+R2KXHMjUwlGY9AefQYRYmyX2\n"
+ "Tn/OFgKvY8lpAkMrhPKONq7SMYc8E9v9G7A0dIOXvW7QOYSapNhKU+np3lUafR5F\n"
+ "4ZN0bxZ9qjHbn3AMYeraKjeutHvlLtbHdIc1j3sxe/EzltRsYmiqLdEBW0p6hwWg\n"
+ "tyGhYWVyaXMCgYAfDOKtHpmEy5nOCLwNXKBWDk7DExfSyPqEgSnk1SeS1HP5ctPK\n"
+ "+1st6sIhdiVpopwFc+TwJWxqKdW18tlfT5jVv1E2DEnccw3kXilS9xAhWkfwrEvf\n"
+ "V5I74GydewFl32o+NZ8hdo9GL1I8zO1rIq/et8dSOWGuWf9BtKu/vTGTTQKBgFxU\n"
+ "VjsCnbvmsEwPUAL2hE/WrBFaKocnxXx5AFNt8lEyHtDwy4Sg1nygGcIJ4sD6koQk\n"
+ "RdClT3LkvR04TAiSY80bN/i6ZcPNGUwSaDGZEWAIOSWbkwZijZNFnSGOEgxZX/IG\n"
+ "yd39766vREEMTwEeiMNEOZQ/dmxkJm4OOVe25cLdAoGACOtPnq1Fxay80UYBf4rQ\n"
+ "+bJ9yX1ulB8WIree1hD7OHSB2lRHxrVYWrglrTvkh63Lgx+EcsTV788OsvAVfPPz\n"
+ "BZrn8SdDlQqalMxUBYEFwnsYD3cQ8yOUnijFVC4xNcdDv8OIqVgSk4KKxU5AshaA\n" + "xk6Mox+u8Cc2eAK12H13i+8=\n"
+ "-----END PRIVATE KEY-----\n");
return privateKey(
"-----BEGIN PRIVATE KEY-----\n" + "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC4cn62E1xLqpN3\n"
+ "4PmbrKBbkOXFjzWgJ9b+pXuaRft6A339uuIQeoeH5qeSKRVTl32L0gdz2ZivLwZX\n"
+ "W+cqvftVW1tvEHvzJFyxeTW3fCUeCQsebLnA2qRa07RkxTo6Nf244mWWRDodcoHE\n"
+ "fDUSbxfTZ6IExSojSIU2RnD6WllYWFdD1GFpBJOmQB8rAc8wJIBdHFdQnX8Ttl7h\n"
+ "Z6rtgqEYMzYVMuJ2F2r1HSU1zSAvwpdYP6rRGFRJEfdA9mm3WKfNLSc5cljz0X/T\n"
+ "Xy0vVlAV95l9qcfFzPmrkNIst9FZSwpvB49LyAVke04FQPPwLgVH4gphiJH3jvZ7\n"
+ "I+J5lS8VAgMBAAECggEBAKyxBlIS7mcp3chvq0RF7B3PHFJMMzkwE+t3pLJcs4cZ\n"
+ "nezh/KbREfP70QjXzk/llnZCvxeIs5vRu24vbdBm79qLHqBuHp8XfHHtuo2AfoAQ\n"
+ "l4h047Xc/+TKMivnPQ0jX9qqndKDLqZDf5wnbslDmlskvF0a/MjsLU0TxtOfo+dB\n"
+ "t55FW11cGqxZwhS5Gnr+cbw3OkHz23b9gEOt9qfwPVepeysbmm9FjU+k4yVa7rAN\n"
+ "xcbzVb6Y7GCITe2tgvvEHmjB9BLmWrH3mZ3Af17YU/iN6TrpPd6Sj3QoS+2wGtAe\n"
+ "HbUs3CKJu7bIHcj4poal6Kh8519S+erJTtqQ8M0ZiEECgYEA43hLYAPaUueFkdfh\n"
+ "9K/7ClH6436CUH3VdizwUXi26fdhhV/I/ot6zLfU2mgEHU22LBECWQGtAFm8kv0P\n"
+ "zPn+qjaR3e62l5PIlSYbnkIidzoDZ2ztu4jF5LgStlTJQPteFEGgZVl5o9DaSZOq\n"
+ "Yd7G3XqXuQ1VGMW58G5FYJPtA1cCgYEAz5TPUtK+R2KXHMjUwlGY9AefQYRYmyX2\n"
+ "Tn/OFgKvY8lpAkMrhPKONq7SMYc8E9v9G7A0dIOXvW7QOYSapNhKU+np3lUafR5F\n"
+ "4ZN0bxZ9qjHbn3AMYeraKjeutHvlLtbHdIc1j3sxe/EzltRsYmiqLdEBW0p6hwWg\n"
+ "tyGhYWVyaXMCgYAfDOKtHpmEy5nOCLwNXKBWDk7DExfSyPqEgSnk1SeS1HP5ctPK\n"
+ "+1st6sIhdiVpopwFc+TwJWxqKdW18tlfT5jVv1E2DEnccw3kXilS9xAhWkfwrEvf\n"
+ "V5I74GydewFl32o+NZ8hdo9GL1I8zO1rIq/et8dSOWGuWf9BtKu/vTGTTQKBgFxU\n"
+ "VjsCnbvmsEwPUAL2hE/WrBFaKocnxXx5AFNt8lEyHtDwy4Sg1nygGcIJ4sD6koQk\n"
+ "RdClT3LkvR04TAiSY80bN/i6ZcPNGUwSaDGZEWAIOSWbkwZijZNFnSGOEgxZX/IG\n"
+ "yd39766vREEMTwEeiMNEOZQ/dmxkJm4OOVe25cLdAoGACOtPnq1Fxay80UYBf4rQ\n"
+ "+bJ9yX1ulB8WIree1hD7OHSB2lRHxrVYWrglrTvkh63Lgx+EcsTV788OsvAVfPPz\n"
+ "BZrn8SdDlQqalMxUBYEFwnsYD3cQ8yOUnijFVC4xNcdDv8OIqVgSk4KKxU5AshaA\n"
+ "xk6Mox+u8Cc2eAK12H13i+8=\n" + "-----END PRIVATE KEY-----\n");
}
private static X509Certificate spCertificate() {
return certificate("-----BEGIN CERTIFICATE-----\n" +
"MIICgTCCAeoCCQCuVzyqFgMSyDANBgkqhkiG9w0BAQsFADCBhDELMAkGA1UEBhMC\n" +
"VVMxEzARBgNVBAgMCldhc2hpbmd0b24xEjAQBgNVBAcMCVZhbmNvdXZlcjEdMBsG\n" +
"A1UECgwUU3ByaW5nIFNlY3VyaXR5IFNBTUwxCzAJBgNVBAsMAnNwMSAwHgYDVQQD\n" +
"DBdzcC5zcHJpbmcuc2VjdXJpdHkuc2FtbDAeFw0xODA1MTQxNDMwNDRaFw0yODA1\n" +
"MTExNDMwNDRaMIGEMQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2FzaGluZ3RvbjES\n" +
"MBAGA1UEBwwJVmFuY291dmVyMR0wGwYDVQQKDBRTcHJpbmcgU2VjdXJpdHkgU0FN\n" +
"TDELMAkGA1UECwwCc3AxIDAeBgNVBAMMF3NwLnNwcmluZy5zZWN1cml0eS5zYW1s\n" +
"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDRu7/EI0BlNzMEBFVAcbx+lLos\n" +
"vzIWU+01dGTY8gBdhMQNYKZ92lMceo2CuVJ66cUURPym3i7nGGzoSnAxAre+0YIM\n" +
"+U0razrWtAUE735bkcqELZkOTZLelaoOztmWqRbe5OuEmpewH7cx+kNgcVjdctOG\n" +
"y3Q6x+I4qakY/9qhBQIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAAeViTvHOyQopWEi\n" +
"XOfI2Z9eukwrSknDwq/zscR0YxwwqDBMt/QdAODfSwAfnciiYLkmEjlozWRtOeN+\n" +
"qK7UFgP1bRl5qksrYX5S0z2iGJh0GvonLUt3e20Ssfl5tTEDDnAEUMLfBkyaxEHD\n" +
"RZ/nbTJ7VTeZOSyRoVn5XHhpuJ0B\n" +
"-----END CERTIFICATE-----");
return certificate(
"-----BEGIN CERTIFICATE-----\n" + "MIICgTCCAeoCCQCuVzyqFgMSyDANBgkqhkiG9w0BAQsFADCBhDELMAkGA1UEBhMC\n"
+ "VVMxEzARBgNVBAgMCldhc2hpbmd0b24xEjAQBgNVBAcMCVZhbmNvdXZlcjEdMBsG\n"
+ "A1UECgwUU3ByaW5nIFNlY3VyaXR5IFNBTUwxCzAJBgNVBAsMAnNwMSAwHgYDVQQD\n"
+ "DBdzcC5zcHJpbmcuc2VjdXJpdHkuc2FtbDAeFw0xODA1MTQxNDMwNDRaFw0yODA1\n"
+ "MTExNDMwNDRaMIGEMQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2FzaGluZ3RvbjES\n"
+ "MBAGA1UEBwwJVmFuY291dmVyMR0wGwYDVQQKDBRTcHJpbmcgU2VjdXJpdHkgU0FN\n"
+ "TDELMAkGA1UECwwCc3AxIDAeBgNVBAMMF3NwLnNwcmluZy5zZWN1cml0eS5zYW1s\n"
+ "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDRu7/EI0BlNzMEBFVAcbx+lLos\n"
+ "vzIWU+01dGTY8gBdhMQNYKZ92lMceo2CuVJ66cUURPym3i7nGGzoSnAxAre+0YIM\n"
+ "+U0razrWtAUE735bkcqELZkOTZLelaoOztmWqRbe5OuEmpewH7cx+kNgcVjdctOG\n"
+ "y3Q6x+I4qakY/9qhBQIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAAeViTvHOyQopWEi\n"
+ "XOfI2Z9eukwrSknDwq/zscR0YxwwqDBMt/QdAODfSwAfnciiYLkmEjlozWRtOeN+\n"
+ "qK7UFgP1bRl5qksrYX5S0z2iGJh0GvonLUt3e20Ssfl5tTEDDnAEUMLfBkyaxEHD\n"
+ "RZ/nbTJ7VTeZOSyRoVn5XHhpuJ0B\n" + "-----END CERTIFICATE-----");
}
private static PrivateKey spPrivateKey() {
return privateKey("-----BEGIN PRIVATE KEY-----\n" +
"MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBANG7v8QjQGU3MwQE\n" +
"VUBxvH6Uuiy/MhZT7TV0ZNjyAF2ExA1gpn3aUxx6jYK5UnrpxRRE/KbeLucYbOhK\n" +
"cDECt77Rggz5TStrOta0BQTvfluRyoQtmQ5Nkt6Vqg7O2ZapFt7k64Sal7AftzH6\n" +
"Q2BxWN1y04bLdDrH4jipqRj/2qEFAgMBAAECgYEAj4ExY1jjdN3iEDuOwXuRB+Nn\n" +
"x7pC4TgntE2huzdKvLJdGvIouTArce8A6JM5NlTBvm69mMepvAHgcsiMH1zGr5J5\n" +
"wJz23mGOyhM1veON41/DJTVG+cxq4soUZhdYy3bpOuXGMAaJ8QLMbQQoivllNihd\n" +
"vwH0rNSK8LTYWWPZYIECQQDxct+TFX1VsQ1eo41K0T4fu2rWUaxlvjUGhK6HxTmY\n" +
"8OMJptunGRJL1CUjIb45Uz7SP8TPz5FwhXWsLfS182kRAkEA3l+Qd9C9gdpUh1uX\n" +
"oPSNIxn5hFUrSTW1EwP9QH9vhwb5Vr8Jrd5ei678WYDLjUcx648RjkjhU9jSMzIx\n" +
"EGvYtQJBAMm/i9NR7IVyyNIgZUpz5q4LI21rl1r4gUQuD8vA36zM81i4ROeuCly0\n" +
"KkfdxR4PUfnKcQCX11YnHjk9uTFj75ECQEFY/gBnxDjzqyF35hAzrYIiMPQVfznt\n" +
"YX/sDTE2AdVBVGaMj1Cb51bPHnNC6Q5kXKQnj/YrLqRQND09Q7ParX0CQQC5NxZr\n" +
"9jKqhHj8yQD6PlXTsY4Occ7DH6/IoDenfdEVD5qlet0zmd50HatN2Jiqm5ubN7CM\n" +
"INrtuLp4YHbgk1mi\n" +
"-----END PRIVATE KEY-----");
return privateKey(
"-----BEGIN PRIVATE KEY-----\n" + "MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBANG7v8QjQGU3MwQE\n"
+ "VUBxvH6Uuiy/MhZT7TV0ZNjyAF2ExA1gpn3aUxx6jYK5UnrpxRRE/KbeLucYbOhK\n"
+ "cDECt77Rggz5TStrOta0BQTvfluRyoQtmQ5Nkt6Vqg7O2ZapFt7k64Sal7AftzH6\n"
+ "Q2BxWN1y04bLdDrH4jipqRj/2qEFAgMBAAECgYEAj4ExY1jjdN3iEDuOwXuRB+Nn\n"
+ "x7pC4TgntE2huzdKvLJdGvIouTArce8A6JM5NlTBvm69mMepvAHgcsiMH1zGr5J5\n"
+ "wJz23mGOyhM1veON41/DJTVG+cxq4soUZhdYy3bpOuXGMAaJ8QLMbQQoivllNihd\n"
+ "vwH0rNSK8LTYWWPZYIECQQDxct+TFX1VsQ1eo41K0T4fu2rWUaxlvjUGhK6HxTmY\n"
+ "8OMJptunGRJL1CUjIb45Uz7SP8TPz5FwhXWsLfS182kRAkEA3l+Qd9C9gdpUh1uX\n"
+ "oPSNIxn5hFUrSTW1EwP9QH9vhwb5Vr8Jrd5ei678WYDLjUcx648RjkjhU9jSMzIx\n"
+ "EGvYtQJBAMm/i9NR7IVyyNIgZUpz5q4LI21rl1r4gUQuD8vA36zM81i4ROeuCly0\n"
+ "KkfdxR4PUfnKcQCX11YnHjk9uTFj75ECQEFY/gBnxDjzqyF35hAzrYIiMPQVfznt\n"
+ "YX/sDTE2AdVBVGaMj1Cb51bPHnNC6Q5kXKQnj/YrLqRQND09Q7ParX0CQQC5NxZr\n"
+ "9jKqhHj8yQD6PlXTsY4Occ7DH6/IoDenfdEVD5qlet0zmd50HatN2Jiqm5ubN7CM\n" + "INrtuLp4YHbgk1mi\n"
+ "-----END PRIVATE KEY-----");
}
}
@@ -44,15 +44,13 @@ public class DefaultSaml2AuthenticatedPrincipalTests {
Map<String, List<Object>> attributes = new LinkedHashMap<>();
attributes.put("email", Arrays.asList("john.doe@example.com", "doe.john@example.com"));
assertThatCode(() -> new DefaultSaml2AuthenticatedPrincipal(null, attributes))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("name cannot be null");
.isInstanceOf(IllegalArgumentException.class).hasMessageContaining("name cannot be null");
}
@Test
public void createDefaultSaml2AuthenticatedPrincipalWhenAttributesNullThenException() {
assertThatCode(() -> new DefaultSaml2AuthenticatedPrincipal("user", null))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("attributes cannot be null");
.isInstanceOf(IllegalArgumentException.class).hasMessageContaining("attributes cannot be null");
}
@Test
@@ -87,4 +85,5 @@ public class DefaultSaml2AuthenticatedPrincipalTests {
assertThat((Boolean) registrationInfo.get(0)).isEqualTo(registered);
assertThat((Instant) registrationInfo.get(1)).isEqualTo(registeredDate);
}
}
@@ -97,7 +97,9 @@ import static org.springframework.util.StringUtils.hasText;
public class OpenSamlAuthenticationProviderTests {
private static String DESTINATION = "https://localhost/login/saml2/sso/idp-alias";
private static String RELYING_PARTY_ENTITY_ID = "https://localhost/saml2/service-provider-metadata/idp-alias";
private static String ASSERTING_PARTY_ENTITY_ID = "https://some.idp.test/saml2/idp";
private OpenSamlAuthenticationProvider provider = new OpenSamlAuthenticationProvider();
@@ -109,7 +111,8 @@ public class OpenSamlAuthenticationProviderTests {
public void supportsWhenSaml2AuthenticationTokenThenReturnTrue() {
assertThat(this.provider.supports(Saml2AuthenticationToken.class))
.withFailMessage(OpenSamlAuthenticationProvider.class + "should support " + Saml2AuthenticationToken.class)
.withFailMessage(
OpenSamlAuthenticationProvider.class + "should support " + Saml2AuthenticationToken.class)
.isTrue();
}
@@ -151,8 +154,7 @@ public class OpenSamlAuthenticationProviderTests {
@Test
public void authenticateWhenNoAssertionsPresentThenThrowAuthenticationException() {
this.exception.expect(
authenticationMatcher(Saml2ErrorCodes.MALFORMED_RESPONSE_DATA, "No assertions found in response.")
);
authenticationMatcher(Saml2ErrorCodes.MALFORMED_RESPONSE_DATA, "No assertions found in response."));
Saml2AuthenticationToken token = token(response(), assertingPartySigningCredential());
this.provider.authenticate(token);
@@ -174,11 +176,7 @@ public class OpenSamlAuthenticationProviderTests {
Response response = response();
Assertion assertion = assertion();
assertion
.getSubject()
.getSubjectConfirmations()
.get(0)
.getSubjectConfirmationData()
assertion.getSubject().getSubjectConfirmations().get(0).getSubjectConfirmationData()
.setNotOnOrAfter(DateTime.now().minus(Duration.standardDays(3)));
signed(assertion, assertingPartySigningCredential(), RELYING_PARTY_ENTITY_ID);
response.getAssertions().add(assertion);
@@ -187,7 +185,7 @@ public class OpenSamlAuthenticationProviderTests {
}
@Test
public void authenticateWhenMissingSubjectThenThrowAuthenticationException() {
public void authenticateWhenMissingSubjectThenThrowAuthenticationException() {
this.exception.expect(authenticationMatcher(Saml2ErrorCodes.SUBJECT_NOT_FOUND));
Response response = response();
@@ -205,10 +203,7 @@ public class OpenSamlAuthenticationProviderTests {
Response response = response();
Assertion assertion = assertion();
assertion
.getSubject()
.getNameID()
.setValue(null);
assertion.getSubject().getNameID().setValue(null);
signed(assertion, assertingPartySigningCredential(), RELYING_PARTY_ENTITY_ID);
response.getAssertions().add(assertion);
Saml2AuthenticationToken token = token(response, relyingPartyVerifyingCredential());
@@ -219,9 +214,8 @@ public class OpenSamlAuthenticationProviderTests {
public void authenticateWhenAssertionContainsValidationAddressThenItSucceeds() throws Exception {
Response response = response();
Assertion assertion = assertion();
assertion.getSubject().getSubjectConfirmations().forEach(
sc -> sc.getSubjectConfirmationData().setAddress("10.10.10.10")
);
assertion.getSubject().getSubjectConfirmations()
.forEach(sc -> sc.getSubjectConfirmationData().setAddress("10.10.10.10"));
signed(assertion, assertingPartySigningCredential(), RELYING_PARTY_ENTITY_ID);
response.getAssertions().add(assertion);
Saml2AuthenticationToken token = token(response, relyingPartyVerifyingCredential());
@@ -268,11 +262,14 @@ public class OpenSamlAuthenticationProviderTests {
when(marshaller.marshall(any(XMLObject.class))).thenReturn(attributeElement);
try {
XMLObjectProviderRegistrySupport.getMarshallerFactory().registerMarshaller(AttributeValue.DEFAULT_ELEMENT_NAME, marshaller);
XMLObjectProviderRegistrySupport.getMarshallerFactory()
.registerMarshaller(AttributeValue.DEFAULT_ELEMENT_NAME, marshaller);
this.provider.authenticate(token);
verify(marshaller, atLeastOnce()).marshall(any(XMLObject.class));
} finally {
XMLObjectProviderRegistrySupport.getMarshallerFactory().deregisterMarshaller(AttributeValue.DEFAULT_ELEMENT_NAME);
}
finally {
XMLObjectProviderRegistrySupport.getMarshallerFactory()
.deregisterMarshaller(AttributeValue.DEFAULT_ELEMENT_NAME);
}
}
@@ -293,7 +290,8 @@ public class OpenSamlAuthenticationProviderTests {
Assertion assertion = signed(assertion(), assertingPartySigningCredential(), RELYING_PARTY_ENTITY_ID);
EncryptedAssertion encryptedAssertion = encrypted(assertion, assertingPartyEncryptingCredential());
response.getEncryptedAssertions().add(encryptedAssertion);
Saml2AuthenticationToken token = token(response, relyingPartyVerifyingCredential(), relyingPartyDecryptingCredential());
Saml2AuthenticationToken token = token(response, relyingPartyVerifyingCredential(),
relyingPartyDecryptingCredential());
this.provider.authenticate(token);
}
@@ -303,7 +301,8 @@ public class OpenSamlAuthenticationProviderTests {
EncryptedAssertion encryptedAssertion = encrypted(assertion(), assertingPartyEncryptingCredential());
response.getEncryptedAssertions().add(encryptedAssertion);
signed(response, assertingPartySigningCredential(), RELYING_PARTY_ENTITY_ID);
Saml2AuthenticationToken token = token(response, relyingPartyVerifyingCredential(), relyingPartyDecryptingCredential());
Saml2AuthenticationToken token = token(response, relyingPartyVerifyingCredential(),
relyingPartyDecryptingCredential());
this.provider.authenticate(token);
}
@@ -317,16 +316,15 @@ public class OpenSamlAuthenticationProviderTests {
assertion.getSubject().setEncryptedID(encryptedID);
response.getAssertions().add(assertion);
signed(assertion, assertingPartySigningCredential(), RELYING_PARTY_ENTITY_ID);
Saml2AuthenticationToken token = token(response, relyingPartyVerifyingCredential(), relyingPartyDecryptingCredential());
Saml2AuthenticationToken token = token(response, relyingPartyVerifyingCredential(),
relyingPartyDecryptingCredential());
this.provider.authenticate(token);
}
@Test
public void authenticateWhenDecryptionKeysAreMissingThenThrowAuthenticationException() throws Exception {
this.exception.expect(
authenticationMatcher(Saml2ErrorCodes.DECRYPTION_ERROR, "Failed to decrypt EncryptedData")
);
this.exception
.expect(authenticationMatcher(Saml2ErrorCodes.DECRYPTION_ERROR, "Failed to decrypt EncryptedData"));
Response response = response();
EncryptedAssertion encryptedAssertion = encrypted(assertion(), assertingPartyEncryptingCredential());
@@ -337,9 +335,8 @@ public class OpenSamlAuthenticationProviderTests {
@Test
public void authenticateWhenDecryptionKeysAreWrongThenThrowAuthenticationException() throws Exception {
this.exception.expect(
authenticationMatcher(Saml2ErrorCodes.DECRYPTION_ERROR, "Failed to decrypt EncryptedData")
);
this.exception
.expect(authenticationMatcher(Saml2ErrorCodes.DECRYPTION_ERROR, "Failed to decrypt EncryptedData"));
Response response = response();
EncryptedAssertion encryptedAssertion = encrypted(assertion(), assertingPartyEncryptingCredential());
@@ -354,7 +351,8 @@ public class OpenSamlAuthenticationProviderTests {
Assertion assertion = signed(assertion(), assertingPartySigningCredential(), RELYING_PARTY_ENTITY_ID);
EncryptedAssertion encryptedAssertion = encrypted(assertion, assertingPartyEncryptingCredential());
response.getEncryptedAssertions().add(encryptedAssertion);
Saml2AuthenticationToken token = token(response, relyingPartyVerifyingCredential(), relyingPartyDecryptingCredential());
Saml2AuthenticationToken token = token(response, relyingPartyVerifyingCredential(),
relyingPartyDecryptingCredential());
Saml2Authentication authentication = (Saml2Authentication) this.provider.authenticate(token);
// the following code will throw an exception if authentication isn't serializable
@@ -409,8 +407,7 @@ public class OpenSamlAuthenticationProviderTests {
@Test
public void setConditionValidatorsWhenNullOrEmptyThenIllegalArgument() {
assertThatCode(() -> this.provider.setConditionValidators(null))
.isInstanceOf(IllegalArgumentException.class);
assertThatCode(() -> this.provider.setConditionValidators(null)).isInstanceOf(IllegalArgumentException.class);
assertThatCode(() -> this.provider.setConditionValidators(Collections.emptyList()))
.isInstanceOf(IllegalArgumentException.class);
@@ -425,7 +422,8 @@ public class OpenSamlAuthenticationProviderTests {
Marshaller marshaller = getMarshallerFactory().getMarshaller(object);
Element element = marshaller.marshall(object);
return SerializeSupport.nodeToString(element);
} catch (MarshallingException e) {
}
catch (MarshallingException e) {
throw new Saml2Exception(e);
}
}
@@ -455,7 +453,7 @@ public class OpenSamlAuthenticationProviderTests {
@Override
public void describeTo(Description desc) {
String excepting = "Saml2AuthenticationException[code="+code+"; description="+description+"]";
String excepting = "Saml2AuthenticationException[code=" + code + "; description=" + description + "]";
desc.appendText(excepting);
}
@@ -468,8 +466,8 @@ public class OpenSamlAuthenticationProviderTests {
}
private Saml2AuthenticationToken token(String payload, Saml2X509Credential... credentials) {
return new Saml2AuthenticationToken(payload,
DESTINATION, ASSERTING_PARTY_ENTITY_ID, RELYING_PARTY_ENTITY_ID, Arrays.asList(credentials));
return new Saml2AuthenticationToken(payload, DESTINATION, ASSERTING_PARTY_ENTITY_ID, RELYING_PARTY_ENTITY_ID,
Arrays.asList(credentials));
}
private static Element element(String xml) throws Exception {
@@ -478,4 +476,5 @@ public class OpenSamlAuthenticationProviderTests {
Document doc = builder.parse(new InputSource(new StringReader(xml)));
return doc.getDocumentElement();
}
}
@@ -57,10 +57,13 @@ import static org.springframework.security.saml2.provider.service.registration.S
public class OpenSamlAuthenticationRequestFactoryTests {
private OpenSamlAuthenticationRequestFactory factory;
private Saml2AuthenticationRequestContext.Builder contextBuilder;
private Saml2AuthenticationRequestContext context;
private RelyingPartyRegistration.Builder relyingPartyRegistrationBuilder;
private RelyingPartyRegistration relyingPartyRegistration;
private AuthnRequestUnmarshaller unmarshaller = (AuthnRequestUnmarshaller) getUnmarshallerFactory()
@@ -74,30 +77,27 @@ public class OpenSamlAuthenticationRequestFactoryTests {
this.relyingPartyRegistrationBuilder = RelyingPartyRegistration.withRegistrationId("id")
.assertionConsumerServiceLocation("template")
.providerDetails(c -> c.webSsoUrl("https://destination/sso"))
.providerDetails(c -> c.entityId("remote-entity-id"))
.localEntityIdTemplate("local-entity-id")
.providerDetails(c -> c.entityId("remote-entity-id")).localEntityIdTemplate("local-entity-id")
.credentials(c -> c.add(relyingPartySigningCredential()));
this.relyingPartyRegistration = this.relyingPartyRegistrationBuilder.build();
contextBuilder = Saml2AuthenticationRequestContext.builder()
.issuer("https://issuer")
.relyingPartyRegistration(relyingPartyRegistration)
.assertionConsumerServiceUrl("https://issuer/sso");
contextBuilder = Saml2AuthenticationRequestContext.builder().issuer("https://issuer")
.relyingPartyRegistration(relyingPartyRegistration).assertionConsumerServiceUrl("https://issuer/sso");
context = contextBuilder.build();
factory = new OpenSamlAuthenticationRequestFactory();
}
@Test
public void createAuthenticationRequestWhenInvokingDeprecatedMethodThenReturnsXML() {
Saml2AuthenticationRequest request = Saml2AuthenticationRequest.withAuthenticationRequestContext(context).build();
Saml2AuthenticationRequest request = Saml2AuthenticationRequest.withAuthenticationRequestContext(context)
.build();
String result = factory.createAuthenticationRequest(request);
assertThat(result.replace("\n", "")).startsWith("<?xml version=\"1.0\" encoding=\"UTF-8\"?><saml2p:AuthnRequest");
assertThat(result.replace("\n", ""))
.startsWith("<?xml version=\"1.0\" encoding=\"UTF-8\"?><saml2p:AuthnRequest");
}
@Test
public void createRedirectAuthenticationRequestWhenUsingContextThenAllValuesAreSet() {
context = contextBuilder
.relayState("Relay State Value")
.build();
context = contextBuilder.relayState("Relay State Value").build();
Saml2RedirectAuthenticationRequest result = factory.createRedirectAuthenticationRequest(context);
assertThat(result.getSamlRequest()).isNotEmpty();
assertThat(result.getRelayState()).isEqualTo("Relay State Value");
@@ -109,13 +109,9 @@ public class OpenSamlAuthenticationRequestFactoryTests {
@Test
public void createRedirectAuthenticationRequestWhenNotSignRequestThenNoSignatureIsPresent() {
context = contextBuilder
.relayState("Relay State Value")
.relyingPartyRegistration(
withRelyingPartyRegistration(relyingPartyRegistration)
.providerDetails(c -> c.signAuthNRequest(false))
.build()
)
context = contextBuilder.relayState("Relay State Value")
.relyingPartyRegistration(withRelyingPartyRegistration(relyingPartyRegistration)
.providerDetails(c -> c.signAuthNRequest(false)).build())
.build();
Saml2RedirectAuthenticationRequest result = factory.createRedirectAuthenticationRequest(context);
assertThat(result.getSamlRequest()).isNotEmpty();
@@ -127,37 +123,26 @@ public class OpenSamlAuthenticationRequestFactoryTests {
@Test
public void createPostAuthenticationRequestWhenNotSignRequestThenNoSignatureIsPresent() {
context = contextBuilder
.relayState("Relay State Value")
.relyingPartyRegistration(
withRelyingPartyRegistration(relyingPartyRegistration)
.providerDetails(c -> c.signAuthNRequest(false))
.build()
)
context = contextBuilder.relayState("Relay State Value")
.relyingPartyRegistration(withRelyingPartyRegistration(relyingPartyRegistration)
.providerDetails(c -> c.signAuthNRequest(false)).build())
.build();
Saml2PostAuthenticationRequest result = factory.createPostAuthenticationRequest(context);
assertThat(result.getSamlRequest()).isNotEmpty();
assertThat(result.getRelayState()).isEqualTo("Relay State Value");
assertThat(result.getBinding()).isEqualTo(POST);
assertThat(new String(samlDecode(result.getSamlRequest()), UTF_8))
.doesNotContain("ds:Signature");
assertThat(new String(samlDecode(result.getSamlRequest()), UTF_8)).doesNotContain("ds:Signature");
}
@Test
public void createPostAuthenticationRequestWhenSignRequestThenSignatureIsPresent() {
context = contextBuilder
.relayState("Relay State Value")
.relyingPartyRegistration(
withRelyingPartyRegistration(relyingPartyRegistration)
.build()
)
.build();
context = contextBuilder.relayState("Relay State Value")
.relyingPartyRegistration(withRelyingPartyRegistration(relyingPartyRegistration).build()).build();
Saml2PostAuthenticationRequest result = factory.createPostAuthenticationRequest(context);
assertThat(result.getSamlRequest()).isNotEmpty();
assertThat(result.getRelayState()).isEqualTo("Relay State Value");
assertThat(result.getBinding()).isEqualTo(POST);
assertThat(new String(samlDecode(result.getSamlRequest()), UTF_8))
.contains("ds:Signature");
assertThat(new String(samlDecode(result.getSamlRequest()), UTF_8)).contains("ds:Signature");
}
@Test
@@ -182,9 +167,10 @@ public class OpenSamlAuthenticationRequestFactoryTests {
@Test
public void createPostAuthenticationRequestWhenAuthnRequestConsumerThenUses() {
Function<Saml2AuthenticationRequestContext, Consumer<AuthnRequest>> authnRequestConsumerResolver =
mock(Function.class);
when(authnRequestConsumerResolver.apply(this.context)).thenReturn(authnRequest -> {});
Function<Saml2AuthenticationRequestContext, Consumer<AuthnRequest>> authnRequestConsumerResolver = mock(
Function.class);
when(authnRequestConsumerResolver.apply(this.context)).thenReturn(authnRequest -> {
});
this.factory.setAuthnRequestConsumerResolver(authnRequestConsumerResolver);
this.factory.createPostAuthenticationRequest(this.context);
@@ -193,9 +179,10 @@ public class OpenSamlAuthenticationRequestFactoryTests {
@Test
public void createRedirectAuthenticationRequestWhenAuthnRequestConsumerThenUses() {
Function<Saml2AuthenticationRequestContext, Consumer<AuthnRequest>> authnRequestConsumerResolver =
mock(Function.class);
when(authnRequestConsumerResolver.apply(this.context)).thenReturn(authnRequest -> {});
Function<Saml2AuthenticationRequestContext, Consumer<AuthnRequest>> authnRequestConsumerResolver = mock(
Function.class);
when(authnRequestConsumerResolver.apply(this.context)).thenReturn(authnRequest -> {
});
this.factory.setAuthnRequestConsumerResolver(authnRequestConsumerResolver);
this.factory.createRedirectAuthenticationRequest(this.context);
@@ -211,11 +198,9 @@ public class OpenSamlAuthenticationRequestFactoryTests {
@Test
public void createPostAuthenticationRequestWhenAssertionConsumerServiceBindingThenUses() {
RelyingPartyRegistration relyingPartyRegistration = this.relyingPartyRegistrationBuilder
.assertionConsumerServiceBinding(REDIRECT)
.build();
.assertionConsumerServiceBinding(REDIRECT).build();
Saml2AuthenticationRequestContext context = this.contextBuilder
.relyingPartyRegistration(relyingPartyRegistration)
.build();
.relyingPartyRegistration(relyingPartyRegistration).build();
Saml2PostAuthenticationRequest request = this.factory.createPostAuthenticationRequest(context);
String samlRequest = request.getSamlRequest();
String inflated = new String(samlDecode(samlRequest));
@@ -223,9 +208,9 @@ public class OpenSamlAuthenticationRequestFactoryTests {
}
private AuthnRequest getAuthNRequest(Saml2MessageBinding binding) {
AbstractSaml2AuthenticationRequest result = (binding == REDIRECT) ?
factory.createRedirectAuthenticationRequest(context) :
factory.createPostAuthenticationRequest(context);
AbstractSaml2AuthenticationRequest result = (binding == REDIRECT)
? factory.createRedirectAuthenticationRequest(context)
: factory.createPostAuthenticationRequest(context);
String samlRequest = result.getSamlRequest();
assertThat(samlRequest).isNotEmpty();
if (result.getBinding() == REDIRECT) {
@@ -235,8 +220,7 @@ public class OpenSamlAuthenticationRequestFactoryTests {
samlRequest = new String(samlDecode(samlRequest), UTF_8);
}
try {
Document document = getParserPool().parse(
new ByteArrayInputStream(samlRequest.getBytes(UTF_8)));
Document document = getParserPool().parse(new ByteArrayInputStream(samlRequest.getBytes(UTF_8)));
Element element = document.getDocumentElement();
return (AuthnRequest) this.unmarshaller.unmarshall(element);
}
@@ -244,4 +228,5 @@ public class OpenSamlAuthenticationRequestFactoryTests {
throw new Saml2Exception(e);
}
}
}
@@ -35,20 +35,16 @@ public class Saml2AuthenticationRequestFactoryTests {
private RelyingPartyRegistration registration = RelyingPartyRegistration.withRegistrationId("id")
.assertionConsumerServiceUrlTemplate("template")
.providerDetails(c -> c.webSsoUrl("https://example.com/destination"))
.providerDetails(c -> c.entityId("remote-entity-id"))
.localEntityIdTemplate("local-entity-id")
.credentials(c -> c.add(relyingPartySigningCredential()))
.build();
.providerDetails(c -> c.entityId("remote-entity-id")).localEntityIdTemplate("local-entity-id")
.credentials(c -> c.add(relyingPartySigningCredential())).build();
@Test
public void createAuthenticationRequestParametersWhenRedirectDefaultIsUsedMessageIsDeflatedAndEncoded() {
final String value = "Test String: "+ UUID.randomUUID().toString();
final String value = "Test String: " + UUID.randomUUID().toString();
Saml2AuthenticationRequestFactory factory = request -> value;
Saml2AuthenticationRequestContext request = Saml2AuthenticationRequestContext.builder()
.relyingPartyRegistration(registration)
.issuer("https://example.com/issuer")
.assertionConsumerServiceUrl("https://example.com/acs-url")
.build();
.relyingPartyRegistration(registration).issuer("https://example.com/issuer")
.assertionConsumerServiceUrl("https://example.com/acs-url").build();
Saml2RedirectAuthenticationRequest response = factory.createRedirectAuthenticationRequest(request);
String resultValue = response.getSamlRequest();
byte[] decoded = samlDecode(resultValue);
@@ -58,16 +54,15 @@ public class Saml2AuthenticationRequestFactoryTests {
@Test
public void createAuthenticationRequestParametersWhenPostDefaultIsUsedMessageIsEncoded() {
final String value = "Test String: "+ UUID.randomUUID().toString();
final String value = "Test String: " + UUID.randomUUID().toString();
Saml2AuthenticationRequestFactory factory = request -> value;
Saml2AuthenticationRequestContext request = Saml2AuthenticationRequestContext.builder()
.relyingPartyRegistration(registration)
.issuer("https://example.com/issuer")
.assertionConsumerServiceUrl("https://example.com/acs-url")
.build();
.relyingPartyRegistration(registration).issuer("https://example.com/issuer")
.assertionConsumerServiceUrl("https://example.com/acs-url").build();
Saml2PostAuthenticationRequest response = factory.createPostAuthenticationRequest(request);
String resultValue = response.getSamlRequest();
byte[] decoded = samlDecode(resultValue);
assertThat(new String(decoded)).isEqualTo(value);
}
}
@@ -21,6 +21,7 @@ import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import java.util.UUID;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.namespace.QName;
@@ -81,16 +82,21 @@ import org.springframework.security.saml2.core.Saml2X509Credential;
import static org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport.getBuilderFactory;
final class TestOpenSamlObjects {
static {
OpenSamlInitializationService.initialize();
}
private static String USERNAME = "test@saml.user";
private static String DESTINATION = "https://localhost/login/saml2/sso/idp-alias";
private static String RELYING_PARTY_ENTITY_ID = "https://localhost/saml2/service-provider-metadata/idp-alias";
private static String ASSERTING_PARTY_ENTITY_ID = "https://some.idp.test/saml2/idp";
private static SecretKey SECRET_KEY =
new SecretKeySpec(Base64.getDecoder().decode("shOnwNMoCv88HKMEa91+FlYoD5RNvzMTAL5LGxZKIFk="), "AES");
private static SecretKey SECRET_KEY = new SecretKeySpec(
Base64.getDecoder().decode("shOnwNMoCv88HKMEa91+FlYoD5RNvzMTAL5LGxZKIFk="), "AES");
static Response response() {
return response(DESTINATION, ASSERTING_PARTY_ENTITY_ID);
@@ -98,7 +104,7 @@ final class TestOpenSamlObjects {
static Response response(String destination, String issuerEntityId) {
Response response = build(Response.DEFAULT_ELEMENT_NAME);
response.setID("R"+UUID.randomUUID().toString());
response.setID("R" + UUID.randomUUID().toString());
response.setIssueInstant(DateTime.now());
response.setVersion(SAMLVersion.VERSION_20);
response.setID("_" + UUID.randomUUID().toString());
@@ -111,14 +117,9 @@ final class TestOpenSamlObjects {
return assertion(USERNAME, ASSERTING_PARTY_ENTITY_ID, RELYING_PARTY_ENTITY_ID, DESTINATION);
}
static Assertion assertion(
String username,
String issuerEntityId,
String recipientEntityId,
String recipientUri
) {
static Assertion assertion(String username, String issuerEntityId, String recipientEntityId, String recipientUri) {
Assertion assertion = build(Assertion.DEFAULT_ELEMENT_NAME);
assertion.setID("A"+ UUID.randomUUID().toString());
assertion.setID("A" + UUID.randomUUID().toString());
assertion.setIssueInstant(DateTime.now());
assertion.setVersion(SAMLVersion.VERSION_20);
assertion.setIssueInstant(DateTime.now());
@@ -135,7 +136,6 @@ final class TestOpenSamlObjects {
return assertion;
}
static Issuer issuer(String entityId) {
Issuer issuer = build(Issuer.DEFAULT_ELEMENT_NAME);
issuer.setValue(entityId);
@@ -184,7 +184,8 @@ final class TestOpenSamlObjects {
return cred;
}
static Credential getSigningCredential(org.springframework.security.saml2.credentials.Saml2X509Credential credential, String entityId) {
static Credential getSigningCredential(
org.springframework.security.saml2.credentials.Saml2X509Credential credential, String entityId) {
BasicCredential cred = getBasicCredential(credential);
cred.setEntityId(entityId);
cred.setUsageType(UsageType.SIGNING);
@@ -192,17 +193,12 @@ final class TestOpenSamlObjects {
}
static BasicCredential getBasicCredential(Saml2X509Credential credential) {
return CredentialSupport.getSimpleCredential(
credential.getCertificate(),
credential.getPrivateKey()
);
return CredentialSupport.getSimpleCredential(credential.getCertificate(), credential.getPrivateKey());
}
static BasicCredential getBasicCredential(org.springframework.security.saml2.credentials.Saml2X509Credential credential) {
return CredentialSupport.getSimpleCredential(
credential.getCertificate(),
credential.getPrivateKey()
);
static BasicCredential getBasicCredential(
org.springframework.security.saml2.credentials.Saml2X509Credential credential) {
return CredentialSupport.getSimpleCredential(credential.getCertificate(), credential.getPrivateKey());
}
static <T extends SignableSAMLObject> T signed(T signable, Saml2X509Credential credential, String entityId) {
@@ -214,14 +210,16 @@ final class TestOpenSamlObjects {
parameters.setSignatureCanonicalizationAlgorithm(SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS);
try {
SignatureSupport.signObject(signable, parameters);
} catch (MarshallingException | SignatureException | SecurityException e) {
}
catch (MarshallingException | SignatureException | SecurityException e) {
throw new Saml2Exception(e);
}
return signable;
}
static <T extends SignableSAMLObject> T signed(T signable, org.springframework.security.saml2.credentials.Saml2X509Credential credential, String entityId) {
static <T extends SignableSAMLObject> T signed(T signable,
org.springframework.security.saml2.credentials.Saml2X509Credential credential, String entityId) {
SignatureSigningParameters parameters = new SignatureSigningParameters();
Credential signingCredential = getSigningCredential(credential, entityId);
parameters.setSigningCredential(signingCredential);
@@ -230,7 +228,8 @@ final class TestOpenSamlObjects {
parameters.setSignatureCanonicalizationAlgorithm(SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS);
try {
SignatureSupport.signObject(signable, parameters);
} catch (MarshallingException | SignatureException | SecurityException e) {
}
catch (MarshallingException | SignatureException | SecurityException e) {
throw new Saml2Exception(e);
}
@@ -248,7 +247,8 @@ final class TestOpenSamlObjects {
}
}
static EncryptedAssertion encrypted(Assertion assertion, org.springframework.security.saml2.credentials.Saml2X509Credential credential) {
static EncryptedAssertion encrypted(Assertion assertion,
org.springframework.security.saml2.credentials.Saml2X509Credential credential) {
X509Certificate certificate = credential.getCertificate();
Encrypter encrypter = getEncrypter(certificate);
try {
@@ -270,7 +270,8 @@ final class TestOpenSamlObjects {
}
}
static EncryptedID encrypted(NameID nameId, org.springframework.security.saml2.credentials.Saml2X509Credential credential) {
static EncryptedID encrypted(NameID nameId,
org.springframework.security.saml2.credentials.Saml2X509Credential credential) {
X509Certificate certificate = credential.getCertificate();
Encrypter encrypter = getEncrypter(certificate);
try {
@@ -347,14 +348,16 @@ final class TestOpenSamlObjects {
Attribute registeredAttr = attributeBuilder.buildObject();
registeredAttr.setName("registered");
XSBoolean registered = new XSBooleanBuilder().buildObject(AttributeValue.DEFAULT_ELEMENT_NAME, XSBoolean.TYPE_NAME);
XSBoolean registered = new XSBooleanBuilder().buildObject(AttributeValue.DEFAULT_ELEMENT_NAME,
XSBoolean.TYPE_NAME);
registered.setValue(new XSBooleanValue(true, false));
registeredAttr.getAttributeValues().add(registered);
attrStmt2.getAttributes().add(registeredAttr);
Attribute registeredDateAttr = attributeBuilder.buildObject();
registeredDateAttr.setName("registeredDate");
XSDateTime registeredDate = new XSDateTimeBuilder().buildObject(AttributeValue.DEFAULT_ELEMENT_NAME, XSDateTime.TYPE_NAME);
XSDateTime registeredDate = new XSDateTimeBuilder().buildObject(AttributeValue.DEFAULT_ELEMENT_NAME,
XSDateTime.TYPE_NAME);
registeredDate.setValue(DateTime.parse("1970-01-01T00:00:00Z"));
registeredDateAttr.getAttributeValues().add(registeredDate);
attrStmt2.getAttributes().add(registeredDateAttr);
@@ -367,4 +370,5 @@ final class TestOpenSamlObjects {
static <T extends XMLObject> T build(QName qName) {
return (T) getBuilderFactory().getBuilder(qName).buildObject(qName);
}
}
@@ -22,11 +22,11 @@ import static org.springframework.security.saml2.provider.service.registration.T
* Test {@link Saml2AuthenticationRequestContext}s
*/
public class TestSaml2AuthenticationRequestContexts {
public static Saml2AuthenticationRequestContext.Builder authenticationRequestContext() {
return Saml2AuthenticationRequestContext.builder()
.relayState("relayState")
.issuer("issuer")
return Saml2AuthenticationRequestContext.builder().relayState("relayState").issuer("issuer")
.relyingPartyRegistration(relyingPartyRegistration().build())
.assertionConsumerServiceUrl("assertionConsumerServiceUrl");
}
}
@@ -34,20 +34,15 @@ public class OpenSamlMetadataResolverTests {
@Test
public void resolveWhenRelyingPartyThenMetadataMatches() {
// given
RelyingPartyRegistration relyingPartyRegistration = full()
.assertionConsumerServiceBinding(REDIRECT)
.build();
RelyingPartyRegistration relyingPartyRegistration = full().assertionConsumerServiceBinding(REDIRECT).build();
OpenSamlMetadataResolver openSamlMetadataResolver = new OpenSamlMetadataResolver();
// when
String metadata = openSamlMetadataResolver.resolve(relyingPartyRegistration);
// then
assertThat(metadata)
.contains("<EntityDescriptor")
.contains("entityID=\"rp-entity-id\"")
.contains("WantAssertionsSigned=\"true\"")
.contains("<md:KeyDescriptor use=\"signing\">")
assertThat(metadata).contains("<EntityDescriptor").contains("entityID=\"rp-entity-id\"")
.contains("WantAssertionsSigned=\"true\"").contains("<md:KeyDescriptor use=\"signing\">")
.contains("<md:KeyDescriptor use=\"encryption\">")
.contains("<ds:X509Certificate>MIICgTCCAeoCCQCuVzyqFgMSyDANBgkqhkiG9w0BAQsFADCBhDELMAkGA1UEBh")
.contains("Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect\"")
@@ -58,9 +53,8 @@ public class OpenSamlMetadataResolverTests {
public void resolveWhenRelyingPartyNoCredentialsThenMetadataMatches() {
// given
RelyingPartyRegistration relyingPartyRegistration = noCredentials()
.assertingPartyDetails(party -> party
.verificationX509Credentials(c -> c.add(relyingPartyVerifyingCredential()))
)
.assertingPartyDetails(
party -> party.verificationX509Credentials(c -> c.add(relyingPartyVerifyingCredential())))
.build();
OpenSamlMetadataResolver openSamlMetadataResolver = new OpenSamlMetadataResolver();
@@ -68,13 +62,11 @@ public class OpenSamlMetadataResolverTests {
String metadata = openSamlMetadataResolver.resolve(relyingPartyRegistration);
// then
assertThat(metadata)
.contains("<EntityDescriptor")
.contains("entityID=\"rp-entity-id\"")
.contains("WantAssertionsSigned=\"true\"")
.doesNotContain("<md:KeyDescriptor use=\"signing\">")
assertThat(metadata).contains("<EntityDescriptor").contains("entityID=\"rp-entity-id\"")
.contains("WantAssertionsSigned=\"true\"").doesNotContain("<md:KeyDescriptor use=\"signing\">")
.doesNotContain("<md:KeyDescriptor use=\"encryption\">")
.contains("Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\"")
.contains("Location=\"https://rp.example.org/acs\" index=\"1\"");
}
}
@@ -33,30 +33,24 @@ import static org.assertj.core.api.Assertions.assertThatCode;
import static org.springframework.http.HttpStatus.OK;
public class OpenSamlRelyingPartyRegistrationBuilderHttpMessageConverterTests {
private static final String CERTIFICATE =
"MIIEEzCCAvugAwIBAgIJAIc1qzLrv+5nMA0GCSqGSIb3DQEBCwUAMIGfMQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ08xFDASBgNVBAcMC0Nhc3RsZSBSb2NrMRwwGgYDVQQKDBNTYW1sIFRlc3RpbmcgU2VydmVyMQswCQYDVQQLDAJJVDEgMB4GA1UEAwwXc2ltcGxlc2FtbHBocC5jZmFwcHMuaW8xIDAeBgkqhkiG9w0BCQEWEWZoYW5pa0BwaXZvdGFsLmlvMB4XDTE1MDIyMzIyNDUwM1oXDTI1MDIyMjIyNDUwM1owgZ8xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDTzEUMBIGA1UEBwwLQ2FzdGxlIFJvY2sxHDAaBgNVBAoME1NhbWwgVGVzdGluZyBTZXJ2ZXIxCzAJBgNVBAsMAklUMSAwHgYDVQQDDBdzaW1wbGVzYW1scGhwLmNmYXBwcy5pbzEgMB4GCSqGSIb3DQEJARYRZmhhbmlrQHBpdm90YWwuaW8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC4cn62E1xLqpN34PmbrKBbkOXFjzWgJ9b+pXuaRft6A339uuIQeoeH5qeSKRVTl32L0gdz2ZivLwZXW+cqvftVW1tvEHvzJFyxeTW3fCUeCQsebLnA2qRa07RkxTo6Nf244mWWRDodcoHEfDUSbxfTZ6IExSojSIU2RnD6WllYWFdD1GFpBJOmQB8rAc8wJIBdHFdQnX8Ttl7hZ6rtgqEYMzYVMuJ2F2r1HSU1zSAvwpdYP6rRGFRJEfdA9mm3WKfNLSc5cljz0X/TXy0vVlAV95l9qcfFzPmrkNIst9FZSwpvB49LyAVke04FQPPwLgVH4gphiJH3jvZ7I+J5lS8VAgMBAAGjUDBOMB0GA1UdDgQWBBTTyP6Cc5HlBJ5+ucVCwGc5ogKNGzAfBgNVHSMEGDAWgBTTyP6Cc5HlBJ5+ucVCwGc5ogKNGzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAvMS4EQeP/ipV4jOG5lO6/tYCb/iJeAduOnRhkJk0DbX329lDLZhTTL/x/w/9muCVcvLrzEp6PN+VWfw5E5FWtZN0yhGtP9R+vZnrV+oc2zGD+no1/ySFOe3EiJCO5dehxKjYEmBRv5sU/LZFKZpozKN/BMEa6CqLuxbzb7ykxVr7EVFXwltPxzE9TmL9OACNNyF5eJHWMRMllarUvkcXlh4pux4ks9e6zV9DQBy2zds9f1I3qxg0eX6JnGrXi/ZiCT+lJgVe3ZFXiejiLAiKB04sXW3ti0LW3lx13Y1YlQ4/tlpgTgfIJxKV6nyPiLoK0nywbMd+vpAirDt2Oc+hk";
private static final String ENTITY_DESCRIPTOR_TEMPLATE =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<md:EntityDescriptor xmlns:md=\"urn:oasis:names:tc:SAML:2.0:metadata\" " +
"entityID=\"entity-id\" " +
"ID=\"_bf133aac099b99b3d81286e1a341f2d34188043a77fe15bf4bf1487dae9b2ea3\">\n%s" +
"</md:EntityDescriptor>";
private static final String IDP_SSO_DESCRIPTOR_TEMPLATE =
"<md:IDPSSODescriptor protocolSupportEnumeration=\"urn:oasis:names:tc:SAML:2.0:protocol\">\n" +
"%s\n" +
"</md:IDPSSODescriptor>";
private static final String KEY_DESCRIPTOR_TEMPLATE =
"<md:KeyDescriptor %s>\n" +
"<ds:KeyInfo xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\">\n" +
"<ds:X509Data>\n" +
"<ds:X509Certificate>" + CERTIFICATE + "</ds:X509Certificate>\n" +
"</ds:X509Data>\n" +
"</ds:KeyInfo>\n" +
"</md:KeyDescriptor>";
private static final String SINGLE_SIGN_ON_SERVICE_TEMPLATE =
"<md:SingleSignOnService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect\" " +
"Location=\"sso-location\"/>";
private static final String CERTIFICATE = "MIIEEzCCAvugAwIBAgIJAIc1qzLrv+5nMA0GCSqGSIb3DQEBCwUAMIGfMQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ08xFDASBgNVBAcMC0Nhc3RsZSBSb2NrMRwwGgYDVQQKDBNTYW1sIFRlc3RpbmcgU2VydmVyMQswCQYDVQQLDAJJVDEgMB4GA1UEAwwXc2ltcGxlc2FtbHBocC5jZmFwcHMuaW8xIDAeBgkqhkiG9w0BCQEWEWZoYW5pa0BwaXZvdGFsLmlvMB4XDTE1MDIyMzIyNDUwM1oXDTI1MDIyMjIyNDUwM1owgZ8xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDTzEUMBIGA1UEBwwLQ2FzdGxlIFJvY2sxHDAaBgNVBAoME1NhbWwgVGVzdGluZyBTZXJ2ZXIxCzAJBgNVBAsMAklUMSAwHgYDVQQDDBdzaW1wbGVzYW1scGhwLmNmYXBwcy5pbzEgMB4GCSqGSIb3DQEJARYRZmhhbmlrQHBpdm90YWwuaW8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC4cn62E1xLqpN34PmbrKBbkOXFjzWgJ9b+pXuaRft6A339uuIQeoeH5qeSKRVTl32L0gdz2ZivLwZXW+cqvftVW1tvEHvzJFyxeTW3fCUeCQsebLnA2qRa07RkxTo6Nf244mWWRDodcoHEfDUSbxfTZ6IExSojSIU2RnD6WllYWFdD1GFpBJOmQB8rAc8wJIBdHFdQnX8Ttl7hZ6rtgqEYMzYVMuJ2F2r1HSU1zSAvwpdYP6rRGFRJEfdA9mm3WKfNLSc5cljz0X/TXy0vVlAV95l9qcfFzPmrkNIst9FZSwpvB49LyAVke04FQPPwLgVH4gphiJH3jvZ7I+J5lS8VAgMBAAGjUDBOMB0GA1UdDgQWBBTTyP6Cc5HlBJ5+ucVCwGc5ogKNGzAfBgNVHSMEGDAWgBTTyP6Cc5HlBJ5+ucVCwGc5ogKNGzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAvMS4EQeP/ipV4jOG5lO6/tYCb/iJeAduOnRhkJk0DbX329lDLZhTTL/x/w/9muCVcvLrzEp6PN+VWfw5E5FWtZN0yhGtP9R+vZnrV+oc2zGD+no1/ySFOe3EiJCO5dehxKjYEmBRv5sU/LZFKZpozKN/BMEa6CqLuxbzb7ykxVr7EVFXwltPxzE9TmL9OACNNyF5eJHWMRMllarUvkcXlh4pux4ks9e6zV9DQBy2zds9f1I3qxg0eX6JnGrXi/ZiCT+lJgVe3ZFXiejiLAiKB04sXW3ti0LW3lx13Y1YlQ4/tlpgTgfIJxKV6nyPiLoK0nywbMd+vpAirDt2Oc+hk";
private static final String ENTITY_DESCRIPTOR_TEMPLATE = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<md:EntityDescriptor xmlns:md=\"urn:oasis:names:tc:SAML:2.0:metadata\" " + "entityID=\"entity-id\" "
+ "ID=\"_bf133aac099b99b3d81286e1a341f2d34188043a77fe15bf4bf1487dae9b2ea3\">\n%s"
+ "</md:EntityDescriptor>";
private static final String IDP_SSO_DESCRIPTOR_TEMPLATE = "<md:IDPSSODescriptor protocolSupportEnumeration=\"urn:oasis:names:tc:SAML:2.0:protocol\">\n"
+ "%s\n" + "</md:IDPSSODescriptor>";
private static final String KEY_DESCRIPTOR_TEMPLATE = "<md:KeyDescriptor %s>\n"
+ "<ds:KeyInfo xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\">\n" + "<ds:X509Data>\n"
+ "<ds:X509Certificate>" + CERTIFICATE + "</ds:X509Certificate>\n" + "</ds:X509Data>\n" + "</ds:KeyInfo>\n"
+ "</md:KeyDescriptor>";
private static final String SINGLE_SIGN_ON_SERVICE_TEMPLATE = "<md:SingleSignOnService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect\" "
+ "Location=\"sso-location\"/>";
private OpenSamlRelyingPartyRegistrationBuilderHttpMessageConverter converter;
@@ -67,8 +61,8 @@ public class OpenSamlRelyingPartyRegistrationBuilderHttpMessageConverterTests {
@Test
public void readWhenMissingIDPSSODescriptorThenException() {
MockClientHttpResponse response = new MockClientHttpResponse
((String.format(ENTITY_DESCRIPTOR_TEMPLATE, "")).getBytes(), OK);
MockClientHttpResponse response = new MockClientHttpResponse(
(String.format(ENTITY_DESCRIPTOR_TEMPLATE, "")).getBytes(), OK);
assertThatCode(() -> this.converter.read(RelyingPartyRegistration.Builder.class, response))
.isInstanceOf(Saml2Exception.class)
.hasMessageContaining("Metadata response is missing the necessary IDPSSODescriptor element");
@@ -76,41 +70,34 @@ public class OpenSamlRelyingPartyRegistrationBuilderHttpMessageConverterTests {
@Test
public void readWhenMissingVerificationKeyThenException() {
String payload = String.format(ENTITY_DESCRIPTOR_TEMPLATE,
String.format(IDP_SSO_DESCRIPTOR_TEMPLATE, ""));
String payload = String.format(ENTITY_DESCRIPTOR_TEMPLATE, String.format(IDP_SSO_DESCRIPTOR_TEMPLATE, ""));
MockClientHttpResponse response = new MockClientHttpResponse(payload.getBytes(), OK);
assertThatCode(() -> this.converter.read(RelyingPartyRegistration.Builder.class, response))
.isInstanceOf(Saml2Exception.class)
.hasMessageContaining("Metadata response is missing verification certificates, necessary for verifying SAML assertions");
.isInstanceOf(Saml2Exception.class).hasMessageContaining(
"Metadata response is missing verification certificates, necessary for verifying SAML assertions");
}
@Test
public void readWhenMissingSingleSignOnServiceThenException() {
String payload = String.format(ENTITY_DESCRIPTOR_TEMPLATE,
String.format(IDP_SSO_DESCRIPTOR_TEMPLATE,
String.format(KEY_DESCRIPTOR_TEMPLATE, "use=\"signing\"")
));
String.format(IDP_SSO_DESCRIPTOR_TEMPLATE, String.format(KEY_DESCRIPTOR_TEMPLATE, "use=\"signing\"")));
MockClientHttpResponse response = new MockClientHttpResponse(payload.getBytes(), OK);
assertThatCode(() -> this.converter.read(RelyingPartyRegistration.Builder.class, response))
.isInstanceOf(Saml2Exception.class)
.hasMessageContaining("Metadata response is missing a SingleSignOnService, necessary for sending AuthnRequests");
.isInstanceOf(Saml2Exception.class).hasMessageContaining(
"Metadata response is missing a SingleSignOnService, necessary for sending AuthnRequests");
}
@Test
public void readWhenDescriptorFullySpecifiedThenConfigures() throws Exception {
String payload = String.format(ENTITY_DESCRIPTOR_TEMPLATE,
String.format(IDP_SSO_DESCRIPTOR_TEMPLATE,
String.format(KEY_DESCRIPTOR_TEMPLATE, "use=\"signing\"") +
String.format(KEY_DESCRIPTOR_TEMPLATE, "use=\"encryption\"") +
String.format(SINGLE_SIGN_ON_SERVICE_TEMPLATE)
));
String.format(KEY_DESCRIPTOR_TEMPLATE, "use=\"signing\"")
+ String.format(KEY_DESCRIPTOR_TEMPLATE, "use=\"encryption\"")
+ String.format(SINGLE_SIGN_ON_SERVICE_TEMPLATE)));
MockClientHttpResponse response = new MockClientHttpResponse(payload.getBytes(), OK);
RelyingPartyRegistration registration =
this.converter.read(RelyingPartyRegistration.Builder.class, response)
.registrationId("one")
.build();
RelyingPartyRegistration.AssertingPartyDetails details =
registration.getAssertingPartyDetails();
RelyingPartyRegistration registration = this.converter.read(RelyingPartyRegistration.Builder.class, response)
.registrationId("one").build();
RelyingPartyRegistration.AssertingPartyDetails details = registration.getAssertingPartyDetails();
assertThat(details.getWantAuthnRequestsSigned()).isFalse();
assertThat(details.getSingleSignOnServiceLocation()).isEqualTo("sso-location");
assertThat(details.getSingleSignOnServiceBinding()).isEqualTo(Saml2MessageBinding.REDIRECT);
@@ -125,18 +112,12 @@ public class OpenSamlRelyingPartyRegistrationBuilderHttpMessageConverterTests {
@Test
public void readWhenKeyDescriptorHasNoUseThenConfiguresBothKeyTypes() throws Exception {
String payload = String.format(ENTITY_DESCRIPTOR_TEMPLATE,
String.format(IDP_SSO_DESCRIPTOR_TEMPLATE,
String.format(KEY_DESCRIPTOR_TEMPLATE, "") +
String.format(SINGLE_SIGN_ON_SERVICE_TEMPLATE)
));
String payload = String.format(ENTITY_DESCRIPTOR_TEMPLATE, String.format(IDP_SSO_DESCRIPTOR_TEMPLATE,
String.format(KEY_DESCRIPTOR_TEMPLATE, "") + String.format(SINGLE_SIGN_ON_SERVICE_TEMPLATE)));
MockClientHttpResponse response = new MockClientHttpResponse(payload.getBytes(), OK);
RelyingPartyRegistration registration =
this.converter.read(RelyingPartyRegistration.Builder.class, response)
.registrationId("one")
.build();
RelyingPartyRegistration.AssertingPartyDetails details =
registration.getAssertingPartyDetails();
RelyingPartyRegistration registration = this.converter.read(RelyingPartyRegistration.Builder.class, response)
.registrationId("one").build();
RelyingPartyRegistration.AssertingPartyDetails details = registration.getAssertingPartyDetails();
assertThat(details.getVerificationX509Credentials().iterator().next().getCertificate())
.isEqualTo(x509Certificate(CERTIFICATE));
assertThat(details.getEncryptionX509Credentials()).hasSize(1);
@@ -147,10 +128,11 @@ public class OpenSamlRelyingPartyRegistrationBuilderHttpMessageConverterTests {
X509Certificate x509Certificate(String data) {
try {
InputStream certificate = new ByteArrayInputStream(Base64.getDecoder().decode(data.getBytes()));
return (X509Certificate) CertificateFactory.getInstance("X.509")
.generateCertificate(certificate);
} catch (Exception e) {
return (X509Certificate) CertificateFactory.getInstance("X.509").generateCertificate(certificate);
}
catch (Exception e) {
throw new IllegalArgumentException(e);
}
}
}
@@ -30,21 +30,16 @@ public class RelyingPartyRegistrationTests {
@Test
public void withRelyingPartyRegistrationWorks() {
RelyingPartyRegistration registration = relyingPartyRegistration()
.providerDetails(p -> p.binding(POST))
RelyingPartyRegistration registration = relyingPartyRegistration().providerDetails(p -> p.binding(POST))
.providerDetails(p -> p.signAuthNRequest(false))
.assertionConsumerServiceBinding(Saml2MessageBinding.REDIRECT)
.build();
.assertionConsumerServiceBinding(Saml2MessageBinding.REDIRECT).build();
RelyingPartyRegistration copy = RelyingPartyRegistration.withRelyingPartyRegistration(registration).build();
compareRegistrations(registration, copy);
}
private void compareRegistrations(RelyingPartyRegistration registration, RelyingPartyRegistration copy) {
assertThat(copy.getRegistrationId())
.isEqualTo(registration.getRegistrationId())
.isEqualTo("simplesamlphp");
assertThat(copy.getProviderDetails().getEntityId())
.isEqualTo(registration.getProviderDetails().getEntityId())
assertThat(copy.getRegistrationId()).isEqualTo(registration.getRegistrationId()).isEqualTo("simplesamlphp");
assertThat(copy.getProviderDetails().getEntityId()).isEqualTo(registration.getProviderDetails().getEntityId())
.isEqualTo(copy.getAssertingPartyDetails().getEntityId())
.isEqualTo(registration.getAssertingPartyDetails().getEntityId())
.isEqualTo("https://simplesaml-for-spring-saml.cfapps.io/saml2/idp/metadata.php");
@@ -53,38 +48,26 @@ public class RelyingPartyRegistrationTests {
.isEqualTo(copy.getAssertionConsumerServiceLocation())
.isEqualTo(registration.getAssertionConsumerServiceLocation())
.isEqualTo("{baseUrl}" + Saml2WebSsoAuthenticationFilter.DEFAULT_FILTER_PROCESSES_URI);
assertThat(copy.getCredentials())
.containsAll(registration.getCredentials())
.containsExactly(
registration.getCredentials().get(0),
registration.getCredentials().get(1)
);
assertThat(copy.getLocalEntityIdTemplate())
.isEqualTo(registration.getLocalEntityIdTemplate())
.isEqualTo(copy.getEntityId())
.isEqualTo(registration.getEntityId())
assertThat(copy.getCredentials()).containsAll(registration.getCredentials())
.containsExactly(registration.getCredentials().get(0), registration.getCredentials().get(1));
assertThat(copy.getLocalEntityIdTemplate()).isEqualTo(registration.getLocalEntityIdTemplate())
.isEqualTo(copy.getEntityId()).isEqualTo(registration.getEntityId())
.isEqualTo("{baseUrl}/saml2/service-provider-metadata/{registrationId}");
assertThat(copy.getProviderDetails().getWebSsoUrl())
.isEqualTo(registration.getProviderDetails().getWebSsoUrl())
assertThat(copy.getProviderDetails().getWebSsoUrl()).isEqualTo(registration.getProviderDetails().getWebSsoUrl())
.isEqualTo(copy.getAssertingPartyDetails().getSingleSignOnServiceLocation())
.isEqualTo(registration.getAssertingPartyDetails().getSingleSignOnServiceLocation())
.isEqualTo("https://simplesaml-for-spring-saml.cfapps.io/saml2/idp/SSOService.php");
assertThat(copy.getProviderDetails().getBinding())
.isEqualTo(registration.getProviderDetails().getBinding())
assertThat(copy.getProviderDetails().getBinding()).isEqualTo(registration.getProviderDetails().getBinding())
.isEqualTo(copy.getAssertingPartyDetails().getSingleSignOnServiceBinding())
.isEqualTo(registration.getAssertingPartyDetails().getSingleSignOnServiceBinding())
.isEqualTo(POST);
.isEqualTo(registration.getAssertingPartyDetails().getSingleSignOnServiceBinding()).isEqualTo(POST);
assertThat(copy.getProviderDetails().isSignAuthNRequest())
.isEqualTo(registration.getProviderDetails().isSignAuthNRequest())
.isEqualTo(copy.getAssertingPartyDetails().getWantAuthnRequestsSigned())
.isEqualTo(registration.getAssertingPartyDetails().getWantAuthnRequestsSigned())
.isFalse();
.isEqualTo(registration.getAssertingPartyDetails().getWantAuthnRequestsSigned()).isFalse();
assertThat(copy.getAssertionConsumerServiceBinding())
.isEqualTo(registration.getAssertionConsumerServiceBinding());
assertThat(copy.getDecryptionX509Credentials())
.isEqualTo(registration.getDecryptionX509Credentials());
assertThat(copy.getSigningX509Credentials())
.isEqualTo(registration.getSigningX509Credentials());
assertThat(copy.getDecryptionX509Credentials()).isEqualTo(registration.getDecryptionX509Credentials());
assertThat(copy.getSigningX509Credentials()).isEqualTo(registration.getSigningX509Credentials());
assertThat(copy.getAssertingPartyDetails().getEncryptionX509Credentials())
.isEqualTo(registration.getAssertingPartyDetails().getEncryptionX509Credentials());
assertThat(copy.getAssertingPartyDetails().getVerificationX509Credentials())
@@ -93,16 +76,13 @@ public class RelyingPartyRegistrationTests {
@Test
public void buildWhenUsingDefaultsThenAssertionConsumerServiceBindingDefaultsToPost() {
RelyingPartyRegistration relyingPartyRegistration = withRegistrationId("id")
.entityId("entity-id")
RelyingPartyRegistration relyingPartyRegistration = withRegistrationId("id").entityId("entity-id")
.assertionConsumerServiceLocation("location")
.assertingPartyDetails(assertingParty -> assertingParty
.entityId("entity-id")
.singleSignOnServiceLocation("location"))
.credentials(c -> c.add(relyingPartyVerifyingCredential()))
.build();
.assertingPartyDetails(
assertingParty -> assertingParty.entityId("entity-id").singleSignOnServiceLocation("location"))
.credentials(c -> c.add(relyingPartyVerifyingCredential())).build();
assertThat(relyingPartyRegistration.getAssertionConsumerServiceBinding())
.isEqualTo(POST);
assertThat(relyingPartyRegistration.getAssertionConsumerServiceBinding()).isEqualTo(POST);
}
}
@@ -29,108 +29,72 @@ import static org.assertj.core.api.AssertionsForClassTypes.assertThatCode;
* Tests for {@link RelyingPartyRegistration}
*/
public class RelyingPartyRegistrationsTests {
private static final String IDP_SSO_DESCRIPTOR_PAYLOAD =
"<md:EntityDescriptor entityID=\"https://idp.example.com/idp/shibboleth\"\n" +
" xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\"\n" +
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" +
" xmlns:shibmd=\"urn:mace:shibboleth:metadata:1.0\"\n" +
" xmlns:md=\"urn:oasis:names:tc:SAML:2.0:metadata\"\n" +
" xmlns:mdui=\"urn:oasis:names:tc:SAML:metadata:ui\">\n" +
" \n" +
" <md:IDPSSODescriptor protocolSupportEnumeration=\"urn:oasis:names:tc:SAML:2.0:protocol\">\n" +
" <md:Extensions>\n" +
" <shibmd:Scope regexp=\"false\">example.com</shibmd:Scope>\n" +
" \n" +
" <mdui:UIInfo>\n" +
" <mdui:DisplayName xml:lang=\"en\">\n" +
" Consortium GARR IdP\n" +
" </mdui:DisplayName>\n" +
" <mdui:DisplayName xml:lang=\"it\">\n" +
" Consortium GARR IdP\n" +
" </mdui:DisplayName>\n" +
" \n" +
" <mdui:Description xml:lang=\"en\">\n" +
" This Identity Provider gives support for the Consortium GARR's user community\n" +
" </mdui:Description>\n" +
" <mdui:Description xml:lang=\"it\">\n" +
" Questo Identity Provider di test fornisce supporto alla comunita' utenti GARR\n" +
" </mdui:Description>\n" +
" </mdui:UIInfo>\n" +
" </md:Extensions>\n" +
" \n" +
" <md:KeyDescriptor>\n" +
" <ds:KeyInfo>\n" +
" <ds:X509Data>\n" +
" <ds:X509Certificate>\n" +
" MIIDZjCCAk6gAwIBAgIVAL9O+PA7SXtlwZZY8MVSE9On1cVWMA0GCSqGSIb3DQEB\n" +
" BQUAMCkxJzAlBgNVBAMTHmlkZW0tcHVwYWdlbnQuZG16LWludC51bmltby5pdDAe\n" +
" Fw0xMzA3MjQwMDQ0MTRaFw0zMzA3MjQwMDQ0MTRaMCkxJzAlBgNVBAMTHmlkZW0t\n" +
" cHVwYWdlbnQuZG16LWludC51bmltby5pdDCCASIwDQYJKoZIhvcNAMIIDQADggEP\n" +
" ADCCAQoCggEBAIAcp/VyzZGXUF99kwj4NvL/Rwv4YvBgLWzpCuoxqHZ/hmBwJtqS\n" +
" v0y9METBPFbgsF3hCISnxbcmNVxf/D0MoeKtw1YPbsUmow/bFe+r72hZ+IVAcejN\n" +
" iDJ7t5oTjsRN1t1SqvVVk6Ryk5AZhpFW+W9pE9N6c7kJ16Rp2/mbtax9OCzxpece\n" +
" byi1eiLfIBmkcRawL/vCc2v6VLI18i6HsNVO3l2yGosKCbuSoGDx2fCdAOk/rgdz\n" +
" cWOvFsIZSKuD+FVbSS/J9GVs7yotsS4PRl4iX9UMnfDnOMfO7bcBgbXtDl4SCU1v\n" +
" dJrRw7IL/pLz34Rv9a8nYitrzrxtLOp3nYUCAwEAAaOBhDCBgTBgBgMIIDEEWTBX\n" +
" gh5pZGVtLXB1cGFnZW50LmRtei1pbnQudW5pbW8uaXSGNWh0dHBzOi8vaWRlbS1w\n" +
" dXBhZ2VudC5kbXotaW50LnVuaW1vLml0L2lkcC9zaGliYm9sZXRoMB0GA1UdDgQW\n" +
" BBT8PANzz+adGnTRe8ldcyxAwe4VnzANBgkqhkiG9w0BAQUFAAOCAQEAOEnO8Clu\n" +
" 9z/Lf/8XOOsTdxJbV29DIF3G8KoQsB3dBsLwPZVEAQIP6ceS32Xaxrl6FMTDDNkL\n" +
" qUvvInUisw0+I5zZwYHybJQCletUWTnz58SC4C9G7FpuXHFZnOGtRcgGD1NOX4UU\n" +
" duus/4nVcGSLhDjszZ70Xtj0gw2Sn46oQPHTJ81QZ3Y9ih+Aj1c9OtUSBwtWZFkU\n" +
" yooAKoR8li68Yb21zN2N65AqV+ndL98M8xUYMKLONuAXStDeoVCipH6PJ09Z5U2p\n" +
" V5p4IQRV6QBsNw9CISJFuHzkVYTH5ZxzN80Ru46vh4y2M0Nu8GQ9I085KoZkrf5e\n" +
" Cq53OZt9ISjHEw==\n" +
" </ds:X509Certificate>\n" +
" </ds:X509Data>\n" +
" </ds:KeyInfo>\n" +
" </md:KeyDescriptor>\n" +
" \n" +
" <md:SingleSignOnService\n" +
" Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\"\n" +
" Location=\"https://idp.example.com/idp/profile/SAML2/POST/SSO\"/>\n" +
" </md:IDPSSODescriptor>\n" +
" \n" +
" <md:Organization>\n" +
" <md:OrganizationName xml:lang=\"en\">\n" +
" Consortium GARR\n" +
" </md:OrganizationName>\n" +
" <md:OrganizationName xml:lang=\"it\">\n" +
" Consortium GARR\n" +
" </md:OrganizationName>\n" +
" \n" +
" <md:OrganizationDisplayName xml:lang=\"en\">\n" +
" Consortium GARR\n" +
" </md:OrganizationDisplayName>\n" +
" <md:OrganizationDisplayName xml:lang=\"it\">\n" +
" Consortium GARR\n" +
" </md:OrganizationDisplayName>\n" +
" \n" +
" <md:OrganizationURL xml:lang=\"it\">\n" +
" https://example.org\n" +
" </md:OrganizationURL>\n" +
" </md:Organization>\n" +
" \n" +
" <md:ContactPerson contactType=\"technical\">\n" +
" <md:EmailAddress>mailto:technical.contact@example.com</md:EmailAddress>\n" +
" </md:ContactPerson>\n" +
" \n" +
"</md:EntityDescriptor>";
private static final String IDP_SSO_DESCRIPTOR_PAYLOAD = "<md:EntityDescriptor entityID=\"https://idp.example.com/idp/shibboleth\"\n"
+ " xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\"\n"
+ " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
+ " xmlns:shibmd=\"urn:mace:shibboleth:metadata:1.0\"\n"
+ " xmlns:md=\"urn:oasis:names:tc:SAML:2.0:metadata\"\n"
+ " xmlns:mdui=\"urn:oasis:names:tc:SAML:metadata:ui\">\n" + " \n"
+ " <md:IDPSSODescriptor protocolSupportEnumeration=\"urn:oasis:names:tc:SAML:2.0:protocol\">\n"
+ " <md:Extensions>\n" + " <shibmd:Scope regexp=\"false\">example.com</shibmd:Scope>\n"
+ " \n" + " <mdui:UIInfo>\n" + " <mdui:DisplayName xml:lang=\"en\">\n"
+ " Consortium GARR IdP\n" + " </mdui:DisplayName>\n"
+ " <mdui:DisplayName xml:lang=\"it\">\n" + " Consortium GARR IdP\n"
+ " </mdui:DisplayName>\n" + " \n" + " <mdui:Description xml:lang=\"en\">\n"
+ " This Identity Provider gives support for the Consortium GARR's user community\n"
+ " </mdui:Description>\n" + " <mdui:Description xml:lang=\"it\">\n"
+ " Questo Identity Provider di test fornisce supporto alla comunita' utenti GARR\n"
+ " </mdui:Description>\n" + " </mdui:UIInfo>\n" + " </md:Extensions>\n" + " \n"
+ " <md:KeyDescriptor>\n" + " <ds:KeyInfo>\n" + " <ds:X509Data>\n"
+ " <ds:X509Certificate>\n"
+ " MIIDZjCCAk6gAwIBAgIVAL9O+PA7SXtlwZZY8MVSE9On1cVWMA0GCSqGSIb3DQEB\n"
+ " BQUAMCkxJzAlBgNVBAMTHmlkZW0tcHVwYWdlbnQuZG16LWludC51bmltby5pdDAe\n"
+ " Fw0xMzA3MjQwMDQ0MTRaFw0zMzA3MjQwMDQ0MTRaMCkxJzAlBgNVBAMTHmlkZW0t\n"
+ " cHVwYWdlbnQuZG16LWludC51bmltby5pdDCCASIwDQYJKoZIhvcNAMIIDQADggEP\n"
+ " ADCCAQoCggEBAIAcp/VyzZGXUF99kwj4NvL/Rwv4YvBgLWzpCuoxqHZ/hmBwJtqS\n"
+ " v0y9METBPFbgsF3hCISnxbcmNVxf/D0MoeKtw1YPbsUmow/bFe+r72hZ+IVAcejN\n"
+ " iDJ7t5oTjsRN1t1SqvVVk6Ryk5AZhpFW+W9pE9N6c7kJ16Rp2/mbtax9OCzxpece\n"
+ " byi1eiLfIBmkcRawL/vCc2v6VLI18i6HsNVO3l2yGosKCbuSoGDx2fCdAOk/rgdz\n"
+ " cWOvFsIZSKuD+FVbSS/J9GVs7yotsS4PRl4iX9UMnfDnOMfO7bcBgbXtDl4SCU1v\n"
+ " dJrRw7IL/pLz34Rv9a8nYitrzrxtLOp3nYUCAwEAAaOBhDCBgTBgBgMIIDEEWTBX\n"
+ " gh5pZGVtLXB1cGFnZW50LmRtei1pbnQudW5pbW8uaXSGNWh0dHBzOi8vaWRlbS1w\n"
+ " dXBhZ2VudC5kbXotaW50LnVuaW1vLml0L2lkcC9zaGliYm9sZXRoMB0GA1UdDgQW\n"
+ " BBT8PANzz+adGnTRe8ldcyxAwe4VnzANBgkqhkiG9w0BAQUFAAOCAQEAOEnO8Clu\n"
+ " 9z/Lf/8XOOsTdxJbV29DIF3G8KoQsB3dBsLwPZVEAQIP6ceS32Xaxrl6FMTDDNkL\n"
+ " qUvvInUisw0+I5zZwYHybJQCletUWTnz58SC4C9G7FpuXHFZnOGtRcgGD1NOX4UU\n"
+ " duus/4nVcGSLhDjszZ70Xtj0gw2Sn46oQPHTJ81QZ3Y9ih+Aj1c9OtUSBwtWZFkU\n"
+ " yooAKoR8li68Yb21zN2N65AqV+ndL98M8xUYMKLONuAXStDeoVCipH6PJ09Z5U2p\n"
+ " V5p4IQRV6QBsNw9CISJFuHzkVYTH5ZxzN80Ru46vh4y2M0Nu8GQ9I085KoZkrf5e\n"
+ " Cq53OZt9ISjHEw==\n" + " </ds:X509Certificate>\n"
+ " </ds:X509Data>\n" + " </ds:KeyInfo>\n" + " </md:KeyDescriptor>\n" + " \n"
+ " <md:SingleSignOnService\n"
+ " Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\"\n"
+ " Location=\"https://idp.example.com/idp/profile/SAML2/POST/SSO\"/>\n"
+ " </md:IDPSSODescriptor>\n" + " \n" + " <md:Organization>\n"
+ " <md:OrganizationName xml:lang=\"en\">\n" + " Consortium GARR\n"
+ " </md:OrganizationName>\n" + " <md:OrganizationName xml:lang=\"it\">\n"
+ " Consortium GARR\n" + " </md:OrganizationName>\n" + " \n"
+ " <md:OrganizationDisplayName xml:lang=\"en\">\n" + " Consortium GARR\n"
+ " </md:OrganizationDisplayName>\n" + " <md:OrganizationDisplayName xml:lang=\"it\">\n"
+ " Consortium GARR\n" + " </md:OrganizationDisplayName>\n" + " \n"
+ " <md:OrganizationURL xml:lang=\"it\">\n" + " https://example.org\n"
+ " </md:OrganizationURL>\n" + " </md:Organization>\n" + " \n"
+ " <md:ContactPerson contactType=\"technical\">\n"
+ " <md:EmailAddress>mailto:technical.contact@example.com</md:EmailAddress>\n"
+ " </md:ContactPerson>\n" + " \n" + "</md:EntityDescriptor>";
@Test
public void fromMetadataLocationWhenResolvableThenPopulatesBuilder() throws Exception {
try (MockWebServer server = new MockWebServer()) {
server.enqueue(new MockResponse().setBody(IDP_SSO_DESCRIPTOR_PAYLOAD).setResponseCode(200));
RelyingPartyRegistration registration = RelyingPartyRegistrations
.fromMetadataLocation(server.url("/").toString())
.entityId("rp")
.build();
.fromMetadataLocation(server.url("/").toString()).entityId("rp").build();
RelyingPartyRegistration.AssertingPartyDetails details = registration.getAssertingPartyDetails();
assertThat(details.getEntityId()).isEqualTo("https://idp.example.com/idp/shibboleth");
assertThat(details.getSingleSignOnServiceLocation())
.isEqualTo("https://idp.example.com/idp/profile/SAML2/POST/SSO");
assertThat(details.getSingleSignOnServiceBinding())
.isEqualTo(Saml2MessageBinding.POST);
assertThat(details.getSingleSignOnServiceBinding()).isEqualTo(Saml2MessageBinding.POST);
assertThat(details.getVerificationX509Credentials()).hasSize(1);
assertThat(details.getEncryptionX509Credentials()).hasSize(1);
}
@@ -156,4 +120,5 @@ public class RelyingPartyRegistrationsTests {
.isInstanceOf(Saml2Exception.class);
}
}
}
@@ -33,40 +33,32 @@ public class TestRelyingPartyRegistrations {
String rpEntityId = "{baseUrl}/saml2/service-provider-metadata/{registrationId}";
Saml2X509Credential signingCredential = relyingPartySigningCredential();
String assertionConsumerServiceLocation = "{baseUrl}" + Saml2WebSsoAuthenticationFilter.DEFAULT_FILTER_PROCESSES_URI;
String assertionConsumerServiceLocation = "{baseUrl}"
+ Saml2WebSsoAuthenticationFilter.DEFAULT_FILTER_PROCESSES_URI;
String apEntityId = "https://simplesaml-for-spring-saml.cfapps.io/saml2/idp/metadata.php";
Saml2X509Credential verificationCertificate = relyingPartyVerifyingCredential();
String singleSignOnServiceLocation = "https://simplesaml-for-spring-saml.cfapps.io/saml2/idp/SSOService.php";
return RelyingPartyRegistration.withRegistrationId(registrationId)
.entityId(rpEntityId)
return RelyingPartyRegistration.withRegistrationId(registrationId).entityId(rpEntityId)
.assertionConsumerServiceLocation(assertionConsumerServiceLocation)
.credentials(c -> c.add(signingCredential))
.providerDetails(c -> c
.entityId(apEntityId)
.webSsoUrl(singleSignOnServiceLocation))
.credentials(c -> c.add(verificationCertificate));
.providerDetails(c -> c.entityId(apEntityId).webSsoUrl(singleSignOnServiceLocation))
.credentials(c -> c.add(verificationCertificate));
}
public static RelyingPartyRegistration.Builder noCredentials() {
return RelyingPartyRegistration.withRegistrationId("registration-id")
.entityId("rp-entity-id")
.assertionConsumerServiceLocation("https://rp.example.org/acs")
.assertingPartyDetails(party -> party
.entityId("ap-entity-id")
.singleSignOnServiceLocation("https://ap.example.org/sso")
);
return RelyingPartyRegistration.withRegistrationId("registration-id").entityId("rp-entity-id")
.assertionConsumerServiceLocation("https://rp.example.org/acs").assertingPartyDetails(party -> party
.entityId("ap-entity-id").singleSignOnServiceLocation("https://ap.example.org/sso"));
}
public static RelyingPartyRegistration.Builder full() {
return noCredentials()
.signingX509Credentials(c -> c.add(TestSaml2X509Credentials.relyingPartySigningCredential()))
.decryptionX509Credentials(c -> c.add(TestSaml2X509Credentials.relyingPartyDecryptingCredential()))
.assertingPartyDetails(party -> party
.verificationX509Credentials(c -> c.add(
TestSaml2X509Credentials.relyingPartyVerifyingCredential())
)
);
.assertingPartyDetails(party -> party.verificationX509Credentials(
c -> c.add(TestSaml2X509Credentials.relyingPartyVerifyingCredential())));
}
}
@@ -36,8 +36,11 @@ import static org.mockito.Mockito.when;
public class Saml2WebSsoAuthenticationFilterTests {
private Saml2WebSsoAuthenticationFilter filter;
private RelyingPartyRegistrationRepository repository = mock(RelyingPartyRegistrationRepository.class);
private MockHttpServletRequest request = new MockHttpServletRequest();
private HttpServletResponse response = new MockHttpServletResponse();
@Rule
@@ -87,9 +90,11 @@ public class Saml2WebSsoAuthenticationFilterTests {
try {
filter.attemptAuthentication(request, response);
failBecauseExceptionWasNotThrown(Saml2AuthenticationException.class);
} catch (Exception e) {
}
catch (Exception e) {
assertThat(e).isInstanceOf(Saml2AuthenticationException.class);
assertThat(e.getMessage()).isEqualTo("No relying party registration found");
}
}
}
@@ -48,14 +48,21 @@ import static org.springframework.security.saml2.provider.service.registration.S
public class Saml2WebSsoAuthenticationRequestFilterTests {
private static final String IDP_SSO_URL = "https://sso-url.example.com/IDP/SSO";
private Saml2WebSsoAuthenticationRequestFilter filter;
private RelyingPartyRegistrationRepository repository = mock(RelyingPartyRegistrationRepository.class);
private Saml2AuthenticationRequestFactory factory = mock(Saml2AuthenticationRequestFactory.class);
private Saml2AuthenticationRequestContextResolver resolver =
mock(Saml2AuthenticationRequestContextResolver.class);
private Saml2AuthenticationRequestContextResolver resolver = mock(Saml2AuthenticationRequestContextResolver.class);
private MockHttpServletRequest request;
private MockHttpServletResponse response;
private MockFilterChain filterChain;
private RelyingPartyRegistration.Builder rpBuilder;
@Before
@@ -67,10 +74,8 @@ public class Saml2WebSsoAuthenticationRequestFilterTests {
filterChain = new MockFilterChain();
rpBuilder = RelyingPartyRegistration
.withRegistrationId("registration-id")
.providerDetails(c -> c.entityId("idp-entity-id"))
.providerDetails(c -> c.webSsoUrl(IDP_SSO_URL))
rpBuilder = RelyingPartyRegistration.withRegistrationId("registration-id")
.providerDetails(c -> c.entityId("idp-entity-id")).providerDetails(c -> c.webSsoUrl(IDP_SSO_URL))
.assertionConsumerServiceUrlTemplate("template")
.credentials(c -> c.add(assertingPartyPrivateCredential()));
}
@@ -79,9 +84,7 @@ public class Saml2WebSsoAuthenticationRequestFilterTests {
public void doFilterWhenNoRelayStateThenRedirectDoesNotContainParameter() throws ServletException, IOException {
when(repository.findByRegistrationId("registration-id")).thenReturn(rpBuilder.build());
filter.doFilterInternal(request, response, filterChain);
assertThat(response.getHeader("Location"))
.doesNotContain("RelayState=")
.startsWith(IDP_SSO_URL);
assertThat(response.getHeader("Location")).doesNotContain("RelayState=").startsWith(IDP_SSO_URL);
}
@Test
@@ -89,9 +92,7 @@ public class Saml2WebSsoAuthenticationRequestFilterTests {
when(repository.findByRegistrationId("registration-id")).thenReturn(rpBuilder.build());
request.setParameter("RelayState", "my-relay-state");
filter.doFilterInternal(request, response, filterChain);
assertThat(response.getHeader("Location"))
.contains("RelayState=my-relay-state")
.startsWith(IDP_SSO_URL);
assertThat(response.getHeader("Location")).contains("RelayState=my-relay-state").startsWith(IDP_SSO_URL);
}
@Test
@@ -101,53 +102,36 @@ public class Saml2WebSsoAuthenticationRequestFilterTests {
final String relayStateEncoded = UriUtils.encode(relayStateValue, StandardCharsets.ISO_8859_1);
request.setParameter("RelayState", relayStateValue);
filter.doFilterInternal(request, response, filterChain);
assertThat(response.getHeader("Location"))
.contains("RelayState="+relayStateEncoded)
.startsWith(IDP_SSO_URL);
assertThat(response.getHeader("Location")).contains("RelayState=" + relayStateEncoded).startsWith(IDP_SSO_URL);
}
@Test
public void doFilterWhenSimpleSignatureSpecifiedThenSignatureParametersAreInTheRedirectURL() throws Exception {
when(repository.findByRegistrationId("registration-id")).thenReturn(
rpBuilder
.build()
);
when(repository.findByRegistrationId("registration-id")).thenReturn(rpBuilder.build());
final String relayStateValue = "https://my-relay-state.example.com?with=param&other=param";
final String relayStateEncoded = UriUtils.encode(relayStateValue, StandardCharsets.ISO_8859_1);
request.setParameter("RelayState", relayStateValue);
filter.doFilterInternal(request, response, filterChain);
assertThat(response.getHeader("Location"))
.contains("RelayState="+relayStateEncoded)
.contains("SigAlg=")
.contains("Signature=")
.startsWith(IDP_SSO_URL);
assertThat(response.getHeader("Location")).contains("RelayState=" + relayStateEncoded).contains("SigAlg=")
.contains("Signature=").startsWith(IDP_SSO_URL);
}
@Test
public void doFilterWhenSignatureIsDisabledThenSignatureParametersAreNotInTheRedirectURL() throws Exception {
when(repository.findByRegistrationId("registration-id")).thenReturn(
rpBuilder
.providerDetails(c -> c.signAuthNRequest(false))
.build()
);
when(repository.findByRegistrationId("registration-id"))
.thenReturn(rpBuilder.providerDetails(c -> c.signAuthNRequest(false)).build());
final String relayStateValue = "https://my-relay-state.example.com?with=param&other=param";
final String relayStateEncoded = UriUtils.encode(relayStateValue, StandardCharsets.ISO_8859_1);
request.setParameter("RelayState", relayStateValue);
filter.doFilterInternal(request, response, filterChain);
assertThat(response.getHeader("Location"))
.contains("RelayState="+relayStateEncoded)
.doesNotContain("SigAlg=")
.doesNotContain("Signature=")
.startsWith(IDP_SSO_URL);
assertThat(response.getHeader("Location")).contains("RelayState=" + relayStateEncoded).doesNotContain("SigAlg=")
.doesNotContain("Signature=").startsWith(IDP_SSO_URL);
}
@Test
public void doFilterWhenPostFormDataIsPresent() throws Exception {
when(repository.findByRegistrationId("registration-id")).thenReturn(
rpBuilder
.providerDetails(c -> c.binding(POST))
.build()
);
when(repository.findByRegistrationId("registration-id"))
.thenReturn(rpBuilder.providerDetails(c -> c.binding(POST)).build());
final String relayStateValue = "https://my-relay-state.example.com?with=param&other=param&javascript{alert('1');}";
final String relayStateEncoded = HtmlUtils.htmlEscape(relayStateValue);
request.setParameter("RelayState", relayStateValue);
@@ -156,28 +140,23 @@ public class Saml2WebSsoAuthenticationRequestFilterTests {
assertThat(response.getContentAsString())
.contains("<form action=\"https://sso-url.example.com/IDP/SSO\" method=\"post\">")
.contains("<input type=\"hidden\" name=\"SAMLRequest\"")
.contains("value=\""+relayStateEncoded+"\"");
.contains("value=\"" + relayStateEncoded + "\"");
}
@Test
public void doFilterWhenSetAuthenticationRequestFactoryThenUses() throws Exception {
RelyingPartyRegistration relyingParty = this.rpBuilder
.providerDetails(c -> c.binding(POST))
.build();
RelyingPartyRegistration relyingParty = this.rpBuilder.providerDetails(c -> c.binding(POST)).build();
Saml2PostAuthenticationRequest authenticationRequest = mock(Saml2PostAuthenticationRequest.class);
when(authenticationRequest.getAuthenticationRequestUri()).thenReturn("uri");
when(authenticationRequest.getRelayState()).thenReturn("relay");
when(authenticationRequest.getSamlRequest()).thenReturn("saml");
when(this.repository.findByRegistrationId("registration-id")).thenReturn(relyingParty);
when(this.factory.createPostAuthenticationRequest(any()))
.thenReturn(authenticationRequest);
when(this.factory.createPostAuthenticationRequest(any())).thenReturn(authenticationRequest);
Saml2WebSsoAuthenticationRequestFilter filter = new Saml2WebSsoAuthenticationRequestFilter
(this.repository);
Saml2WebSsoAuthenticationRequestFilter filter = new Saml2WebSsoAuthenticationRequestFilter(this.repository);
filter.setAuthenticationRequestFactory(this.factory);
filter.doFilterInternal(this.request, this.response, this.filterChain);
assertThat(this.response.getContentAsString())
.contains("<form action=\"uri\" method=\"post\">")
assertThat(this.response.getContentAsString()).contains("<form action=\"uri\" method=\"post\">")
.contains("<input type=\"hidden\" name=\"SAMLRequest\" value=\"saml\"")
.contains("<input type=\"hidden\" name=\"RelayState\" value=\"relay\"");
verify(this.factory).createPostAuthenticationRequest(any());
@@ -185,24 +164,19 @@ public class Saml2WebSsoAuthenticationRequestFilterTests {
@Test
public void doFilterWhenCustomAuthenticationRequestFactoryThenUses() throws Exception {
RelyingPartyRegistration relyingParty = this.rpBuilder
.providerDetails(c -> c.binding(POST))
.build();
RelyingPartyRegistration relyingParty = this.rpBuilder.providerDetails(c -> c.binding(POST)).build();
Saml2PostAuthenticationRequest authenticationRequest = mock(Saml2PostAuthenticationRequest.class);
when(authenticationRequest.getAuthenticationRequestUri()).thenReturn("uri");
when(authenticationRequest.getRelayState()).thenReturn("relay");
when(authenticationRequest.getSamlRequest()).thenReturn("saml");
when(this.resolver.resolve(this.request)).thenReturn(authenticationRequestContext()
.relyingPartyRegistration(relyingParty)
.build());
when(this.factory.createPostAuthenticationRequest(any()))
.thenReturn(authenticationRequest);
when(this.resolver.resolve(this.request))
.thenReturn(authenticationRequestContext().relyingPartyRegistration(relyingParty).build());
when(this.factory.createPostAuthenticationRequest(any())).thenReturn(authenticationRequest);
Saml2WebSsoAuthenticationRequestFilter filter = new Saml2WebSsoAuthenticationRequestFilter
(this.resolver, this.factory);
Saml2WebSsoAuthenticationRequestFilter filter = new Saml2WebSsoAuthenticationRequestFilter(this.resolver,
this.factory);
filter.doFilterInternal(this.request, this.response, this.filterChain);
assertThat(this.response.getContentAsString())
.contains("<form action=\"uri\" method=\"post\">")
assertThat(this.response.getContentAsString()).contains("<form action=\"uri\" method=\"post\">")
.contains("<input type=\"hidden\" name=\"SAMLRequest\" value=\"saml\"")
.contains("<input type=\"hidden\" name=\"RelayState\" value=\"relay\"");
verify(this.factory).createPostAuthenticationRequest(any());
@@ -210,23 +184,19 @@ public class Saml2WebSsoAuthenticationRequestFilterTests {
@Test
public void setRequestMatcherWhenNullThenException() {
Saml2WebSsoAuthenticationRequestFilter filter = new Saml2WebSsoAuthenticationRequestFilter
(this.repository);
assertThatCode(() -> filter.setRedirectMatcher(null))
.isInstanceOf(IllegalArgumentException.class);
Saml2WebSsoAuthenticationRequestFilter filter = new Saml2WebSsoAuthenticationRequestFilter(this.repository);
assertThatCode(() -> filter.setRedirectMatcher(null)).isInstanceOf(IllegalArgumentException.class);
}
@Test
public void setAuthenticationRequestFactoryWhenNullThenException() {
Saml2WebSsoAuthenticationRequestFilter filter = new Saml2WebSsoAuthenticationRequestFilter(this.repository);
assertThatCode(() -> filter.setAuthenticationRequestFactory(null))
.isInstanceOf(IllegalArgumentException.class);
assertThatCode(() -> filter.setAuthenticationRequestFactory(null)).isInstanceOf(IllegalArgumentException.class);
}
@Test
public void doFilterWhenRequestMatcherFailsThenSkipsFilter() throws Exception {
Saml2WebSsoAuthenticationRequestFilter filter = new Saml2WebSsoAuthenticationRequestFilter
(this.repository);
Saml2WebSsoAuthenticationRequestFilter filter = new Saml2WebSsoAuthenticationRequestFilter(this.repository);
filter.setRedirectMatcher(request -> false);
filter.doFilter(this.request, this.response, this.filterChain);
verifyNoInteractions(this.repository);
@@ -234,9 +204,9 @@ public class Saml2WebSsoAuthenticationRequestFilterTests {
@Test
public void doFilterWhenRelyingPartyRegistrationNotFoundThenUnauthorized() throws Exception {
Saml2WebSsoAuthenticationRequestFilter filter = new Saml2WebSsoAuthenticationRequestFilter
(this.repository);
Saml2WebSsoAuthenticationRequestFilter filter = new Saml2WebSsoAuthenticationRequestFilter(this.repository);
filter.doFilter(this.request, this.response, this.filterChain);
assertThat(this.response.getStatus()).isEqualTo(401);
}
}
@@ -31,11 +31,14 @@ import static org.springframework.security.saml2.provider.service.registration.T
* Tests for {@link DefaultRelyingPartyRegistrationResolver}
*/
public class DefaultRelyingPartyRegistrationResolverTests {
private final RelyingPartyRegistration registration = relyingPartyRegistration().build();
private final RelyingPartyRegistrationRepository repository =
new InMemoryRelyingPartyRegistrationRepository(this.registration);
private final DefaultRelyingPartyRegistrationResolver resolver =
new DefaultRelyingPartyRegistrationResolver(this.repository);
private final RelyingPartyRegistrationRepository repository = new InMemoryRelyingPartyRegistrationRepository(
this.registration);
private final DefaultRelyingPartyRegistrationResolver resolver = new DefaultRelyingPartyRegistrationResolver(
this.repository);
@Test
public void resolveWhenRequestContainsRegistrationIdThenResolves() {
@@ -43,8 +46,7 @@ public class DefaultRelyingPartyRegistrationResolverTests {
request.setPathInfo("/some/path/" + this.registration.getRegistrationId());
RelyingPartyRegistration registration = this.resolver.convert(request);
assertThat(registration).isNotNull();
assertThat(registration.getRegistrationId())
.isEqualTo(this.registration.getRegistrationId());
assertThat(registration.getRegistrationId()).isEqualTo(this.registration.getRegistrationId());
assertThat(registration.getEntityId())
.isEqualTo("http://localhost/saml2/service-provider-metadata/" + this.registration.getRegistrationId());
assertThat(registration.getAssertionConsumerServiceLocation())
@@ -71,4 +73,5 @@ public class DefaultRelyingPartyRegistrationResolverTests {
assertThatCode(() -> new DefaultRelyingPartyRegistrationResolver(null))
.isInstanceOf(IllegalArgumentException.class);
}
}
@@ -36,23 +36,27 @@ import static org.springframework.security.saml2.credentials.TestSaml2X509Creden
public class DefaultSaml2AuthenticationRequestContextResolverTests {
private static final String ASSERTING_PARTY_SSO_URL = "https://idp.example.com/sso";
private static final String RELYING_PARTY_SSO_URL = "https://sp.example.com/sso";
private static final String ASSERTING_PARTY_ENTITY_ID = "asserting-party-entity-id";
private static final String RELYING_PARTY_ENTITY_ID = "relying-party-entity-id";
private static final String REGISTRATION_ID = "registration-id";
private MockHttpServletRequest request;
private RelyingPartyRegistration.Builder relyingPartyBuilder;
private Saml2AuthenticationRequestContextResolver authenticationRequestContextResolver
= new DefaultSaml2AuthenticationRequestContextResolver(
new DefaultRelyingPartyRegistrationResolver(id -> relyingPartyBuilder.build()));
private Saml2AuthenticationRequestContextResolver authenticationRequestContextResolver = new DefaultSaml2AuthenticationRequestContextResolver(
new DefaultRelyingPartyRegistrationResolver(id -> relyingPartyBuilder.build()));
@Before
public void setup() {
this.request = new MockHttpServletRequest();
this.request.setPathInfo("/saml2/authenticate/registration-id");
this.relyingPartyBuilder = RelyingPartyRegistration
.withRegistrationId(REGISTRATION_ID)
this.relyingPartyBuilder = RelyingPartyRegistration.withRegistrationId(REGISTRATION_ID)
.localEntityIdTemplate(RELYING_PARTY_ENTITY_ID)
.providerDetails(c -> c.entityId(ASSERTING_PARTY_ENTITY_ID))
.providerDetails(c -> c.webSsoUrl(ASSERTING_PARTY_SSO_URL))
@@ -63,8 +67,7 @@ public class DefaultSaml2AuthenticationRequestContextResolverTests {
@Test
public void resolveWhenRequestAndRelyingPartyNotNullThenCreateSaml2AuthenticationRequestContext() {
this.request.addParameter("RelayState", "relay-state");
Saml2AuthenticationRequestContext context =
this.authenticationRequestContextResolver.resolve(this.request);
Saml2AuthenticationRequestContext context = this.authenticationRequestContextResolver.resolve(this.request);
assertThat(context).isNotNull();
assertThat(context.getAssertionConsumerServiceUrl()).isEqualTo(RELYING_PARTY_SSO_URL);
@@ -77,20 +80,16 @@ public class DefaultSaml2AuthenticationRequestContextResolverTests {
@Test
public void resolveWhenAssertionConsumerServiceUrlTemplateContainsRegistrationIdThenResolves() {
this.relyingPartyBuilder
.assertionConsumerServiceLocation("/saml2/authenticate/{registrationId}");
Saml2AuthenticationRequestContext context =
this.authenticationRequestContextResolver.resolve(this.request);
this.relyingPartyBuilder.assertionConsumerServiceLocation("/saml2/authenticate/{registrationId}");
Saml2AuthenticationRequestContext context = this.authenticationRequestContextResolver.resolve(this.request);
assertThat(context.getAssertionConsumerServiceUrl()).isEqualTo("/saml2/authenticate/registration-id");
}
@Test
public void resolveWhenAssertionConsumerServiceUrlTemplateContainsBaseUrlThenResolves() {
this.relyingPartyBuilder
.assertionConsumerServiceLocation("{baseUrl}/saml2/authenticate/{registrationId}");
Saml2AuthenticationRequestContext context =
this.authenticationRequestContextResolver.resolve(this.request);
this.relyingPartyBuilder.assertionConsumerServiceLocation("{baseUrl}/saml2/authenticate/{registrationId}");
Saml2AuthenticationRequestContext context = this.authenticationRequestContextResolver.resolve(this.request);
assertThat(context.getAssertionConsumerServiceUrl())
.isEqualTo("http://localhost/saml2/authenticate/registration-id");
@@ -98,8 +97,8 @@ public class DefaultSaml2AuthenticationRequestContextResolverTests {
@Test
public void resolveWhenRelyingPartyNullThenException() {
assertThatCode(() ->
this.authenticationRequestContextResolver.resolve(null))
assertThatCode(() -> this.authenticationRequestContextResolver.resolve(null))
.isInstanceOf(IllegalArgumentException.class);
}
}
@@ -43,6 +43,7 @@ import static org.springframework.security.saml2.provider.service.registration.T
@RunWith(MockitoJUnitRunner.class)
public class Saml2AuthenticationTokenConverterTests {
@Mock
Converter<HttpServletRequest, RelyingPartyRegistration> relyingPartyRegistrationResolver;
@@ -50,8 +51,8 @@ public class Saml2AuthenticationTokenConverterTests {
@Test
public void convertWhenSamlResponseThenToken() {
Saml2AuthenticationTokenConverter converter = new Saml2AuthenticationTokenConverter
(this.relyingPartyRegistrationResolver);
Saml2AuthenticationTokenConverter converter = new Saml2AuthenticationTokenConverter(
this.relyingPartyRegistrationResolver);
when(this.relyingPartyRegistrationResolver.convert(any(HttpServletRequest.class)))
.thenReturn(this.relyingPartyRegistration);
MockHttpServletRequest request = new MockHttpServletRequest();
@@ -64,8 +65,8 @@ public class Saml2AuthenticationTokenConverterTests {
@Test
public void convertWhenNoSamlResponseThenNull() {
Saml2AuthenticationTokenConverter converter = new Saml2AuthenticationTokenConverter
(this.relyingPartyRegistrationResolver);
Saml2AuthenticationTokenConverter converter = new Saml2AuthenticationTokenConverter(
this.relyingPartyRegistrationResolver);
when(this.relyingPartyRegistrationResolver.convert(any(HttpServletRequest.class)))
.thenReturn(this.relyingPartyRegistration);
MockHttpServletRequest request = new MockHttpServletRequest();
@@ -74,18 +75,17 @@ public class Saml2AuthenticationTokenConverterTests {
@Test
public void convertWhenNoRelyingPartyRegistrationThenNull() {
Saml2AuthenticationTokenConverter converter = new Saml2AuthenticationTokenConverter
(this.relyingPartyRegistrationResolver);
when(this.relyingPartyRegistrationResolver.convert(any(HttpServletRequest.class)))
.thenReturn(null);
Saml2AuthenticationTokenConverter converter = new Saml2AuthenticationTokenConverter(
this.relyingPartyRegistrationResolver);
when(this.relyingPartyRegistrationResolver.convert(any(HttpServletRequest.class))).thenReturn(null);
MockHttpServletRequest request = new MockHttpServletRequest();
assertThat(converter.convert(request)).isNull();
}
@Test
public void convertWhenGetRequestThenInflates() {
Saml2AuthenticationTokenConverter converter = new Saml2AuthenticationTokenConverter
(this.relyingPartyRegistrationResolver);
Saml2AuthenticationTokenConverter converter = new Saml2AuthenticationTokenConverter(
this.relyingPartyRegistrationResolver);
when(this.relyingPartyRegistrationResolver.convert(any(HttpServletRequest.class)))
.thenReturn(this.relyingPartyRegistration);
MockHttpServletRequest request = new MockHttpServletRequest();
@@ -101,14 +101,13 @@ public class Saml2AuthenticationTokenConverterTests {
@Test
public void constructorWhenResolverIsNullThenIllegalArgument() {
assertThatCode(() -> new Saml2AuthenticationTokenConverter(null))
.isInstanceOf(IllegalArgumentException.class);
assertThatCode(() -> new Saml2AuthenticationTokenConverter(null)).isInstanceOf(IllegalArgumentException.class);
}
@Test
public void convertWhenUsingSamlUtilsBase64ThenXmlIsValid() throws Exception {
Saml2AuthenticationTokenConverter converter = new Saml2AuthenticationTokenConverter
(this.relyingPartyRegistrationResolver);
Saml2AuthenticationTokenConverter converter = new Saml2AuthenticationTokenConverter(
this.relyingPartyRegistrationResolver);
when(this.relyingPartyRegistrationResolver.convert(any(HttpServletRequest.class)))
.thenReturn(this.relyingPartyRegistration);
MockHttpServletRequest request = new MockHttpServletRequest();
@@ -118,8 +117,7 @@ public class Saml2AuthenticationTokenConverterTests {
}
private void validateSsoCircleXml(String xml) {
assertThat(xml)
.contains("InResponseTo=\"ARQ9a73ead-7dcf-45a8-89eb-26f3c9900c36\"")
assertThat(xml).contains("InResponseTo=\"ARQ9a73ead-7dcf-45a8-89eb-26f3c9900c36\"")
.contains(" ID=\"s246d157446618e90e43fb79bdd4d9e9e19cf2c7c4\"")
.contains("<saml:Issuer>https://idp.ssocircle.com</saml:Issuer>");
}
@@ -129,4 +127,5 @@ public class Saml2AuthenticationTokenConverterTests {
String response = StreamUtils.copyToString(resource.getInputStream(), StandardCharsets.UTF_8);
return UriUtils.decode(response, UTF_8);
}
}
@@ -42,18 +42,23 @@ import static org.springframework.security.saml2.provider.service.registration.T
public class Saml2MetadataFilterTests {
RelyingPartyRegistrationRepository repository;
Saml2MetadataResolver resolver;
Saml2MetadataFilter filter;
MockHttpServletRequest request;
MockHttpServletResponse response;
FilterChain chain;
@Before
public void setup() {
this.repository = mock(RelyingPartyRegistrationRepository.class);
this.resolver = mock(Saml2MetadataResolver.class);
this.filter = new Saml2MetadataFilter(
new DefaultRelyingPartyRegistrationResolver(this.repository), this.resolver);
this.filter = new Saml2MetadataFilter(new DefaultRelyingPartyRegistrationResolver(this.repository),
this.resolver);
this.request = new MockHttpServletRequest();
this.response = new MockHttpServletResponse();
this.chain = mock(FilterChain.class);
@@ -103,8 +108,8 @@ public class Saml2MetadataFilterTests {
// given
this.request.setPathInfo("/saml2/service-provider-metadata/validRegistration");
RelyingPartyRegistration validRegistration = noCredentials()
.assertingPartyDetails(party -> party
.verificationX509Credentials(c -> c.add(relyingPartyVerifyingCredential())))
.assertingPartyDetails(
party -> party.verificationX509Credentials(c -> c.add(relyingPartyVerifyingCredential())))
.build();
String generatedMetadata = "<xml>test</xml>";
@@ -138,7 +143,7 @@ public class Saml2MetadataFilterTests {
@Test
public void setRequestMatcherWhenNullThenIllegalArgument() {
assertThatCode(() -> this.filter.setRequestMatcher(null))
.isInstanceOf(IllegalArgumentException.class);
assertThatCode(() -> this.filter.setRequestMatcher(null)).isInstanceOf(IllegalArgumentException.class);
}
}