From 21750242cfc871d79a540ae2e243e33a885d08c7 Mon Sep 17 00:00:00 2001 From: Eric Deandrea Date: Thu, 10 May 2018 09:38:07 -0400 Subject: [PATCH] Add HttpStatusReturningServerLogoutSuccessHandler An HttpStatusReturningServerLogoutSuccessHandler is missing on the reactive side - essentially the reactive equivalent of HttpStatusReturningLogoutSuccessHandler. Fixes gh-5081 --- ...usReturningServerLogoutSuccessHandler.java | 65 ++++++++++++++++++ ...urningServerLogoutSuccessHandlerTests.java | 67 +++++++++++++++++++ 2 files changed, 132 insertions(+) create mode 100644 web/src/main/java/org/springframework/security/web/server/authentication/logout/HttpStatusReturningServerLogoutSuccessHandler.java create mode 100644 web/src/test/java/org/springframework/security/web/server/authentication/logout/HttpStatusReturningServerLogoutSuccessHandlerTests.java diff --git a/web/src/main/java/org/springframework/security/web/server/authentication/logout/HttpStatusReturningServerLogoutSuccessHandler.java b/web/src/main/java/org/springframework/security/web/server/authentication/logout/HttpStatusReturningServerLogoutSuccessHandler.java new file mode 100644 index 0000000000..445186b914 --- /dev/null +++ b/web/src/main/java/org/springframework/security/web/server/authentication/logout/HttpStatusReturningServerLogoutSuccessHandler.java @@ -0,0 +1,65 @@ +/* + * Copyright 2002-2018 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.security.web.server.authentication.logout; + +import org.springframework.http.HttpStatus; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.server.WebFilterExchange; +import org.springframework.util.Assert; + +import reactor.core.publisher.Mono; + +/** + * Implementation of the {@link ServerLogoutSuccessHandler}. By default returns an HTTP status code of {@code 200}. + * This is useful in REST-type scenarios where a redirect upon a successful logout is not desired. + * + * @author Eric Deandrea + * @since 5.1 + */ +public class HttpStatusReturningServerLogoutSuccessHandler implements ServerLogoutSuccessHandler { + private final HttpStatus httpStatusToReturn; + + /** + * Initialize the {@code HttpStatusReturningServerLogoutSuccessHandler} with a user-defined {@link HttpStatus}. + * + * @param httpStatusToReturn Must not be {@code null}. + */ + public HttpStatusReturningServerLogoutSuccessHandler(HttpStatus httpStatusToReturn) { + Assert.notNull(httpStatusToReturn, "The provided HttpStatus must not be null."); + this.httpStatusToReturn = httpStatusToReturn; + } + + /** + * Initialize the {@code HttpStatusReturningServerLogoutSuccessHandler} with the default {@link HttpStatus#OK}. + */ + public HttpStatusReturningServerLogoutSuccessHandler() { + this.httpStatusToReturn = HttpStatus.OK; + } + + /** + * Implementation of {@link ServerLogoutSuccessHandler#onLogoutSuccess(WebFilterExchange, Authentication)}. Sets the status + * on the {@link WebFilterExchange}. + * + * @param exchange The exchange + * @param authentication The {@link Authentication} + * @return A completion notification (success or error) + */ + @Override + public Mono onLogoutSuccess(WebFilterExchange exchange, Authentication authentication) { + return Mono.fromRunnable(() -> exchange.getExchange().getResponse().setStatusCode(this.httpStatusToReturn)); + } +} diff --git a/web/src/test/java/org/springframework/security/web/server/authentication/logout/HttpStatusReturningServerLogoutSuccessHandlerTests.java b/web/src/test/java/org/springframework/security/web/server/authentication/logout/HttpStatusReturningServerLogoutSuccessHandlerTests.java new file mode 100644 index 0000000000..0f8250bc30 --- /dev/null +++ b/web/src/test/java/org/springframework/security/web/server/authentication/logout/HttpStatusReturningServerLogoutSuccessHandlerTests.java @@ -0,0 +1,67 @@ +/* + * Copyright 2002-2018 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.security.web.server.authentication.logout; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.mock; + +import org.junit.Test; + +import org.springframework.http.HttpStatus; +import org.springframework.mock.http.server.reactive.MockServerHttpRequest; +import org.springframework.mock.web.server.MockServerWebExchange; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.server.WebFilterExchange; +import org.springframework.web.server.WebFilterChain; + +/** + * @author Eric Deandrea + * @since 5.1 + */ +public class HttpStatusReturningServerLogoutSuccessHandlerTests { + @Test + public void defaultHttpStatusBeingReturned() { + WebFilterExchange filterExchange = buildFilterExchange(); + new HttpStatusReturningServerLogoutSuccessHandler() + .onLogoutSuccess(filterExchange, mock(Authentication.class)).block(); + + assertThat(filterExchange.getExchange().getResponse().getStatusCode()).isEqualTo(HttpStatus.OK); + } + + @Test + public void customHttpStatusBeingReturned() { + WebFilterExchange filterExchange = buildFilterExchange(); + new HttpStatusReturningServerLogoutSuccessHandler(HttpStatus.NO_CONTENT) + .onLogoutSuccess(filterExchange, mock(Authentication.class)).block(); + + assertThat(filterExchange.getExchange().getResponse().getStatusCode()).isEqualTo(HttpStatus.NO_CONTENT); + } + + @Test + public void nullHttpStatusThrowsException() { + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> new HttpStatusReturningServerLogoutSuccessHandler(null)) + .withMessage("The provided HttpStatus must not be null."); + } + + private static WebFilterExchange buildFilterExchange() { + MockServerHttpRequest request = MockServerHttpRequest.get("/").build(); + MockServerWebExchange exchange = MockServerWebExchange.from(request); + + return new WebFilterExchange(exchange, mock(WebFilterChain.class)); + } +}