diff --git a/docs/modules/ROOT/pages/servlet/authorization/authorize-http-requests.adoc b/docs/modules/ROOT/pages/servlet/authorization/authorize-http-requests.adoc index a5e5639854..71835d48f6 100644 --- a/docs/modules/ROOT/pages/servlet/authorization/authorize-http-requests.adoc +++ b/docs/modules/ROOT/pages/servlet/authorization/authorize-http-requests.adoc @@ -376,14 +376,14 @@ Java:: @WithMockUser(authorities="USER") @Test void endpointWhenUserAuthorityThenAuthorized() { - this.mvc.perform(get("/endpoint/jon")) + this.mvc.perform(get("/resource/jon")) .andExpect(status().isOk()); } @WithMockUser @Test void endpointWhenNotUserAuthorityThenForbidden() { - this.mvc.perform(get("/endpoint/jon")) + this.mvc.perform(get("/resource/jon")) .andExpect(status().isForbidden()); } @@ -530,7 +530,7 @@ void postWhenWriteAuthorityThenAuthorized() { @WithMockUser(authorities="read") @Test void postWhenNoWriteAuthorityThenForbidden() { - this.mvc.perform(get("/any").with(csrf())) + this.mvc.perform(post("/any").with(csrf())) .andExpect(status().isForbidden()); } ---- @@ -720,7 +720,7 @@ As a quick summary, here are the authorization rules built into the DSL: * `hasAuthority` - The request requires that the `Authentication` have xref:servlet/authorization/architecture.adoc#authz-authorities[a `GrantedAuthority`] that matches the given value * `hasRole` - A shortcut for `hasAuthority` that prefixes `ROLE_` or whatever is configured as the default prefix * `hasAnyAuthority` - The request requires that the `Authentication` have a `GrantedAuthority` that matches any of the given values -* `hasAnyRole` - A shortcut for `hasAnyAuthority` that prefixes `ROLE_` or whatever is configured as the default prefix* `hasAnyAuthority` - The request requires that the `Authentication` have a `GrantedAuthority` that matches any of the given values +* `hasAnyRole` - A shortcut for `hasAnyAuthority` that prefixes `ROLE_` or whatever is configured as the default prefix * `hasAllRoles` - A shortcut for `hasAllAuthorities` that prefixes `ROLE_` or whatever is configured as the default prefix * `hasAllAuthorities` - The request requires that the `Authentication` have a `GrantedAuthority` that matches all of the given values * `access` - The request uses this custom `AuthorizationManager` to determine access @@ -1233,4 +1233,4 @@ open class SecurityConfig { == Further Reading Now that you have secured your application's requests, consider xref:servlet/authorization/method-security.adoc[securing its methods]. -You can also read further on xref:servlet/test/index.adoc[testing your application] or on integrating Spring Security with other aspects of you application like xref:servlet/integrations/data.adoc[the data layer] or xref:servlet/integrations/observability.adoc[tracing and metrics]. +You can also read further on xref:servlet/test/index.adoc[testing your application] or on integrating Spring Security with other aspects of your application like xref:servlet/integrations/data.adoc[the data layer] or xref:servlet/integrations/observability.adoc[tracing and metrics]. diff --git a/docs/modules/ROOT/pages/servlet/authorization/method-security.adoc b/docs/modules/ROOT/pages/servlet/authorization/method-security.adoc index 28f0009e1b..666a3b88e0 100644 --- a/docs/modules/ROOT/pages/servlet/authorization/method-security.adoc +++ b/docs/modules/ROOT/pages/servlet/authorization/method-security.adoc @@ -758,7 +758,7 @@ fun readAccountsWhenOwnedThenReturns() { `@PostFilter` supports arrays, collections, maps, and streams (so long as the stream is still open). -For example, the above `readAccounts` declaration will function the same way as the following other three: +For example, the above `readAccounts` declaration will function the same way as the following other four: [tabs] ====== @@ -3020,7 +3020,7 @@ void fooWhenDeniedThenReturnStars() { @Test void barWhenDeniedThenReturnQuestionMarks() { - String value = this.myService.foo(); + String value = this.myService.bar(); assertThat(value).isEqualTo("???"); } ---- @@ -3040,7 +3040,7 @@ fun fooWhenDeniedThenReturnStars() { @Test fun barWhenDeniedThenReturnQuestionMarks() { - val value: String = myService.foo() + val value: String = myService.bar() assertThat(value).isEqualTo("???") } ---- @@ -3338,5 +3338,5 @@ class MyExpressionHandler: DefaultMethodSecurityExpressionHandler { == Further Reading -Now that you have secured your application's requests, please xref:servlet/authorization/authorize-http-requests.adoc[secure its requests] if you haven't already. +Now that you have secured your application's methods, please xref:servlet/authorization/authorize-http-requests.adoc[secure its requests] if you haven't already. You can also read further on xref:servlet/test/index.adoc[testing your application] or on integrating Spring Security with other aspects of you application like xref:servlet/integrations/data.adoc[the data layer] or xref:servlet/integrations/observability.adoc[tracing and metrics]. diff --git a/docs/modules/ROOT/pages/servlet/oauth2/resource-server/jwt.adoc b/docs/modules/ROOT/pages/servlet/oauth2/resource-server/jwt.adoc index a4b1d27bff..6bdc26ba80 100644 --- a/docs/modules/ROOT/pages/servlet/oauth2/resource-server/jwt.adoc +++ b/docs/modules/ROOT/pages/servlet/oauth2/resource-server/jwt.adoc @@ -961,7 +961,7 @@ By default, Spring Security will wire the `JwtAuthenticationProvider` with a def As part of configuring a `JwtAuthenticationConverter`, you can supply a subsidiary converter to go from `Jwt` to a `Collection` of granted authorities. -Let's say that that your authorization server communicates authorities in a custom claim called `authorities`. +Let's say that your authorization server communicates authorities in a custom claim called `authorities`. In that case, you can configure the claim that <> should inspect, like so: .Authorities Claim Configuration diff --git a/docs/modules/ROOT/pages/servlet/oauth2/resource-server/opaque-token.adoc b/docs/modules/ROOT/pages/servlet/oauth2/resource-server/opaque-token.adoc index 1e406208a1..80654024cb 100644 --- a/docs/modules/ROOT/pages/servlet/oauth2/resource-server/opaque-token.adoc +++ b/docs/modules/ROOT/pages/servlet/oauth2/resource-server/opaque-token.adoc @@ -755,7 +755,7 @@ public OpaqueTokenIntrospector introspector(RestTemplateBuilder builder, OAuth2R .setReadTimeout(Duration.ofSeconds(60)) .build(); - return SpringOpaqueTokenIntrospector(introspectionUri, rest); + return new SpringOpaqueTokenIntrospector(introspectionUri, rest); } ---- diff --git a/docs/modules/ROOT/pages/servlet/saml2/login/authentication.adoc b/docs/modules/ROOT/pages/servlet/saml2/login/authentication.adoc index 632c87995d..c9df44e9c6 100644 --- a/docs/modules/ROOT/pages/servlet/saml2/login/authentication.adoc +++ b/docs/modules/ROOT/pages/servlet/saml2/login/authentication.adoc @@ -270,12 +270,13 @@ class MyUserDetailsResponseAuthenticationConverter implements Converter - UserDetails principal = this.userDetailsService.loadByUsername(username); <2> + String username = authentication.getName(); + UserDetails user = this.userDetailsService.loadUserByUsername(username); <2> String saml2Response = authentication.getSaml2Response(); Saml2ResponseAssertionAccessor assertion = new OpenSamlResponseAssertionAccessor( - saml2Response, CollectionUtils.getFirst(response.getAssertions())); - Collection authorities = principal.getAuthorities(); - return new Saml2AssertionAuthentication(userDetails, assertion, authorities); <3> + saml2Response, CollectionUtils.getFirst(responseToken.getResponse().getAssertions())); + Collection authorities = user.getAuthorities(); + return new Saml2AssertionAuthentication(user, assertion, authorities); <3> } } @@ -286,18 +287,19 @@ Kotlin:: [source,kotlin,role="secondary"] ---- @Component -open class MyUserDetailsResponseAuthenticationConverter(val delegate: ResponseAuthenticationConverter, - UserDetailsService userDetailsService): Converter { +open class MyUserDetailsResponseAuthenticationConverter(private val userDetailsService: UserDetailsService) : Converter { - @Override - open fun convert(responseToken: ResponseToken): Saml2Authentication { + private val delegate = ResponseAuthenticationConverter() + + override fun convert(responseToken: ResponseToken): Saml2Authentication { val authentication = this.delegate.convert(responseToken) <1> - val principal = this.userDetailsService.loadByUsername(username) <2> - val saml2Response = authentication.getSaml2Response() + val username = authentication.name + val userDetails = this.userDetailsService.loadUserByUsername(username) <2> + val saml2Response = authentication.saml2Response val assertion = OpenSamlResponseAssertionAccessor( - saml2Response, CollectionUtils.getFirst(response.getAssertions())) + saml2Response, responseToken.response.assertions.firstOrNull()) val authorities = principal.getAuthorities() - return Saml2AssertionAuthentication(userDetails, assertion, authorities) <3> + return Saml2AssertionAuthentication(userDetails, assertion, userDetails.authorities) <3> } } diff --git a/docs/modules/ROOT/pages/servlet/saml2/login/overview.adoc b/docs/modules/ROOT/pages/servlet/saml2/login/overview.adoc index f661890753..be605e2f2e 100644 --- a/docs/modules/ROOT/pages/servlet/saml2/login/overview.adoc +++ b/docs/modules/ROOT/pages/servlet/saml2/login/overview.adoc @@ -215,7 +215,7 @@ If any signature is invalid, authentication fails. Also, if neither the response nor the assertions have signatures, authentication fails. Either the response or all the assertions must have signatures. -image:{icondir}/number_7.png[] Then, the provider xref:servlet/saml2/login/authentication.adoc#servlet-saml2login-opensamlauthenticationprovider-decryption[,]decrypts any `EncryptedID` or `EncryptedAttribute` elements]. +image:{icondir}/number_7.png[] Then, the provider xref:servlet/saml2/login/authentication.adoc#servlet-saml2login-opensamlauthenticationprovider-decryption[decrypts any `EncryptedID` or `EncryptedAttribute` elements]. If any decryptions fail, authentication fails. image:{icondir}/number_8.png[] Next, the provider validates each assertion's `ExpiresAt` and `NotBefore` timestamps, the `` and any `` conditions. @@ -1039,4 +1039,4 @@ You can see a completed example of this in {gh-samples-url}/servlet/spring-boot/ In the event that you are migrating from the Spring Security SAML Extension, there may be some benefit to configuring your application to use the SAML Extension URI defaults. -For more information on this, please see {gh-samples-url}/servlet/spring-boot/java/saml2/custom-urls[our `custom-urls` sample] and {gh-samples-url}/servlet/spring-boot/java/saml2/saml-extension-federation[our `saml-extension-federation` sample]. +For more information on this, please see {gh-samples-url}/servlet/spring-boot/java/saml2/saml-extension-urls[our `saml-extension-urls` sample] and {gh-samples-url}/servlet/spring-boot/java/saml2/saml-extension-federation[our `saml-extension-federation` sample]. diff --git a/docs/modules/ROOT/pages/servlet/saml2/logout.adoc b/docs/modules/ROOT/pages/servlet/saml2/logout.adoc index 27de151381..e9827195e6 100644 --- a/docs/modules/ROOT/pages/servlet/saml2/logout.adoc +++ b/docs/modules/ROOT/pages/servlet/saml2/logout.adoc @@ -439,14 +439,14 @@ Java:: ---- @Bean public Saml2LogoutResponseResolver logoutResponseResolver(RelyingPartyRegistrationRepository registrations) { - OpenSaml5LogoutResponseResolver logoutRequestResolver = + OpenSaml5LogoutResponseResolver resolver = new OpenSaml5LogoutResponseResolver(registrations); - logoutRequestResolver.setParametersConsumer((parameters) -> { + resolver.setParametersConsumer((parameters) -> { if (checkOtherPrevailingConditions(parameters.getRequest())) { - parameters.getLogoutRequest().getStatus().getStatusCode().setCode(StatusCode.PARTIAL_LOGOUT); + parameters.getLogoutResponse().getStatus().getStatusCode().setCode(StatusCode.PARTIAL_LOGOUT); } }); - return logoutRequestResolver; + return resolver; } ---- @@ -456,13 +456,13 @@ Kotlin:: ---- @Bean open fun logoutResponseResolver(registrations: RelyingPartyRegistrationRepository?): Saml2LogoutResponseResolver { - val logoutRequestResolver = OpenSaml5LogoutResponseResolver(registrations) - logoutRequestResolver.setParametersConsumer { LogoutResponseParameters parameters -> - if (checkOtherPrevailingConditions(parameters.getRequest())) { - parameters.getLogoutRequest().getStatus().getStatusCode().setCode(StatusCode.PARTIAL_LOGOUT) + val resolver = OpenSaml5LogoutResponseResolver(registrations) + resolver.setParametersConsumer { parameters -> + if (checkOtherPrevailingConditions(parameters.request)) { + parameters.logoutResponse.status.statusCode.code = StatusCode.PARTIAL_LOGOUT } } - return logoutRequestResolver + return resolver } ---- ====== @@ -477,8 +477,8 @@ Java:: ---- http .saml2Logout((saml2) -> saml2 - .logoutRequest((request) -> request - .logoutRequestResolver(this.logoutRequestResolver) + .logoutResponse((request) -> request + .logoutResponseResolver(this.logoutResponseResolver) ) ); ---- @@ -489,8 +489,8 @@ Kotlin:: ---- http { saml2Logout { - logoutRequest { - logoutRequestResolver = this.logoutRequestResolver + logoutResponse { + logoutResponseResolver = logoutResponseResolver } } } @@ -513,12 +513,17 @@ public class MyOpenSamlLogoutRequestValidator implements Saml2LogoutRequestValid private final Saml2LogoutRequestValidator delegate = new OpenSaml5LogoutRequestValidator(); @Override - public Saml2LogoutRequestValidator logout(Saml2LogoutRequestValidatorParameters parameters) { + public Saml2LogoutValidatorResult validate(Saml2LogoutRequestValidatorParameters parameters) { // verify signature, issuer, destination, and principal name - Saml2LogoutValidatorResult result = delegate.authenticate(authentication); + Saml2LogoutValidatorResult result = delegate.validate(authentication); - LogoutRequest logoutRequest = // ... parse using OpenSAML + if(result.hasErrors()){ + return result; + } + // perform custom validation + + return result; } } ---- @@ -528,16 +533,21 @@ Kotlin:: [source,kotlin,role="secondary"] ---- @Component -open class MyOpenSamlLogoutRequestValidator: Saml2LogoutRequestValidator { +open class MyOpenSamlLogoutRequestValidator : Saml2LogoutRequestValidator { private val delegate = OpenSaml5LogoutRequestValidator() @Override - fun logout(parameters: Saml2LogoutRequestValidatorParameters): Saml2LogoutRequestValidator { + fun validate(parameters: Saml2LogoutRequestValidatorParameters): Saml2LogoutValidatorResult { // verify signature, issuer, destination, and principal name - val result = delegate.authenticate(authentication) + val result = delegate.validate(authentication) + + if (result.hasErrors()) { + return result + } - val logoutRequest: LogoutRequest = // ... parse using OpenSAML // perform custom validation + + return result } } ---- @@ -589,12 +599,17 @@ public class MyOpenSamlLogoutResponseValidator implements Saml2LogoutResponseVal private final Saml2LogoutResponseValidator delegate = new OpenSaml5LogoutResponseValidator(); @Override - public Saml2LogoutValidatorResult logout(Saml2LogoutResponseValidatorParameters parameters) { + public Saml2LogoutValidatorResult validate(Saml2LogoutResponseValidatorParameters parameters) { // verify signature, issuer, destination, and status - Saml2LogoutValidatorResult result = delegate.authenticate(parameters); + Saml2LogoutValidatorResult result = delegate.validate(parameters); + + if (result.hasErrors()) { + return result; + } - LogoutResponse logoutResponse = // ... parse using OpenSAML // perform custom validation + + return result; } } ---- @@ -604,16 +619,20 @@ Kotlin:: [source,kotlin,role="secondary"] ---- @Component -open class MyOpenSamlLogoutResponseValidator: Saml2LogoutResponseValidator { +open class MyOpenSamlLogoutResponseValidator : Saml2LogoutResponseValidator { private val delegate = OpenSaml5LogoutResponseValidator() - @Override - fun logout(parameters: Saml2LogoutResponseValidatorParameters): Saml2LogoutResponseValidator { + override fun validate(parameters: Saml2LogoutResponseValidatorParameters): Saml2LogoutValidatorResult { // verify signature, issuer, destination, and status - val result = delegate.authenticate(authentication) + val result = delegate.validate(authentication) - val logoutResponse: LogoutResponse = // ... parse using OpenSAML + if (result.hasErrors()) { + return result + } + // perform custom validation + + return result } } ---- @@ -630,7 +649,7 @@ Java:: http .saml2Logout((saml2) -> saml2 .logoutResponse((response) -> response - .logoutResponseAuthenticator(myOpenSamlLogoutResponseAuthenticator) + .logoutResponseValidator(myOpenSamlLogoutResponseValidator) ) ); ----