1
1
/*
2
- * Copyright 2002-2023 the original author or authors.
2
+ * Copyright 2002-2024 the original author or authors.
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
16
16
17
17
package org .springframework .security .saml2 .provider .service .web .authentication .logout ;
18
18
19
- import java .nio .charset .StandardCharsets ;
19
+ import java .time .Clock ;
20
+ import java .time .Instant ;
21
+ import java .util .HashMap ;
22
+ import java .util .Map ;
20
23
import java .util .UUID ;
21
- import java .util .function .BiConsumer ;
24
+ import java .util .function .Consumer ;
22
25
23
26
import jakarta .servlet .http .HttpServletRequest ;
24
- import net .shibboleth .utilities .java .support .xml .SerializeSupport ;
25
27
import org .apache .commons .logging .Log ;
26
28
import org .apache .commons .logging .LogFactory ;
27
29
import org .opensaml .core .config .ConfigurationService ;
28
30
import org .opensaml .core .xml .config .XMLObjectProviderRegistry ;
29
- import org .opensaml .core .xml .io .MarshallingException ;
30
31
import org .opensaml .saml .saml2 .core .Issuer ;
31
32
import org .opensaml .saml .saml2 .core .LogoutRequest ;
32
33
import org .opensaml .saml .saml2 .core .NameID ;
36
37
import org .opensaml .saml .saml2 .core .impl .LogoutRequestMarshaller ;
37
38
import org .opensaml .saml .saml2 .core .impl .NameIDBuilder ;
38
39
import org .opensaml .saml .saml2 .core .impl .SessionIndexBuilder ;
39
- import org .w3c .dom .Element ;
40
40
41
41
import org .springframework .core .convert .converter .Converter ;
42
42
import org .springframework .security .core .Authentication ;
43
- import org .springframework .security .saml2 .Saml2Exception ;
44
43
import org .springframework .security .saml2 .core .OpenSamlInitializationService ;
45
44
import org .springframework .security .saml2 .core .Saml2ParameterNames ;
46
45
import org .springframework .security .saml2 .provider .service .authentication .Saml2AuthenticatedPrincipal ;
50
49
import org .springframework .security .saml2 .provider .service .web .RelyingPartyRegistrationPlaceholderResolvers ;
51
50
import org .springframework .security .saml2 .provider .service .web .RelyingPartyRegistrationPlaceholderResolvers .UriResolver ;
52
51
import org .springframework .security .saml2 .provider .service .web .RelyingPartyRegistrationResolver ;
53
- import org .springframework .security .saml2 .provider .service .web .authentication .logout .OpenSamlSigningUtils .QueryParametersPartial ;
54
52
import org .springframework .util .Assert ;
55
53
56
54
/**
57
55
* For internal use only. Intended for consolidating common behavior related to minting a
58
56
* SAML 2.0 Logout Request.
59
57
*/
60
- final class OpenSamlLogoutRequestResolver {
58
+ final class BaseOpenSamlLogoutRequestResolver implements Saml2LogoutRequestResolver {
61
59
62
60
static {
63
61
OpenSamlInitializationService .initialize ();
64
62
}
65
63
66
64
private final Log logger = LogFactory .getLog (getClass ());
67
65
66
+ private final OpenSamlOperations saml ;
67
+
68
+ private Clock clock = Clock .systemUTC ();
69
+
68
70
private final LogoutRequestMarshaller marshaller ;
69
71
70
72
private final IssuerBuilder issuerBuilder ;
@@ -79,11 +81,16 @@ final class OpenSamlLogoutRequestResolver {
79
81
80
82
private Converter <HttpServletRequest , String > relayStateResolver = (request ) -> UUID .randomUUID ().toString ();
81
83
84
+ private Consumer <LogoutRequestParameters > parametersConsumer = (parameters ) -> {
85
+ };
86
+
82
87
/**
83
- * Construct a {@link OpenSamlLogoutRequestResolver }
88
+ * Construct a {@link BaseOpenSamlLogoutRequestResolver }
84
89
*/
85
- OpenSamlLogoutRequestResolver (RelyingPartyRegistrationResolver relyingPartyRegistrationResolver ) {
90
+ BaseOpenSamlLogoutRequestResolver (RelyingPartyRegistrationResolver relyingPartyRegistrationResolver ,
91
+ OpenSamlOperations saml ) {
86
92
this .relyingPartyRegistrationResolver = relyingPartyRegistrationResolver ;
93
+ this .saml = saml ;
87
94
XMLObjectProviderRegistry registry = ConfigurationService .get (XMLObjectProviderRegistry .class );
88
95
this .marshaller = (LogoutRequestMarshaller ) registry .getMarshallerFactory ()
89
96
.getMarshaller (LogoutRequest .DEFAULT_ELEMENT_NAME );
@@ -100,10 +107,18 @@ final class OpenSamlLogoutRequestResolver {
100
107
Assert .notNull (this .sessionIndexBuilder , "sessionIndexBuilder must be configured in OpenSAML" );
101
108
}
102
109
110
+ void setClock (Clock clock ) {
111
+ this .clock = clock ;
112
+ }
113
+
103
114
void setRelayStateResolver (Converter <HttpServletRequest , String > relayStateResolver ) {
104
115
this .relayStateResolver = relayStateResolver ;
105
116
}
106
117
118
+ void setParametersConsumer (Consumer <LogoutRequestParameters > parametersConsumer ) {
119
+ this .parametersConsumer = parametersConsumer ;
120
+ }
121
+
107
122
/**
108
123
* Prepare to create, sign, and serialize a SAML 2.0 Logout Request.
109
124
*
@@ -114,13 +129,8 @@ void setRelayStateResolver(Converter<HttpServletRequest, String> relayStateResol
114
129
* @param authentication the current user
115
130
* @return a signed and serialized SAML 2.0 Logout Request
116
131
*/
117
- Saml2LogoutRequest resolve (HttpServletRequest request , Authentication authentication ) {
118
- return resolve (request , authentication , (registration , logoutRequest ) -> {
119
- });
120
- }
121
-
122
- Saml2LogoutRequest resolve (HttpServletRequest request , Authentication authentication ,
123
- BiConsumer <RelyingPartyRegistration , LogoutRequest > logoutRequestConsumer ) {
132
+ @ Override
133
+ public Saml2LogoutRequest resolve (HttpServletRequest request , Authentication authentication ) {
124
134
String registrationId = getRegistrationId (authentication );
125
135
RelyingPartyRegistration registration = this .relyingPartyRegistrationResolver .resolve (request , registrationId );
126
136
if (registration == null ) {
@@ -147,26 +157,33 @@ Saml2LogoutRequest resolve(HttpServletRequest request, Authentication authentica
147
157
logoutRequest .getSessionIndexes ().add (sessionIndex );
148
158
}
149
159
}
150
- logoutRequestConsumer .accept (registration , logoutRequest );
160
+ logoutRequest .setIssueInstant (Instant .now (this .clock ));
161
+ this .parametersConsumer
162
+ .accept (new LogoutRequestParameters (request , registration , authentication , logoutRequest ));
151
163
if (logoutRequest .getID () == null ) {
152
164
logoutRequest .setID ("LR" + UUID .randomUUID ());
153
165
}
154
166
String relayState = this .relayStateResolver .convert (request );
155
167
Saml2LogoutRequest .Builder result = Saml2LogoutRequest .withRelyingPartyRegistration (registration )
156
168
.id (logoutRequest .getID ());
157
169
if (registration .getAssertingPartyMetadata ().getSingleLogoutServiceBinding () == Saml2MessageBinding .POST ) {
158
- String xml = serialize (OpenSamlSigningUtils .sign (logoutRequest , registration ));
159
- String samlRequest = Saml2Utils .samlEncode (xml .getBytes (StandardCharsets .UTF_8 ));
170
+ String xml = serialize (this .saml .withSigningKeys (registration .getSigningX509Credentials ())
171
+ .algorithms (registration .getAssertingPartyMetadata ().getSigningAlgorithms ())
172
+ .sign (logoutRequest ));
173
+ String samlRequest = Saml2Utils .withDecoded (xml ).encode ();
160
174
return result .samlRequest (samlRequest ).relayState (relayState ).build ();
161
175
}
162
176
else {
163
177
String xml = serialize (logoutRequest );
164
- String deflatedAndEncoded = Saml2Utils .samlEncode ( Saml2Utils . samlDeflate ( xml ) );
178
+ String deflatedAndEncoded = Saml2Utils .withDecoded ( xml ). deflate ( true ). encode ( );
165
179
result .samlRequest (deflatedAndEncoded );
166
- QueryParametersPartial partial = OpenSamlSigningUtils .sign (registration )
167
- .param (Saml2ParameterNames .SAML_REQUEST , deflatedAndEncoded )
168
- .param (Saml2ParameterNames .RELAY_STATE , relayState );
169
- return result .parameters ((params ) -> params .putAll (partial .parameters ())).build ();
180
+ Map <String , String > signingParameters = new HashMap <>();
181
+ signingParameters .put (Saml2ParameterNames .SAML_REQUEST , deflatedAndEncoded );
182
+ signingParameters .put (Saml2ParameterNames .RELAY_STATE , relayState );
183
+ Map <String , String > query = this .saml .withSigningKeys (registration .getSigningX509Credentials ())
184
+ .algorithms (registration .getAssertingPartyMetadata ().getSigningAlgorithms ())
185
+ .sign (signingParameters );
186
+ return result .parameters ((params ) -> params .putAll (query )).build ();
170
187
}
171
188
}
172
189
@@ -185,13 +202,43 @@ private String getRegistrationId(Authentication authentication) {
185
202
}
186
203
187
204
private String serialize (LogoutRequest logoutRequest ) {
188
- try {
189
- Element element = this .marshaller .marshall (logoutRequest );
190
- return SerializeSupport .nodeToString (element );
205
+ return this .saml .serialize (logoutRequest ).serialize ();
206
+ }
207
+
208
+ static final class LogoutRequestParameters {
209
+
210
+ private final HttpServletRequest request ;
211
+
212
+ private final RelyingPartyRegistration registration ;
213
+
214
+ private final Authentication authentication ;
215
+
216
+ private final LogoutRequest logoutRequest ;
217
+
218
+ LogoutRequestParameters (HttpServletRequest request , RelyingPartyRegistration registration ,
219
+ Authentication authentication , LogoutRequest logoutRequest ) {
220
+ this .request = request ;
221
+ this .registration = registration ;
222
+ this .authentication = authentication ;
223
+ this .logoutRequest = logoutRequest ;
224
+ }
225
+
226
+ HttpServletRequest getRequest () {
227
+ return this .request ;
191
228
}
192
- catch (MarshallingException ex ) {
193
- throw new Saml2Exception (ex );
229
+
230
+ RelyingPartyRegistration getRelyingPartyRegistration () {
231
+ return this .registration ;
232
+ }
233
+
234
+ Authentication getAuthentication () {
235
+ return this .authentication ;
194
236
}
237
+
238
+ LogoutRequest getLogoutRequest () {
239
+ return this .logoutRequest ;
240
+ }
241
+
195
242
}
196
243
197
244
}
0 commit comments