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 ;
18
18
19
- import java .nio .charset .StandardCharsets ;
19
+ import java .time .Clock ;
20
+ import java .time .Instant ;
20
21
import java .util .ArrayList ;
22
+ import java .util .HashMap ;
21
23
import java .util .List ;
22
24
import java .util .Map ;
23
25
import java .util .UUID ;
24
- import java .util .function .BiConsumer ;
26
+ import java .util .function .Consumer ;
25
27
26
28
import jakarta .servlet .http .HttpServletRequest ;
27
- import net .shibboleth .utilities .java .support .xml .SerializeSupport ;
28
29
import org .opensaml .core .config .ConfigurationService ;
29
30
import org .opensaml .core .xml .config .XMLObjectProviderRegistry ;
30
31
import org .opensaml .core .xml .config .XMLObjectProviderRegistrySupport ;
31
- import org .opensaml .core .xml .io .MarshallingException ;
32
32
import org .opensaml .saml .saml2 .core .AuthnRequest ;
33
33
import org .opensaml .saml .saml2 .core .Issuer ;
34
34
import org .opensaml .saml .saml2 .core .NameID ;
38
38
import org .opensaml .saml .saml2 .core .impl .IssuerBuilder ;
39
39
import org .opensaml .saml .saml2 .core .impl .NameIDBuilder ;
40
40
import org .opensaml .saml .saml2 .core .impl .NameIDPolicyBuilder ;
41
- import org .w3c .dom .Element ;
42
41
43
42
import org .springframework .core .convert .converter .Converter ;
44
- import org .springframework .security .saml2 .Saml2Exception ;
45
43
import org .springframework .security .saml2 .core .OpenSamlInitializationService ;
46
44
import org .springframework .security .saml2 .core .Saml2ParameterNames ;
47
45
import org .springframework .security .saml2 .provider .service .authentication .AbstractSaml2AuthenticationRequest ;
63
61
* For internal use only. Intended for consolidating common behavior related to minting a
64
62
* SAML 2.0 Authn Request.
65
63
*/
66
- class OpenSamlAuthenticationRequestResolver {
64
+ class BaseOpenSamlAuthenticationRequestResolver implements Saml2AuthenticationRequestResolver {
67
65
68
66
static {
69
67
OpenSamlInitializationService .initialize ();
70
68
}
69
+
70
+ private final OpenSamlOperations saml ;
71
+
71
72
private final RelyingPartyRegistrationResolver relyingPartyRegistrationResolver ;
72
73
73
74
private final AuthnRequestBuilder authnRequestBuilder ;
@@ -84,15 +85,22 @@ class OpenSamlAuthenticationRequestResolver {
84
85
new AntPathRequestMatcher (Saml2AuthenticationRequestResolver .DEFAULT_AUTHENTICATION_REQUEST_URI ),
85
86
new AntPathQueryRequestMatcher ("/saml2/authenticate" , "registrationId={registrationId}" ));
86
87
88
+ private Clock clock = Clock .systemUTC ();
89
+
87
90
private Converter <HttpServletRequest , String > relayStateResolver = (request ) -> UUID .randomUUID ().toString ();
88
91
92
+ private Consumer <AuthnRequestParameters > parametersConsumer = (parameters ) -> {
93
+ };
94
+
89
95
/**
90
- * Construct a {@link OpenSamlAuthenticationRequestResolver } using the provided
96
+ * Construct a {@link BaseOpenSamlAuthenticationRequestResolver } using the provided
91
97
* parameters
92
98
* @param relyingPartyRegistrationResolver a strategy for resolving the
93
99
* {@link RelyingPartyRegistration} from the {@link HttpServletRequest}
94
100
*/
95
- OpenSamlAuthenticationRequestResolver (RelyingPartyRegistrationResolver relyingPartyRegistrationResolver ) {
101
+ BaseOpenSamlAuthenticationRequestResolver (RelyingPartyRegistrationResolver relyingPartyRegistrationResolver ,
102
+ OpenSamlOperations saml ) {
103
+ this .saml = saml ;
96
104
Assert .notNull (relyingPartyRegistrationResolver , "relyingPartyRegistrationResolver cannot be null" );
97
105
this .relyingPartyRegistrationResolver = relyingPartyRegistrationResolver ;
98
106
XMLObjectProviderRegistry registry = ConfigurationService .get (XMLObjectProviderRegistry .class );
@@ -111,6 +119,10 @@ class OpenSamlAuthenticationRequestResolver {
111
119
Assert .notNull (this .nameIdPolicyBuilder , "nameIdPolicyBuilder must be configured in OpenSAML" );
112
120
}
113
121
122
+ void setClock (Clock clock ) {
123
+ this .clock = clock ;
124
+ }
125
+
114
126
void setRelayStateResolver (Converter <HttpServletRequest , String > relayStateResolver ) {
115
127
this .relayStateResolver = relayStateResolver ;
116
128
}
@@ -119,13 +131,12 @@ void setRequestMatcher(RequestMatcher requestMatcher) {
119
131
this .requestMatcher = requestMatcher ;
120
132
}
121
133
122
- <T extends AbstractSaml2AuthenticationRequest > T resolve (HttpServletRequest request ) {
123
- return resolve (request , (registration , logoutRequest ) -> {
124
- });
134
+ void setParametersConsumer (Consumer <AuthnRequestParameters > parametersConsumer ) {
135
+ this .parametersConsumer = parametersConsumer ;
125
136
}
126
137
127
- < T extends AbstractSaml2AuthenticationRequest > T resolve ( HttpServletRequest request ,
128
- BiConsumer < RelyingPartyRegistration , AuthnRequest > authnRequestConsumer ) {
138
+ @ Override
139
+ public < T extends AbstractSaml2AuthenticationRequest > T resolve ( HttpServletRequest request ) {
129
140
RequestMatcher .MatchResult result = this .requestMatcher .matcher (request );
130
141
if (!result .isMatch ()) {
131
142
return null ;
@@ -153,7 +164,8 @@ <T extends AbstractSaml2AuthenticationRequest> T resolve(HttpServletRequest requ
153
164
nameIdPolicy .setFormat (registration .getNameIdFormat ());
154
165
authnRequest .setNameIDPolicy (nameIdPolicy );
155
166
}
156
- authnRequestConsumer .accept (registration , authnRequest );
167
+ authnRequest .setIssueInstant (Instant .now (this .clock ));
168
+ this .parametersConsumer .accept (new AuthnRequestParameters (request , registration , authnRequest ));
157
169
if (authnRequest .getID () == null ) {
158
170
authnRequest .setID ("ARQ" + UUID .randomUUID ().toString ().substring (1 ));
159
171
}
@@ -162,10 +174,12 @@ <T extends AbstractSaml2AuthenticationRequest> T resolve(HttpServletRequest requ
162
174
if (binding == Saml2MessageBinding .POST ) {
163
175
if (registration .getAssertingPartyMetadata ().getWantAuthnRequestsSigned ()
164
176
|| registration .isAuthnRequestsSigned ()) {
165
- OpenSamlSigningUtils .sign (authnRequest , registration );
177
+ this .saml .withSigningKeys (registration .getSigningX509Credentials ())
178
+ .algorithms (registration .getAssertingPartyMetadata ().getSigningAlgorithms ())
179
+ .sign (authnRequest );
166
180
}
167
181
String xml = serialize (authnRequest );
168
- String encoded = Saml2Utils .samlEncode (xml . getBytes ( StandardCharsets . UTF_8 ) );
182
+ String encoded = Saml2Utils .withDecoded (xml ). encode ( );
169
183
return (T ) Saml2PostAuthenticationRequest .withRelyingPartyRegistration (registration )
170
184
.samlRequest (encoded )
171
185
.relayState (relayState )
@@ -174,35 +188,31 @@ <T extends AbstractSaml2AuthenticationRequest> T resolve(HttpServletRequest requ
174
188
}
175
189
else {
176
190
String xml = serialize (authnRequest );
177
- String deflatedAndEncoded = Saml2Utils .samlEncode ( Saml2Utils . samlDeflate ( xml ) );
191
+ String deflatedAndEncoded = Saml2Utils .withDecoded ( xml ). deflate ( true ). encode ( );
178
192
Saml2RedirectAuthenticationRequest .Builder builder = Saml2RedirectAuthenticationRequest
179
193
.withRelyingPartyRegistration (registration )
180
194
.samlRequest (deflatedAndEncoded )
181
195
.relayState (relayState )
182
196
.id (authnRequest .getID ());
183
197
if (registration .getAssertingPartyMetadata ().getWantAuthnRequestsSigned ()
184
198
|| registration .isAuthnRequestsSigned ()) {
185
- OpenSamlSigningUtils . QueryParametersPartial parametersPartial = OpenSamlSigningUtils . sign ( registration )
186
- . param (Saml2ParameterNames .SAML_REQUEST , deflatedAndEncoded );
199
+ Map < String , String > signingParameters = new HashMap <>();
200
+ signingParameters . put (Saml2ParameterNames .SAML_REQUEST , deflatedAndEncoded );
187
201
if (relayState != null ) {
188
- parametersPartial = parametersPartial . param (Saml2ParameterNames .RELAY_STATE , relayState );
202
+ signingParameters . put (Saml2ParameterNames .RELAY_STATE , relayState );
189
203
}
190
- Map <String , String > parameters = parametersPartial .parameters ();
191
- builder .sigAlg (parameters .get (Saml2ParameterNames .SIG_ALG ))
192
- .signature (parameters .get (Saml2ParameterNames .SIGNATURE ));
204
+ Map <String , String > query = this .saml .withSigningKeys (registration .getSigningX509Credentials ())
205
+ .algorithms (registration .getAssertingPartyMetadata ().getSigningAlgorithms ())
206
+ .sign (signingParameters );
207
+ builder .sigAlg (query .get (Saml2ParameterNames .SIG_ALG ))
208
+ .signature (query .get (Saml2ParameterNames .SIGNATURE ));
193
209
}
194
210
return (T ) builder .build ();
195
211
}
196
212
}
197
213
198
214
private String serialize (AuthnRequest authnRequest ) {
199
- try {
200
- Element element = this .marshaller .marshall (authnRequest );
201
- return SerializeSupport .nodeToString (element );
202
- }
203
- catch (MarshallingException ex ) {
204
- throw new Saml2Exception (ex );
205
- }
215
+ return this .saml .serialize (authnRequest ).serialize ();
206
216
}
207
217
208
218
private static final class AntPathQueryRequestMatcher implements RequestMatcher {
@@ -236,4 +246,33 @@ public MatchResult matcher(HttpServletRequest request) {
236
246
237
247
}
238
248
249
+ static final class AuthnRequestParameters {
250
+
251
+ private final HttpServletRequest request ;
252
+
253
+ private final RelyingPartyRegistration registration ;
254
+
255
+ private final AuthnRequest authnRequest ;
256
+
257
+ AuthnRequestParameters (HttpServletRequest request , RelyingPartyRegistration registration ,
258
+ AuthnRequest authnRequest ) {
259
+ this .request = request ;
260
+ this .registration = registration ;
261
+ this .authnRequest = authnRequest ;
262
+ }
263
+
264
+ HttpServletRequest getRequest () {
265
+ return this .request ;
266
+ }
267
+
268
+ RelyingPartyRegistration getRelyingPartyRegistration () {
269
+ return this .registration ;
270
+ }
271
+
272
+ AuthnRequest getAuthnRequest () {
273
+ return this .authnRequest ;
274
+ }
275
+
276
+ }
277
+
239
278
}
0 commit comments