Skip to content

Commit ee061f3

Browse files
committed
Use RFC2045 Encoding for SAML 2.0 Logout
Closes gh-10923
1 parent 440ffce commit ee061f3

File tree

5 files changed

+62
-4
lines changed

5 files changed

+62
-4
lines changed

saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/logout/Saml2Utils.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,11 @@ private Saml2Utils() {
4040
}
4141

4242
static String samlEncode(byte[] b) {
43-
return Base64.getEncoder().encodeToString(b);
43+
return Base64.getMimeEncoder().encodeToString(b);
4444
}
4545

4646
static byte[] samlDecode(String s) {
47-
return Base64.getDecoder().decode(s);
47+
return Base64.getMimeDecoder().decode(s);
4848
}
4949

5050
static byte[] samlDeflate(String s) {

saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2Utils.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,11 @@ private Saml2Utils() {
4040
}
4141

4242
static String samlEncode(byte[] b) {
43-
return Base64.getEncoder().encodeToString(b);
43+
return Base64.getMimeEncoder().encodeToString(b);
4444
}
4545

4646
static byte[] samlDecode(String s) {
47-
return Base64.getDecoder().decode(s);
47+
return Base64.getMimeDecoder().decode(s);
4848
}
4949

5050
static byte[] samlDeflate(String s) {

saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/authentication/logout/OpenSamlLogoutRequestValidatorTests.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,23 @@ public void handleWhenMismatchedDestinationThenInvalidDestinationError() {
129129
assertThat(result.getErrors().iterator().next().getErrorCode()).isEqualTo(Saml2ErrorCodes.INVALID_DESTINATION);
130130
}
131131

132+
// gh-10923
133+
@Test
134+
public void handleWhenLogoutResponseHasLineBreaksThenHandles() {
135+
RelyingPartyRegistration registration = registration().build();
136+
LogoutRequest logoutRequest = TestOpenSamlObjects.assertingPartyLogoutRequest(registration);
137+
sign(logoutRequest, registration);
138+
String encoded = new StringBuffer(
139+
Saml2Utils.samlEncode(serialize(logoutRequest).getBytes(StandardCharsets.UTF_8))).insert(10, "\r\n")
140+
.toString();
141+
Saml2LogoutRequest request = Saml2LogoutRequest.withRelyingPartyRegistration(registration).samlRequest(encoded)
142+
.build();
143+
Saml2LogoutRequestValidatorParameters parameters = new Saml2LogoutRequestValidatorParameters(request,
144+
registration, authentication(registration));
145+
Saml2LogoutValidatorResult result = this.manager.validate(parameters);
146+
assertThat(result.hasErrors()).isFalse();
147+
}
148+
132149
private RelyingPartyRegistration.Builder registration() {
133150
return signing(verifying(TestRelyingPartyRegistrations.noCredentials()))
134151
.assertingPartyDetails((party) -> party.singleLogoutServiceBinding(Saml2MessageBinding.POST));

saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/authentication/logout/OpenSamlLogoutResponseValidatorTests.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,24 @@ public void handleWhenStatusNotSuccessThenInvalidResponseError() {
119119
assertThat(result.getErrors().iterator().next().getErrorCode()).isEqualTo(Saml2ErrorCodes.INVALID_RESPONSE);
120120
}
121121

122+
// gh-10923
123+
@Test
124+
public void handleWhenLogoutResponseHasLineBreaksThenHandles() {
125+
RelyingPartyRegistration registration = signing(verifying(registration())).build();
126+
Saml2LogoutRequest logoutRequest = Saml2LogoutRequest.withRelyingPartyRegistration(registration).id("id")
127+
.build();
128+
LogoutResponse logoutResponse = TestOpenSamlObjects.assertingPartyLogoutResponse(registration);
129+
sign(logoutResponse, registration);
130+
String encoded = new StringBuilder(
131+
Saml2Utils.samlEncode(serialize(logoutResponse).getBytes(StandardCharsets.UTF_8))).insert(10, "\r\n")
132+
.toString();
133+
Saml2LogoutResponse response = Saml2LogoutResponse.withRelyingPartyRegistration(registration)
134+
.samlResponse(encoded).build();
135+
Saml2LogoutResponseValidatorParameters parameters = new Saml2LogoutResponseValidatorParameters(response,
136+
logoutRequest, registration);
137+
this.manager.validate(parameters);
138+
}
139+
122140
private RelyingPartyRegistration.Builder registration() {
123141
return signing(verifying(TestRelyingPartyRegistrations.noCredentials()))
124142
.assertingPartyDetails((party) -> party.singleLogoutServiceBinding(Saml2MessageBinding.POST));

saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSamlLogoutResponseResolverTests.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,29 @@ public void resolvePostWhenAuthenticatedThenSuccess() {
9898
assertThat(logoutResponse.getStatus().getStatusCode().getValue()).isEqualTo(StatusCode.SUCCESS);
9999
}
100100

101+
// gh-10923
102+
@Test
103+
public void resolvePostWithLineBreaksWhenAuthenticatedThenSuccess() {
104+
RelyingPartyRegistration registration = TestRelyingPartyRegistrations.full()
105+
.assertingPartyDetails((party) -> party.singleLogoutServiceBinding(Saml2MessageBinding.POST)).build();
106+
MockHttpServletRequest request = new MockHttpServletRequest();
107+
LogoutRequest logoutRequest = TestOpenSamlObjects.assertingPartyLogoutRequest(registration);
108+
String encoded = new StringBuffer(
109+
Saml2Utils.samlEncode(OpenSamlSigningUtils.serialize(logoutRequest).getBytes())).insert(10, "\r\n")
110+
.toString();
111+
request.setParameter(Saml2ParameterNames.SAML_REQUEST, encoded);
112+
request.setParameter(Saml2ParameterNames.RELAY_STATE, "abcd");
113+
Authentication authentication = authentication(registration);
114+
given(this.relyingPartyRegistrationResolver.resolve(any(), any())).willReturn(registration);
115+
Saml2LogoutResponse saml2LogoutResponse = this.logoutResponseResolver.resolve(request, authentication);
116+
assertThat(saml2LogoutResponse.getParameter(Saml2ParameterNames.SIG_ALG)).isNull();
117+
assertThat(saml2LogoutResponse.getParameter(Saml2ParameterNames.SIGNATURE)).isNull();
118+
assertThat(saml2LogoutResponse.getParameter(Saml2ParameterNames.RELAY_STATE)).isSameAs("abcd");
119+
Saml2MessageBinding binding = registration.getAssertingPartyDetails().getSingleLogoutServiceBinding();
120+
LogoutResponse logoutResponse = getLogoutResponse(saml2LogoutResponse.getSamlResponse(), binding);
121+
assertThat(logoutResponse.getStatus().getStatusCode().getValue()).isEqualTo(StatusCode.SUCCESS);
122+
}
123+
101124
private Saml2Authentication authentication(RelyingPartyRegistration registration) {
102125
DefaultSaml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal("user", new HashMap<>());
103126
principal.setRelyingPartyRegistrationId(registration.getRegistrationId());

0 commit comments

Comments
 (0)