Skip to content
Open

Fix docs #18488

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}

Expand Down Expand Up @@ -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());
}
----
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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].
Original file line number Diff line number Diff line change
Expand Up @@ -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]
======
Expand Down Expand Up @@ -3020,7 +3020,7 @@ void fooWhenDeniedThenReturnStars() {

@Test
void barWhenDeniedThenReturnQuestionMarks() {
String value = this.myService.foo();
String value = this.myService.bar();
assertThat(value).isEqualTo("???");
}
----
Expand All @@ -3040,7 +3040,7 @@ fun fooWhenDeniedThenReturnStars() {

@Test
fun barWhenDeniedThenReturnQuestionMarks() {
val value: String = myService.foo()
val value: String = myService.bar()
assertThat(value).isEqualTo("???")
}
----
Expand Down Expand Up @@ -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].
Original file line number Diff line number Diff line change
Expand Up @@ -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 <<oauth2resourceserver-jwt-architecture-jwtauthenticationconverter,`JwtAuthenticationConverter`>> should inspect, like so:

.Authorities Claim Configuration
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -755,7 +755,7 @@ public OpaqueTokenIntrospector introspector(RestTemplateBuilder builder, OAuth2R
.setReadTimeout(Duration.ofSeconds(60))
.build();

return SpringOpaqueTokenIntrospector(introspectionUri, rest);
return new SpringOpaqueTokenIntrospector(introspectionUri, rest);
}
----

Expand Down
26 changes: 14 additions & 12 deletions docs/modules/ROOT/pages/servlet/saml2/login/authentication.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -270,12 +270,13 @@ class MyUserDetailsResponseAuthenticationConverter implements Converter<Response
@Override
public Saml2Authentication convert(ResponseToken responseToken) {
Saml2Authentication authentication = this.delegate.convert(responseToken); <1>
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<GrantedAuthority> authorities = principal.getAuthorities();
return new Saml2AssertionAuthentication(userDetails, assertion, authorities); <3>
saml2Response, CollectionUtils.getFirst(responseToken.getResponse().getAssertions()));
Collection<GrantedAuthority> authorities = user.getAuthorities();
return new Saml2AssertionAuthentication(user, assertion, authorities); <3>
}

}
Expand All @@ -286,18 +287,19 @@ Kotlin::
[source,kotlin,role="secondary"]
----
@Component
open class MyUserDetailsResponseAuthenticationConverter(val delegate: ResponseAuthenticationConverter,
UserDetailsService userDetailsService): Converter<ResponseToken, Saml2Authentication> {
open class MyUserDetailsResponseAuthenticationConverter(private val userDetailsService: UserDetailsService) : Converter<ResponseToken, Saml2Authentication> {

@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>
}

}
Expand Down
4 changes: 2 additions & 2 deletions docs/modules/ROOT/pages/servlet/saml2/login/overview.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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 `<Subject>` and any `<AudienceRestriction>` conditions.
Expand Down Expand Up @@ -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].
77 changes: 48 additions & 29 deletions docs/modules/ROOT/pages/servlet/saml2/logout.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
----

Expand All @@ -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
}
----
======
Expand All @@ -477,8 +477,8 @@ Java::
----
http
.saml2Logout((saml2) -> saml2
.logoutRequest((request) -> request
.logoutRequestResolver(this.logoutRequestResolver)
.logoutResponse((request) -> request
.logoutResponseResolver(this.logoutResponseResolver)
)
);
----
Expand All @@ -489,8 +489,8 @@ Kotlin::
----
http {
saml2Logout {
logoutRequest {
logoutRequestResolver = this.logoutRequestResolver
logoutResponse {
logoutResponseResolver = logoutResponseResolver
}
}
}
Expand All @@ -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;
}
}
----
Expand All @@ -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
}
}
----
Expand Down Expand Up @@ -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;
}
}
----
Expand All @@ -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
}
}
----
Expand All @@ -630,7 +649,7 @@ Java::
http
.saml2Logout((saml2) -> saml2
.logoutResponse((response) -> response
.logoutResponseAuthenticator(myOpenSamlLogoutResponseAuthenticator)
.logoutResponseValidator(myOpenSamlLogoutResponseValidator)
)
);
----
Expand Down