|
16 | 16 |
|
17 | 17 | package org.springframework.security.saml2.provider.service.authentication.logout; |
18 | 18 |
|
| 19 | +import java.util.ArrayList; |
19 | 20 | import java.util.Collection; |
20 | | -import java.util.function.Consumer; |
| 21 | +import java.util.Collections; |
21 | 22 |
|
22 | 23 | import org.opensaml.saml.saml2.core.LogoutResponse; |
23 | 24 | import org.opensaml.saml.saml2.core.StatusCode; |
@@ -52,101 +53,90 @@ public Saml2LogoutValidatorResult validate(Saml2LogoutResponseValidatorParameter |
52 | 53 | LogoutResponse logoutResponse = this.saml.deserialize(Saml2Utils.withEncoded(response.getSamlResponse()) |
53 | 54 | .inflate(response.getBinding() == Saml2MessageBinding.REDIRECT) |
54 | 55 | .decode()); |
55 | | - return Saml2LogoutValidatorResult.withErrors() |
56 | | - .errors(verifySignature(response, logoutResponse, registration)) |
57 | | - .errors(validateRequest(logoutResponse, registration)) |
58 | | - .errors(validateLogoutRequest(logoutResponse, request.getId())) |
59 | | - .build(); |
| 56 | + Collection<Saml2Error> errors = verifySignature(response, logoutResponse, registration); |
| 57 | + if (!errors.isEmpty()) { |
| 58 | + return Saml2LogoutValidatorResult.withErrors(errors.toArray(Saml2Error[]::new)).build(); |
| 59 | + } |
| 60 | + errors = validateRequest(logoutResponse, registration, request.getId()); |
| 61 | + return errors.isEmpty() ? Saml2LogoutValidatorResult.success() |
| 62 | + : Saml2LogoutValidatorResult.withErrors(errors.toArray(Saml2Error[]::new)).build(); |
60 | 63 | } |
61 | 64 |
|
62 | | - private Consumer<Collection<Saml2Error>> verifySignature(Saml2LogoutResponse response, |
63 | | - LogoutResponse logoutResponse, RelyingPartyRegistration registration) { |
64 | | - return (errors) -> { |
65 | | - AssertingPartyMetadata details = registration.getAssertingPartyMetadata(); |
66 | | - Collection<Saml2X509Credential> credentials = details.getVerificationX509Credentials(); |
67 | | - VerificationConfigurer verify = this.saml.withVerificationKeys(credentials) |
68 | | - .entityId(details.getEntityId()) |
69 | | - .entityId(details.getEntityId()); |
70 | | - if (logoutResponse.isSigned()) { |
71 | | - errors.addAll(verify.verify(logoutResponse)); |
72 | | - } |
73 | | - else { |
74 | | - RedirectParameters params = new RedirectParameters(response.getParameters(), |
75 | | - response.getParametersQuery(), logoutResponse); |
76 | | - errors.addAll(verify.verify(params)); |
77 | | - } |
78 | | - }; |
| 65 | + private Collection<Saml2Error> verifySignature(Saml2LogoutResponse response, LogoutResponse logoutResponse, |
| 66 | + RelyingPartyRegistration registration) { |
| 67 | + AssertingPartyMetadata details = registration.getAssertingPartyMetadata(); |
| 68 | + Collection<Saml2X509Credential> credentials = details.getVerificationX509Credentials(); |
| 69 | + VerificationConfigurer verify = this.saml.withVerificationKeys(credentials).entityId(details.getEntityId()); |
| 70 | + if (logoutResponse.isSigned()) { |
| 71 | + return verify.verify(logoutResponse); |
| 72 | + } |
| 73 | + RedirectParameters params = new RedirectParameters(response.getParameters(), response.getParametersQuery(), |
| 74 | + logoutResponse); |
| 75 | + return verify.verify(params); |
79 | 76 | } |
80 | 77 |
|
81 | | - private Consumer<Collection<Saml2Error>> validateRequest(LogoutResponse response, |
82 | | - RelyingPartyRegistration registration) { |
83 | | - return (errors) -> { |
84 | | - validateIssuer(response, registration).accept(errors); |
85 | | - validateDestination(response, registration).accept(errors); |
86 | | - validateStatus(response).accept(errors); |
87 | | - }; |
| 78 | + private Collection<Saml2Error> validateRequest(LogoutResponse response, RelyingPartyRegistration registration, |
| 79 | + String logoutRequestId) { |
| 80 | + Collection<Saml2Error> errors = new ArrayList<>(); |
| 81 | + errors.addAll(validateIssuer(response, registration)); |
| 82 | + errors.addAll(validateDestination(response, registration)); |
| 83 | + errors.addAll(validateStatus(response)); |
| 84 | + errors.addAll(validateLogoutRequest(response, logoutRequestId)); |
| 85 | + return errors; |
88 | 86 | } |
89 | 87 |
|
90 | | - private Consumer<Collection<Saml2Error>> validateIssuer(LogoutResponse response, |
91 | | - RelyingPartyRegistration registration) { |
92 | | - return (errors) -> { |
93 | | - if (response.getIssuer() == null) { |
94 | | - errors.add(new Saml2Error(Saml2ErrorCodes.INVALID_ISSUER, "Failed to find issuer in LogoutResponse")); |
95 | | - return; |
96 | | - } |
97 | | - String issuer = response.getIssuer().getValue(); |
98 | | - if (!issuer.equals(registration.getAssertingPartyMetadata().getEntityId())) { |
99 | | - errors |
100 | | - .add(new Saml2Error(Saml2ErrorCodes.INVALID_ISSUER, "Failed to match issuer to configured issuer")); |
101 | | - } |
102 | | - }; |
| 88 | + private Collection<Saml2Error> validateIssuer(LogoutResponse response, RelyingPartyRegistration registration) { |
| 89 | + if (response.getIssuer() == null) { |
| 90 | + return Collections.singletonList( |
| 91 | + new Saml2Error(Saml2ErrorCodes.INVALID_ISSUER, "Failed to find issuer in LogoutResponse")); |
| 92 | + } |
| 93 | + String issuer = response.getIssuer().getValue(); |
| 94 | + if (!issuer.equals(registration.getAssertingPartyMetadata().getEntityId())) { |
| 95 | + return Collections.singletonList( |
| 96 | + new Saml2Error(Saml2ErrorCodes.INVALID_ISSUER, "Failed to match issuer to configured issuer")); |
| 97 | + } |
| 98 | + return Collections.emptyList(); |
103 | 99 | } |
104 | 100 |
|
105 | | - private Consumer<Collection<Saml2Error>> validateDestination(LogoutResponse response, |
106 | | - RelyingPartyRegistration registration) { |
107 | | - return (errors) -> { |
108 | | - if (response.getDestination() == null) { |
109 | | - errors.add(new Saml2Error(Saml2ErrorCodes.INVALID_DESTINATION, |
110 | | - "Failed to find destination in LogoutResponse")); |
111 | | - return; |
112 | | - } |
113 | | - String destination = response.getDestination(); |
114 | | - if (!destination.equals(registration.getSingleLogoutServiceResponseLocation())) { |
115 | | - errors.add(new Saml2Error(Saml2ErrorCodes.INVALID_DESTINATION, |
116 | | - "Failed to match destination to configured destination")); |
117 | | - } |
118 | | - }; |
| 101 | + private Collection<Saml2Error> validateDestination(LogoutResponse response, RelyingPartyRegistration registration) { |
| 102 | + if (response.getDestination() == null) { |
| 103 | + return Collections.singletonList(new Saml2Error(Saml2ErrorCodes.INVALID_DESTINATION, |
| 104 | + "Failed to find destination in LogoutResponse")); |
| 105 | + } |
| 106 | + String destination = response.getDestination(); |
| 107 | + if (!destination.equals(registration.getSingleLogoutServiceResponseLocation())) { |
| 108 | + return Collections.singletonList(new Saml2Error(Saml2ErrorCodes.INVALID_DESTINATION, |
| 109 | + "Failed to match destination to configured destination")); |
| 110 | + } |
| 111 | + return Collections.emptyList(); |
119 | 112 | } |
120 | 113 |
|
121 | | - private Consumer<Collection<Saml2Error>> validateStatus(LogoutResponse response) { |
122 | | - return (errors) -> { |
123 | | - if (response.getStatus() == null) { |
124 | | - return; |
125 | | - } |
126 | | - if (response.getStatus().getStatusCode() == null) { |
127 | | - return; |
128 | | - } |
129 | | - if (StatusCode.SUCCESS.equals(response.getStatus().getStatusCode().getValue())) { |
130 | | - return; |
131 | | - } |
132 | | - if (StatusCode.PARTIAL_LOGOUT.equals(response.getStatus().getStatusCode().getValue())) { |
133 | | - return; |
134 | | - } |
135 | | - errors.add(new Saml2Error(Saml2ErrorCodes.INVALID_RESPONSE, "Response indicated logout failed")); |
136 | | - }; |
| 114 | + private Collection<Saml2Error> validateStatus(LogoutResponse response) { |
| 115 | + if (response.getStatus() == null) { |
| 116 | + return Collections.emptyList(); |
| 117 | + } |
| 118 | + if (response.getStatus().getStatusCode() == null) { |
| 119 | + return Collections.emptyList(); |
| 120 | + } |
| 121 | + if (StatusCode.SUCCESS.equals(response.getStatus().getStatusCode().getValue())) { |
| 122 | + return Collections.emptyList(); |
| 123 | + } |
| 124 | + if (StatusCode.PARTIAL_LOGOUT.equals(response.getStatus().getStatusCode().getValue())) { |
| 125 | + return Collections.emptyList(); |
| 126 | + } |
| 127 | + return Collections |
| 128 | + .singletonList(new Saml2Error(Saml2ErrorCodes.INVALID_RESPONSE, "Response indicated logout failed")); |
137 | 129 | } |
138 | 130 |
|
139 | | - private Consumer<Collection<Saml2Error>> validateLogoutRequest(LogoutResponse response, String id) { |
140 | | - return (errors) -> { |
141 | | - if (response.getInResponseTo() == null) { |
142 | | - return; |
143 | | - } |
144 | | - if (response.getInResponseTo().equals(id)) { |
145 | | - return; |
146 | | - } |
147 | | - errors.add(new Saml2Error(Saml2ErrorCodes.INVALID_RESPONSE, |
148 | | - "LogoutResponse InResponseTo doesn't match ID of associated LogoutRequest")); |
149 | | - }; |
| 131 | + private Collection<Saml2Error> validateLogoutRequest(LogoutResponse response, String id) { |
| 132 | + if (response.getInResponseTo() == null) { |
| 133 | + return Collections.emptyList(); |
| 134 | + } |
| 135 | + if (response.getInResponseTo().equals(id)) { |
| 136 | + return Collections.emptyList(); |
| 137 | + } |
| 138 | + return Collections.singletonList(new Saml2Error(Saml2ErrorCodes.INVALID_RESPONSE, |
| 139 | + "LogoutResponse InResponseTo doesn't match ID of associated LogoutRequest")); |
150 | 140 | } |
151 | 141 |
|
152 | 142 | } |
0 commit comments