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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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