Browse Source
Implements the following bindings for AuthNRequest - REDIRECT - POST (future PR) Has been tested with - Keycloak - SSOCircle - Okta - SimpleSAMLPhp Fixes gh-7711pull/7986/head
21 changed files with 1290 additions and 241 deletions
@ -0,0 +1,148 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2002-2020 the original author or authors. |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.springframework.security.saml2.provider.service.authentication; |
||||||
|
|
||||||
|
import org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding; |
||||||
|
import org.springframework.util.Assert; |
||||||
|
|
||||||
|
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)
|
||||||
|
* |
||||||
|
* @see Saml2AuthenticationRequestFactory#createPostAuthenticationRequest(Saml2AuthenticationRequestContext) |
||||||
|
* @see Saml2AuthenticationRequestFactory#createRedirectAuthenticationRequest(Saml2AuthenticationRequestContext) |
||||||
|
* @since 5.3 |
||||||
|
*/ |
||||||
|
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 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 |
||||||
|
*/ |
||||||
|
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; |
||||||
|
this.samlRequest = samlRequest; |
||||||
|
this.relayState = relayState; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 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() { |
||||||
|
return this.samlRequest; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the RelayState value, if present in the parameters |
||||||
|
* @return the RelayState value, or null if not available |
||||||
|
*/ |
||||||
|
public String getRelayState() { |
||||||
|
return this.relayState; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the URI endpoint that this AuthNRequest should be sent to. |
||||||
|
* @return the URI endpoint for this message |
||||||
|
*/ |
||||||
|
public String getAuthenticationRequestUri() { |
||||||
|
return this.authenticationRequestUri; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 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(); |
||||||
|
|
||||||
|
/** |
||||||
|
* A builder for {@link AbstractSaml2AuthenticationRequest} and its subclasses. |
||||||
|
*/ |
||||||
|
static class Builder<T extends Builder<T>> { |
||||||
|
String authenticationRequestUri; |
||||||
|
String samlRequest; |
||||||
|
String relayState; |
||||||
|
|
||||||
|
protected Builder() { |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Casting the return as the generic subtype, when returning itself |
||||||
|
* @return this object |
||||||
|
*/ |
||||||
|
@SuppressWarnings("unchecked") |
||||||
|
protected final T _this() { |
||||||
|
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. |
||||||
|
* @return this object |
||||||
|
*/ |
||||||
|
public T relayState(String relayState) { |
||||||
|
this.relayState = relayState; |
||||||
|
return _this(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the {@code SAMLRequest} parameter that will accompany this AuthNRequest |
||||||
|
* |
||||||
|
* @param samlRequest the SAMLRequest parameter. |
||||||
|
* @return this object |
||||||
|
*/ |
||||||
|
public T samlRequest(String samlRequest) { |
||||||
|
this.samlRequest = samlRequest; |
||||||
|
return _this(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the {@code authenticationRequestUri}, a URL that will receive the AuthNRequest message |
||||||
|
* |
||||||
|
* @param authenticationRequestUri the relay state value, unencoded. |
||||||
|
* @return this object |
||||||
|
*/ |
||||||
|
public T authenticationRequestUri(String authenticationRequestUri) { |
||||||
|
this.authenticationRequestUri = authenticationRequestUri; |
||||||
|
return _this(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,172 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2002-2020 the original author or authors. |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.springframework.security.saml2.provider.service.authentication; |
||||||
|
|
||||||
|
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; |
||||||
|
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"> |
||||||
|
* Assertions and Protocols for SAML 2 (line 2031)</a> |
||||||
|
* |
||||||
|
* @see Saml2AuthenticationRequestFactory#createPostAuthenticationRequest(Saml2AuthenticationRequestContext) |
||||||
|
* @see Saml2AuthenticationRequestFactory#createRedirectAuthenticationRequest(Saml2AuthenticationRequestContext) |
||||||
|
* @since 5.3 |
||||||
|
*/ |
||||||
|
public final class Saml2AuthenticationRequestContext { |
||||||
|
private final RelyingPartyRegistration relyingPartyRegistration; |
||||||
|
private final String issuer; |
||||||
|
private final String assertionConsumerServiceUrl; |
||||||
|
private final String relayState; |
||||||
|
|
||||||
|
private 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"); |
||||||
|
this.issuer = issuer; |
||||||
|
this.relyingPartyRegistration = relyingPartyRegistration; |
||||||
|
this.assertionConsumerServiceUrl = assertionConsumerServiceUrl; |
||||||
|
this.relayState = relayState; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the {@link RelyingPartyRegistration} configuration for which the AuthNRequest is intended for. |
||||||
|
* @return the {@link RelyingPartyRegistration} configuration |
||||||
|
*/ |
||||||
|
public RelyingPartyRegistration getRelyingPartyRegistration() { |
||||||
|
return this.relyingPartyRegistration; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 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. |
||||||
|
* @return the Issuer value |
||||||
|
*/ |
||||||
|
public String getIssuer() { |
||||||
|
return this.issuer; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 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() { |
||||||
|
return assertionConsumerServiceUrl; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the RelayState value, if present in the parameters |
||||||
|
* @return the RelayState value, or null if not available |
||||||
|
*/ |
||||||
|
public String getRelayState() { |
||||||
|
return this.relayState; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 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() { |
||||||
|
return this.getRelyingPartyRegistration().getIdpWebSsoUrl(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* A builder for {@link Saml2AuthenticationRequestContext}. |
||||||
|
* @return a builder object |
||||||
|
*/ |
||||||
|
public static Builder builder() { |
||||||
|
return new Builder(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* A builder for {@link Saml2AuthenticationRequestContext}. |
||||||
|
*/ |
||||||
|
public static class Builder { |
||||||
|
private String issuer; |
||||||
|
private String assertionConsumerServiceUrl; |
||||||
|
private String relayState; |
||||||
|
private RelyingPartyRegistration relyingPartyRegistration; |
||||||
|
|
||||||
|
private Builder() { |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the issuer for the authentication request. |
||||||
|
* @param issuer - a required value |
||||||
|
* @return this {@code Builder} |
||||||
|
*/ |
||||||
|
public Builder issuer(String issuer) { |
||||||
|
this.issuer = issuer; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the {@link RelyingPartyRegistration} used to build the authentication request. |
||||||
|
* @param relyingPartyRegistration - a required value |
||||||
|
* @return this {@code Builder} |
||||||
|
*/ |
||||||
|
public Builder relyingPartyRegistration(RelyingPartyRegistration relyingPartyRegistration) { |
||||||
|
this.relyingPartyRegistration = relyingPartyRegistration; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the {@code assertionConsumerServiceURL} for the authentication request. |
||||||
|
* Typically the {@code Service Provider EntityID} |
||||||
|
* @param assertionConsumerServiceUrl - a required value |
||||||
|
* @return this {@code Builder} |
||||||
|
*/ |
||||||
|
public Builder assertionConsumerServiceUrl(String assertionConsumerServiceUrl) { |
||||||
|
this.assertionConsumerServiceUrl = assertionConsumerServiceUrl; |
||||||
|
return 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. |
||||||
|
* @return this object |
||||||
|
*/ |
||||||
|
public Builder relayState(String relayState) { |
||||||
|
this.relayState = relayState; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a {@link Saml2AuthenticationRequestContext} object. |
||||||
|
* @return the Saml2AuthenticationRequest object |
||||||
|
* @throws {@link IllegalArgumentException} if a required property is not set |
||||||
|
*/ |
||||||
|
public Saml2AuthenticationRequestContext build() { |
||||||
|
return new Saml2AuthenticationRequestContext( |
||||||
|
this.relyingPartyRegistration, |
||||||
|
this.issuer, |
||||||
|
this.assertionConsumerServiceUrl, |
||||||
|
this.relayState |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,85 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2002-2020 the original author or authors. |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.springframework.security.saml2.provider.service.authentication; |
||||||
|
|
||||||
|
import org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding; |
||||||
|
|
||||||
|
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)
|
||||||
|
* |
||||||
|
* @see Saml2AuthenticationRequestFactory |
||||||
|
* @since 5.3 |
||||||
|
*/ |
||||||
|
public class Saml2PostAuthenticationRequest extends AbstractSaml2AuthenticationRequest { |
||||||
|
|
||||||
|
private Saml2PostAuthenticationRequest( |
||||||
|
String samlRequest, |
||||||
|
String relayState, |
||||||
|
String authenticationRequestUri) { |
||||||
|
super(samlRequest, relayState, authenticationRequestUri); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return {@link Saml2MessageBinding#POST} |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public Saml2MessageBinding getBinding() { |
||||||
|
return POST; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 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()) |
||||||
|
; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Builder class for a {@link Saml2PostAuthenticationRequest} object. |
||||||
|
*/ |
||||||
|
public static class Builder extends AbstractSaml2AuthenticationRequest.Builder<Builder> { |
||||||
|
|
||||||
|
private Builder() { |
||||||
|
super(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructs an immutable {@link Saml2PostAuthenticationRequest} object. |
||||||
|
* @return an immutable {@link Saml2PostAuthenticationRequest} object. |
||||||
|
*/ |
||||||
|
public Saml2PostAuthenticationRequest build() { |
||||||
|
return new Saml2PostAuthenticationRequest( |
||||||
|
this.samlRequest, |
||||||
|
this.relayState, |
||||||
|
this.authenticationRequestUri |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,133 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2002-2020 the original author or authors. |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.springframework.security.saml2.provider.service.authentication; |
||||||
|
|
||||||
|
import org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding; |
||||||
|
|
||||||
|
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)
|
||||||
|
* |
||||||
|
* @see Saml2AuthenticationRequestFactory |
||||||
|
* @since 5.3 |
||||||
|
*/ |
||||||
|
public class Saml2RedirectAuthenticationRequest extends AbstractSaml2AuthenticationRequest { |
||||||
|
|
||||||
|
private final String sigAlg; |
||||||
|
private final String signature; |
||||||
|
|
||||||
|
private Saml2RedirectAuthenticationRequest( |
||||||
|
String samlRequest, |
||||||
|
String sigAlg, |
||||||
|
String signature, |
||||||
|
String relayState, |
||||||
|
String authenticationRequestUri) { |
||||||
|
super(samlRequest, relayState, authenticationRequestUri); |
||||||
|
this.sigAlg = sigAlg; |
||||||
|
this.signature = signature; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the SigAlg value for {@link Saml2MessageBinding#REDIRECT} requests |
||||||
|
* @return the SigAlg value |
||||||
|
*/ |
||||||
|
public String getSigAlg() { |
||||||
|
return this.sigAlg; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the Signature value for {@link Saml2MessageBinding#REDIRECT} requests |
||||||
|
* @return the Signature value |
||||||
|
*/ |
||||||
|
public String getSignature() { |
||||||
|
return this.signature; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return {@link Saml2MessageBinding#REDIRECT} |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public Saml2MessageBinding getBinding() { |
||||||
|
return REDIRECT; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 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()) |
||||||
|
; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Builder class for a {@link Saml2RedirectAuthenticationRequest} object. |
||||||
|
*/ |
||||||
|
public static class Builder extends AbstractSaml2AuthenticationRequest.Builder<Builder> { |
||||||
|
private String sigAlg; |
||||||
|
private String signature; |
||||||
|
|
||||||
|
private Builder() { |
||||||
|
super(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the {@code SigAlg} parameter that will accompany this AuthNRequest |
||||||
|
* @param sigAlg the SigAlg parameter value. |
||||||
|
* @return this object |
||||||
|
*/ |
||||||
|
public Builder sigAlg(String sigAlg) { |
||||||
|
this.sigAlg = sigAlg; |
||||||
|
return _this(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the {@code Signature} parameter that will accompany this AuthNRequest |
||||||
|
* @param signature the Signature parameter value. |
||||||
|
* @return this object |
||||||
|
*/ |
||||||
|
public Builder signature(String signature) { |
||||||
|
this.signature = signature; |
||||||
|
return _this(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructs an immutable {@link Saml2RedirectAuthenticationRequest} object. |
||||||
|
* @return an immutable {@link Saml2RedirectAuthenticationRequest} object. |
||||||
|
*/ |
||||||
|
public Saml2RedirectAuthenticationRequest build() { |
||||||
|
return new Saml2RedirectAuthenticationRequest( |
||||||
|
this.samlRequest, |
||||||
|
this.sigAlg, |
||||||
|
this.signature, |
||||||
|
this.relayState, |
||||||
|
this.authenticationRequestUri |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,73 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2002-2020 the original author or authors. |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.springframework.security.saml2.provider.service.authentication; |
||||||
|
|
||||||
|
import org.apache.commons.codec.binary.Base64; |
||||||
|
import org.springframework.security.saml2.Saml2Exception; |
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream; |
||||||
|
import java.io.IOException; |
||||||
|
import java.util.zip.Deflater; |
||||||
|
import java.util.zip.DeflaterOutputStream; |
||||||
|
import java.util.zip.Inflater; |
||||||
|
import java.util.zip.InflaterOutputStream; |
||||||
|
|
||||||
|
import static java.nio.charset.StandardCharsets.UTF_8; |
||||||
|
import static java.util.zip.Deflater.DEFLATED; |
||||||
|
|
||||||
|
/** |
||||||
|
* @since 5.3 |
||||||
|
*/ |
||||||
|
final class Saml2Utils { |
||||||
|
|
||||||
|
|
||||||
|
private static Base64 BASE64 = new Base64(0, new byte[]{'\n'}); |
||||||
|
|
||||||
|
static String samlEncode(byte[] b) { |
||||||
|
return BASE64.encodeAsString(b); |
||||||
|
} |
||||||
|
|
||||||
|
static byte[] samlDecode(String s) { |
||||||
|
return BASE64.decode(s); |
||||||
|
} |
||||||
|
|
||||||
|
static byte[] samlDeflate(String s) { |
||||||
|
try { |
||||||
|
ByteArrayOutputStream b = new ByteArrayOutputStream(); |
||||||
|
DeflaterOutputStream deflater = new DeflaterOutputStream(b, new Deflater(DEFLATED, true)); |
||||||
|
deflater.write(s.getBytes(UTF_8)); |
||||||
|
deflater.finish(); |
||||||
|
return b.toByteArray(); |
||||||
|
} |
||||||
|
catch (IOException e) { |
||||||
|
throw new Saml2Exception("Unable to deflate string", e); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static String samlInflate(byte[] b) { |
||||||
|
try { |
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream(); |
||||||
|
InflaterOutputStream iout = new InflaterOutputStream(out, new Inflater(true)); |
||||||
|
iout.write(b); |
||||||
|
iout.finish(); |
||||||
|
return new String(out.toByteArray(), UTF_8); |
||||||
|
} |
||||||
|
catch (IOException e) { |
||||||
|
throw new Saml2Exception("Unable to inflate string", e); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,45 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2002-2020 the original author or authors. |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package 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. |
||||||
|
* @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"); |
||||||
|
|
||||||
|
private final String urn; |
||||||
|
|
||||||
|
Saml2MessageBinding(String s) { |
||||||
|
this.urn = s; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the URN value from the SAML 2 specification for this binding. |
||||||
|
* @return URN value representing this binding |
||||||
|
*/ |
||||||
|
public String getUrn() { |
||||||
|
return urn; |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,88 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2002-2020 the original author or authors. |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.springframework.security.saml2.provider.service.servlet.filter; |
||||||
|
|
||||||
|
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; |
||||||
|
import org.springframework.util.StringUtils; |
||||||
|
import org.springframework.web.util.UriComponents; |
||||||
|
import org.springframework.web.util.UriComponentsBuilder; |
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest; |
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
import static org.springframework.security.web.util.UrlUtils.buildFullRequestUrl; |
||||||
|
import static org.springframework.web.util.UriComponentsBuilder.fromHttpUrl; |
||||||
|
|
||||||
|
/** |
||||||
|
* @since 5.3 |
||||||
|
*/ |
||||||
|
final class Saml2ServletUtils { |
||||||
|
|
||||||
|
private static final char PATH_DELIMITER = '/'; |
||||||
|
|
||||||
|
static String getServiceProviderEntityId(RelyingPartyRegistration rp, HttpServletRequest request) { |
||||||
|
return resolveUrlTemplate( |
||||||
|
rp.getLocalEntityIdTemplate(), |
||||||
|
getApplicationUri(request), |
||||||
|
rp.getRemoteIdpEntityId(), |
||||||
|
rp.getRegistrationId() |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
static String resolveUrlTemplate(String template, String baseUrl, String entityId, String registrationId) { |
||||||
|
if (!StringUtils.hasText(template)) { |
||||||
|
return baseUrl; |
||||||
|
} |
||||||
|
|
||||||
|
Map<String, String> uriVariables = new HashMap<>(); |
||||||
|
UriComponents uriComponents = UriComponentsBuilder.fromHttpUrl(baseUrl) |
||||||
|
.replaceQuery(null) |
||||||
|
.fragment(null) |
||||||
|
.build(); |
||||||
|
String scheme = uriComponents.getScheme(); |
||||||
|
uriVariables.put("baseScheme", scheme == null ? "" : scheme); |
||||||
|
String host = uriComponents.getHost(); |
||||||
|
uriVariables.put("baseHost", host == null ? "" : host); |
||||||
|
// following logic is based on HierarchicalUriComponents#toUriString()
|
||||||
|
int port = uriComponents.getPort(); |
||||||
|
uriVariables.put("basePort", port == -1 ? "" : ":" + port); |
||||||
|
String path = uriComponents.getPath(); |
||||||
|
if (StringUtils.hasLength(path)) { |
||||||
|
if (path.charAt(0) != PATH_DELIMITER) { |
||||||
|
path = PATH_DELIMITER + path; |
||||||
|
} |
||||||
|
} |
||||||
|
uriVariables.put("basePath", path == null ? "" : path); |
||||||
|
uriVariables.put("baseUrl", uriComponents.toUriString()); |
||||||
|
uriVariables.put("entityId", StringUtils.hasText(entityId) ? entityId : ""); |
||||||
|
uriVariables.put("registrationId", StringUtils.hasText(registrationId) ? registrationId : ""); |
||||||
|
|
||||||
|
return UriComponentsBuilder.fromUriString(template) |
||||||
|
.buildAndExpand(uriVariables) |
||||||
|
.toUriString(); |
||||||
|
} |
||||||
|
|
||||||
|
static String getApplicationUri(HttpServletRequest request) { |
||||||
|
UriComponents uriComponents = fromHttpUrl(buildFullRequestUrl(request)) |
||||||
|
.replacePath(request.getContextPath()) |
||||||
|
.replaceQuery(null) |
||||||
|
.fragment(null) |
||||||
|
.build(); |
||||||
|
return uriComponents.toUriString(); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,72 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2002-2020 the original author or authors. |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.springframework.security.saml2.provider.service.authentication; |
||||||
|
|
||||||
|
import org.junit.Test; |
||||||
|
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; |
||||||
|
|
||||||
|
import java.util.UUID; |
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat; |
||||||
|
import static org.springframework.security.saml2.provider.service.authentication.Saml2Utils.samlDecode; |
||||||
|
import static org.springframework.security.saml2.provider.service.authentication.Saml2Utils.samlInflate; |
||||||
|
import static org.springframework.security.saml2.provider.service.authentication.TestSaml2X509Credentials.relyingPartyCredentials; |
||||||
|
|
||||||
|
/** |
||||||
|
* Tests for {@link Saml2AuthenticationRequestFactory} default interface methods |
||||||
|
*/ |
||||||
|
public class Saml2AuthenticationRequestFactoryTests { |
||||||
|
|
||||||
|
private RelyingPartyRegistration registration = RelyingPartyRegistration.withRegistrationId("id") |
||||||
|
.assertionConsumerServiceUrlTemplate("template") |
||||||
|
.idpWebSsoUrl("https://example.com/destination") |
||||||
|
.remoteIdpEntityId("remote-entity-id") |
||||||
|
.localEntityIdTemplate("local-entity-id") |
||||||
|
.credentials(c -> c.addAll(relyingPartyCredentials())) |
||||||
|
.build(); |
||||||
|
|
||||||
|
@Test |
||||||
|
public void createAuthenticationRequestParametersWhenRedirectDefaultIsUsedMessageIsDeflatedAndEncoded() { |
||||||
|
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(); |
||||||
|
Saml2RedirectAuthenticationRequest response = factory.createRedirectAuthenticationRequest(request); |
||||||
|
String resultValue = response.getSamlRequest(); |
||||||
|
byte[] decoded = samlDecode(resultValue); |
||||||
|
String inflated = samlInflate(decoded); |
||||||
|
assertThat(inflated).isEqualTo(value); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void createAuthenticationRequestParametersWhenPostDefaultIsUsedMessageIsEncoded() { |
||||||
|
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(); |
||||||
|
Saml2PostAuthenticationRequest response = factory.createPostAuthenticationRequest(request); |
||||||
|
String resultValue = response.getSamlRequest(); |
||||||
|
byte[] decoded = samlDecode(resultValue); |
||||||
|
assertThat(new String(decoded)).isEqualTo(value); |
||||||
|
} |
||||||
|
} |
||||||
Loading…
Reference in new issue