diff options
author | Julien Lancelot <julien.lancelot@sonarsource.com> | 2017-01-26 08:50:24 +0100 |
---|---|---|
committer | Julien Lancelot <julien.lancelot@sonarsource.com> | 2017-01-26 17:54:19 +0100 |
commit | c61cf09e06a03410f1d9fe07cd6184e171e30448 (patch) | |
tree | 68e1f9d37e802aa72d3775166cd6a902dd4bbfde /server | |
parent | aea01719326b281aa476eaddd8bd26cebc83ce87 (diff) | |
download | sonarqube-c61cf09e06a03410f1d9fe07cd6184e171e30448.tar.gz sonarqube-c61cf09e06a03410f1d9fe07cd6184e171e30448.zip |
SONAR-8684 Always create cookie with web context
Diffstat (limited to 'server')
9 files changed, 251 insertions, 168 deletions
diff --git a/server/sonar-server/src/main/java/org/sonar/server/authentication/CookieUtils.java b/server/sonar-server/src/main/java/org/sonar/server/authentication/CookieUtils.java deleted file mode 100644 index 02baa1c1b6c..00000000000 --- a/server/sonar-server/src/main/java/org/sonar/server/authentication/CookieUtils.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -package org.sonar.server.authentication; - -import java.util.Arrays; -import java.util.Optional; -import javax.annotation.Nullable; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; - -public class CookieUtils { - - private static final String HTTPS_HEADER = "X-Forwarded-Proto"; - private static final String HTTPS_VALUE = "https"; - - private CookieUtils() { - // Only static methods - } - - public static Optional<Cookie> findCookie(String cookieName, HttpServletRequest request) { - Cookie[] cookies = request.getCookies(); - if (cookies == null) { - return Optional.empty(); - } - return Arrays.stream(cookies) - .filter(cookie -> cookieName.equals(cookie.getName())) - .findFirst(); - } - - public static Cookie createCookie(String name, @Nullable String value, boolean httpOnly, int expiry, HttpServletRequest request) { - Cookie cookie = new Cookie(name, value); - // Path is set "/" in order to allow rails to be able to remove cookies - // TODO When logout when be implemented in Java (SONAR-7774), following line should be replaced by - // cookie.setPath(request.getContextPath()"/"); - cookie.setPath("/"); - cookie.setSecure(isHttps(request)); - cookie.setHttpOnly(httpOnly); - cookie.setMaxAge(expiry); - return cookie; - } - - private static boolean isHttps(HttpServletRequest request) { - return HTTPS_VALUE.equalsIgnoreCase(request.getHeader(HTTPS_HEADER)); - } -} diff --git a/server/sonar-server/src/main/java/org/sonar/server/authentication/Cookies.java b/server/sonar-server/src/main/java/org/sonar/server/authentication/Cookies.java new file mode 100644 index 00000000000..c1cd587abb0 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/authentication/Cookies.java @@ -0,0 +1,106 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package org.sonar.server.authentication; + +import java.util.Arrays; +import java.util.Optional; +import javax.annotation.Nullable; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; + +import static com.google.common.base.Strings.isNullOrEmpty; +import static java.util.Objects.requireNonNull; + +public class Cookies { + + private static final String HTTPS_HEADER = "X-Forwarded-Proto"; + private static final String HTTPS_VALUE = "https"; + + private Cookies() { + // Only static methods + } + + public static Optional<Cookie> findCookie(String cookieName, HttpServletRequest request) { + Cookie[] cookies = request.getCookies(); + if (cookies == null) { + return Optional.empty(); + } + return Arrays.stream(cookies) + .filter(cookie -> cookieName.equals(cookie.getName())) + .findFirst(); + } + + public static CookieBuilder newCookieBuilder(HttpServletRequest request) { + return new CookieBuilder(request); + } + + public static class CookieBuilder { + + private final HttpServletRequest request; + + private String name; + private String value; + private boolean httpOnly; + private int expiry; + + public CookieBuilder(HttpServletRequest request) { + this.request = request; + } + + public CookieBuilder setName(String name) { + this.name = requireNonNull(name); + return this; + } + + public CookieBuilder setValue(@Nullable String value) { + this.value = value; + return this; + } + + public CookieBuilder setHttpOnly(boolean httpOnly) { + this.httpOnly = httpOnly; + return this; + } + + public CookieBuilder setExpiry(int expiry) { + this.expiry = expiry; + return this; + } + + public Cookie build() { + Cookie cookie = new Cookie(requireNonNull(name), value); + cookie.setPath(getContextPath(request)); + cookie.setSecure(isHttps(request)); + cookie.setHttpOnly(httpOnly); + cookie.setMaxAge(expiry); + return cookie; + } + + private static boolean isHttps(HttpServletRequest request) { + return HTTPS_VALUE.equalsIgnoreCase(request.getHeader(HTTPS_HEADER)); + } + + private static String getContextPath(HttpServletRequest request) { + String path = request.getContextPath(); + return isNullOrEmpty(path) ? "/" : path; + } + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/authentication/JwtCsrfVerifier.java b/server/sonar-server/src/main/java/org/sonar/server/authentication/JwtCsrfVerifier.java index 4a156d9799e..28ada037e16 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/authentication/JwtCsrfVerifier.java +++ b/server/sonar-server/src/main/java/org/sonar/server/authentication/JwtCsrfVerifier.java @@ -31,7 +31,7 @@ import org.apache.commons.lang.StringUtils; import org.sonar.server.authentication.event.AuthenticationException; import static org.apache.commons.lang.StringUtils.isBlank; -import static org.sonar.server.authentication.CookieUtils.createCookie; +import static org.sonar.server.authentication.Cookies.newCookieBuilder; import static org.sonar.server.authentication.event.AuthenticationEvent.Method; import static org.sonar.server.authentication.event.AuthenticationEvent.Source; @@ -48,7 +48,7 @@ public class JwtCsrfVerifier { // Create a state token to prevent request forgery. // Store it in the cookie for later validation. String state = new BigInteger(130, new SecureRandom()).toString(32); - response.addCookie(createCookie(CSRF_STATE_COOKIE, state, false, timeoutInSeconds, request)); + response.addCookie(newCookieBuilder(request).setName(CSRF_STATE_COOKIE).setValue(state).setHttpOnly(false).setExpiry(timeoutInSeconds).build()); return state; } @@ -79,11 +79,11 @@ public class JwtCsrfVerifier { } public void refreshState(HttpServletRequest request, HttpServletResponse response, String csrfState, int timeoutInSeconds) { - response.addCookie(createCookie(CSRF_STATE_COOKIE, csrfState, false, timeoutInSeconds, request)); + response.addCookie(newCookieBuilder(request).setName(CSRF_STATE_COOKIE).setValue(csrfState).setHttpOnly(false).setExpiry(timeoutInSeconds).build()); } public void removeState(HttpServletRequest request, HttpServletResponse response) { - response.addCookie(createCookie(CSRF_STATE_COOKIE, null, false, 0, request)); + response.addCookie(newCookieBuilder(request).setName(CSRF_STATE_COOKIE).setValue(null).setHttpOnly(false).setExpiry(0).build()); } private static boolean shouldRequestBeChecked(HttpServletRequest request) { diff --git a/server/sonar-server/src/main/java/org/sonar/server/authentication/JwtHttpHandler.java b/server/sonar-server/src/main/java/org/sonar/server/authentication/JwtHttpHandler.java index 5b9eb6fabe3..cd01aa1d021 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/authentication/JwtHttpHandler.java +++ b/server/sonar-server/src/main/java/org/sonar/server/authentication/JwtHttpHandler.java @@ -41,7 +41,8 @@ import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; import static org.apache.commons.lang.StringUtils.isEmpty; import static org.apache.commons.lang.time.DateUtils.addSeconds; -import static org.sonar.server.authentication.CookieUtils.findCookie; +import static org.sonar.server.authentication.Cookies.findCookie; +import static org.sonar.server.authentication.Cookies.newCookieBuilder; @ServerSide public class JwtHttpHandler { @@ -167,7 +168,7 @@ public class JwtHttpHandler { } private static Cookie createCookie(HttpServletRequest request, String name, @Nullable String value, int expirationInSeconds) { - return CookieUtils.createCookie(name, value, true, expirationInSeconds, request); + return newCookieBuilder(request).setName(name).setValue(value).setHttpOnly(true).setExpiry(expirationInSeconds).build(); } private Optional<UserDto> selectUserFromDb(String userLogin) { diff --git a/server/sonar-server/src/main/java/org/sonar/server/authentication/OAuthCsrfVerifier.java b/server/sonar-server/src/main/java/org/sonar/server/authentication/OAuthCsrfVerifier.java index 2c29f2c1a09..e75171eeb51 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/authentication/OAuthCsrfVerifier.java +++ b/server/sonar-server/src/main/java/org/sonar/server/authentication/OAuthCsrfVerifier.java @@ -30,8 +30,8 @@ import org.sonar.server.authentication.event.AuthenticationException; import static java.lang.String.format; import static org.apache.commons.codec.digest.DigestUtils.sha256Hex; import static org.apache.commons.lang.StringUtils.isBlank; -import static org.sonar.server.authentication.CookieUtils.createCookie; -import static org.sonar.server.authentication.CookieUtils.findCookie; +import static org.sonar.server.authentication.Cookies.findCookie; +import static org.sonar.server.authentication.Cookies.newCookieBuilder; import static org.sonar.server.authentication.event.AuthenticationEvent.Source; public class OAuthCsrfVerifier { @@ -42,7 +42,7 @@ public class OAuthCsrfVerifier { // Create a state token to prevent request forgery. // Store it in the session for later validation. String state = new BigInteger(130, new SecureRandom()).toString(32); - response.addCookie(createCookie(CSRF_STATE_COOKIE, sha256Hex(state), true, -1, request)); + response.addCookie(newCookieBuilder(request).setName(CSRF_STATE_COOKIE).setValue(sha256Hex(state)).setHttpOnly(true).setExpiry(-1).build()); return state; } @@ -54,7 +54,7 @@ public class OAuthCsrfVerifier { String hashInCookie = cookie.getValue(); // remove cookie - response.addCookie(createCookie(CSRF_STATE_COOKIE, null, true, 0, request)); + response.addCookie(newCookieBuilder(request).setName(CSRF_STATE_COOKIE).setValue(null).setHttpOnly(true).setExpiry(0).build()); String stateInRequest = request.getParameter("state"); if (isBlank(stateInRequest) || !sha256Hex(stateInRequest).equals(hashInCookie)) { diff --git a/server/sonar-server/src/main/java/org/sonar/server/authentication/ws/LogoutAction.java b/server/sonar-server/src/main/java/org/sonar/server/authentication/ws/LogoutAction.java index aa48ec86df3..f191d39f6bb 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/authentication/ws/LogoutAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/authentication/ws/LogoutAction.java @@ -37,7 +37,6 @@ import org.sonar.server.authentication.event.AuthenticationException; import org.sonar.server.ws.ServletFilterHandler; import static java.net.HttpURLConnection.HTTP_BAD_REQUEST; -import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED; import static org.sonar.server.authentication.ws.AuthenticationWs.AUTHENTICATION_CONTROLLER; import static org.sonarqube.ws.client.WsRequest.Method.POST; @@ -81,21 +80,23 @@ public class LogoutAction extends ServletFilter implements AuthenticationWsActio } private void logout(HttpServletRequest request, HttpServletResponse response) { + generateAuthenticationEvent(request, response); + jwtHttpHandler.removeToken(request, response); + } + + /** + * The generation of the authentication event should not prevent the removal of JWT cookie, that's why it's done in a separate method + */ + private void generateAuthenticationEvent(HttpServletRequest request, HttpServletResponse response) { try { - generateAuthenticationEvent(request, response); - jwtHttpHandler.removeToken(request, response); + Optional<JwtHttpHandler.Token> token = jwtHttpHandler.getToken(request, response); + String userLogin = token.isPresent() ? token.get().getUserDto().getLogin() : null; + authenticationEvent.logoutSuccess(request, userLogin); } catch (AuthenticationException e) { - response.setStatus(HTTP_UNAUTHORIZED); authenticationEvent.logoutFailure(request, e.getMessage()); } } - private void generateAuthenticationEvent(HttpServletRequest request, HttpServletResponse response) { - Optional<JwtHttpHandler.Token> token = jwtHttpHandler.getToken(request, response); - String userLogin = token.isPresent() ? token.get().getUserDto().getLogin() : null; - authenticationEvent.logoutSuccess(request, userLogin); - } - @Override public void init(FilterConfig filterConfig) throws ServletException { // Nothing to do diff --git a/server/sonar-server/src/test/java/org/sonar/server/authentication/CookieUtilsTest.java b/server/sonar-server/src/test/java/org/sonar/server/authentication/CookieUtilsTest.java deleted file mode 100644 index 02489f17594..00000000000 --- a/server/sonar-server/src/test/java/org/sonar/server/authentication/CookieUtilsTest.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -package org.sonar.server.authentication; - -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import org.junit.Test; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class CookieUtilsTest { - - private static final String HTTPS_HEADER = "X-Forwarded-Proto"; - - HttpServletRequest request = mock(HttpServletRequest.class); - - @Test - public void create_cookie() throws Exception { - Cookie cookie = CookieUtils.createCookie("name", "value", true, 10, request); - assertThat(cookie.getName()).isEqualTo("name"); - assertThat(cookie.getValue()).isEqualTo("value"); - assertThat(cookie.isHttpOnly()).isTrue(); - assertThat(cookie.getMaxAge()).isEqualTo(10); - assertThat(cookie.getSecure()).isFalse(); - } - - @Test - public void create_not_secured_cookie_when_header_is_not_http() throws Exception { - when(request.getHeader(HTTPS_HEADER)).thenReturn("http"); - Cookie cookie = CookieUtils.createCookie("name", "value", true, 10, request); - assertThat(cookie.getSecure()).isFalse(); - } - - @Test - public void create_secured_cookie_when_X_Forwarded_Proto_header_is_https() throws Exception { - when(request.getHeader(HTTPS_HEADER)).thenReturn("https"); - Cookie cookie = CookieUtils.createCookie("name", "value", true, 10, request); - assertThat(cookie.getSecure()).isTrue(); - } - - @Test - public void create_secured_cookie_when_X_Forwarded_Proto_header_is_HTTPS() throws Exception { - when(request.getHeader(HTTPS_HEADER)).thenReturn("HTTPS"); - Cookie cookie = CookieUtils.createCookie("name", "value", true, 10, request); - assertThat(cookie.getSecure()).isTrue(); - } - - @Test - public void find_cookie() throws Exception { - Cookie cookie = new Cookie("name", "value"); - when(request.getCookies()).thenReturn(new Cookie[] {cookie}); - - assertThat(CookieUtils.findCookie("name", request)).isPresent(); - assertThat(CookieUtils.findCookie("NAME", request)).isEmpty(); - assertThat(CookieUtils.findCookie("unknown", request)).isEmpty(); - } - - @Test - public void does_not_fail_to_find_cookie_when_no_cookie() throws Exception { - assertThat(CookieUtils.findCookie("unknown", request)).isEmpty(); - } -} diff --git a/server/sonar-server/src/test/java/org/sonar/server/authentication/CookiesTest.java b/server/sonar-server/src/test/java/org/sonar/server/authentication/CookiesTest.java new file mode 100644 index 00000000000..c9c4f011b37 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/authentication/CookiesTest.java @@ -0,0 +1,122 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package org.sonar.server.authentication; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.sonar.server.authentication.Cookies.findCookie; +import static org.sonar.server.authentication.Cookies.newCookieBuilder; + +public class CookiesTest { + + private static final String HTTPS_HEADER = "X-Forwarded-Proto"; + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + private HttpServletRequest request = mock(HttpServletRequest.class); + + @Test + public void create_cookie() throws Exception { + Cookie cookie = newCookieBuilder(request).setName("name").setValue("value").setHttpOnly(true).setExpiry(10).build(); + assertThat(cookie.getName()).isEqualTo("name"); + assertThat(cookie.getValue()).isEqualTo("value"); + assertThat(cookie.isHttpOnly()).isTrue(); + assertThat(cookie.getMaxAge()).isEqualTo(10); + assertThat(cookie.getSecure()).isFalse(); + assertThat(cookie.getPath()).isEqualTo("/"); + } + + @Test + public void create_cookie_without_value() throws Exception { + Cookie cookie = newCookieBuilder(request).setName("name").build(); + assertThat(cookie.getName()).isEqualTo("name"); + assertThat(cookie.getValue()).isNull(); + } + + @Test + public void create_cookie_when_web_context() throws Exception { + when(request.getContextPath()).thenReturn("/sonarqube"); + Cookie cookie = newCookieBuilder(request).setName("name").setValue("value").setHttpOnly(true).setExpiry(10).build(); + assertThat(cookie.getName()).isEqualTo("name"); + assertThat(cookie.getValue()).isEqualTo("value"); + assertThat(cookie.isHttpOnly()).isTrue(); + assertThat(cookie.getMaxAge()).isEqualTo(10); + assertThat(cookie.getSecure()).isFalse(); + assertThat(cookie.getPath()).isEqualTo("/sonarqube"); + } + + @Test + public void create_not_secured_cookie_when_header_is_not_http() throws Exception { + when(request.getHeader(HTTPS_HEADER)).thenReturn("http"); + Cookie cookie = newCookieBuilder(request).setName("name").setValue("value").setHttpOnly(true).setExpiry(10).build(); + assertThat(cookie.getSecure()).isFalse(); + } + + @Test + public void create_secured_cookie_when_X_Forwarded_Proto_header_is_https() throws Exception { + when(request.getHeader(HTTPS_HEADER)).thenReturn("https"); + Cookie cookie = newCookieBuilder(request).setName("name").setValue("value").setHttpOnly(true).setExpiry(10).build(); + assertThat(cookie.getSecure()).isTrue(); + } + + @Test + public void create_secured_cookie_when_X_Forwarded_Proto_header_is_HTTPS() throws Exception { + when(request.getHeader(HTTPS_HEADER)).thenReturn("HTTPS"); + Cookie cookie = newCookieBuilder(request).setName("name").setValue("value").setHttpOnly(true).setExpiry(10).build(); + assertThat(cookie.getSecure()).isTrue(); + } + + @Test + public void find_cookie() throws Exception { + Cookie cookie = newCookieBuilder(request).setName("name").setValue("value").build(); + when(request.getCookies()).thenReturn(new Cookie[] {cookie}); + + assertThat(findCookie("name", request)).isPresent(); + assertThat(findCookie("NAME", request)).isEmpty(); + assertThat(findCookie("unknown", request)).isEmpty(); + } + + @Test + public void does_not_fail_to_find_cookie_when_no_cookie() throws Exception { + assertThat(findCookie("unknown", request)).isEmpty(); + } + + @Test + public void fail_with_NPE_when_cookie_name_is_null() throws Exception { + expectedException.expect(NullPointerException.class); + newCookieBuilder(request).setName(null); + } + + @Test + public void fail_with_NPE_when_cookie_has_no_name() throws Exception { + expectedException.expect(NullPointerException.class); + newCookieBuilder(request).setName(null); + } + +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/authentication/ws/LogoutActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/authentication/ws/LogoutActionTest.java index 1382808223c..41949c870c3 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/authentication/ws/LogoutActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/authentication/ws/LogoutActionTest.java @@ -40,7 +40,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Matchers.any; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; @@ -113,8 +112,7 @@ public class LogoutActionTest { executeRequest(); verify(authenticationEvent).logoutFailure(request, "error!"); - verify(jwtHttpHandler, never()).removeToken(any(HttpServletRequest.class), any(HttpServletResponse.class)); - verify(response).setStatus(401); + verify(jwtHttpHandler).removeToken(any(HttpServletRequest.class), any(HttpServletResponse.class)); verifyZeroInteractions(chain); } |