diff options
author | Julien Lancelot <julien.lancelot@sonarsource.com> | 2016-11-30 17:46:08 +0100 |
---|---|---|
committer | Julien Lancelot <julien.lancelot@sonarsource.com> | 2016-12-05 10:07:12 +0100 |
commit | b8bb296917cace6ec2ae8ad5763ff132d0f6daa2 (patch) | |
tree | 42c6396d0955b479caa9eaec22686ff5a0b3e03b | |
parent | d753886eb88d763b78c7431746d8c91e89937070 (diff) | |
download | sonarqube-b8bb296917cace6ec2ae8ad5763ff132d0f6daa2.tar.gz sonarqube-b8bb296917cace6ec2ae8ad5763ff132d0f6daa2.zip |
SONAR-7774 Create /api/authentication/logout WS
10 files changed, 220 insertions, 8 deletions
diff --git a/server/sonar-server/src/main/java/org/sonar/server/authentication/AuthenticationModule.java b/server/sonar-server/src/main/java/org/sonar/server/authentication/AuthenticationModule.java index 639a9db840e..cbc0ce0fab8 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/authentication/AuthenticationModule.java +++ b/server/sonar-server/src/main/java/org/sonar/server/authentication/AuthenticationModule.java @@ -23,6 +23,7 @@ import org.sonar.core.platform.Module; import org.sonar.server.authentication.event.AuthenticationEventImpl; import org.sonar.server.authentication.ws.AuthenticationWs; import org.sonar.server.authentication.ws.LoginAction; +import org.sonar.server.authentication.ws.LogoutAction; import org.sonar.server.authentication.ws.ValidateAction; public class AuthenticationModule extends Module { @@ -43,6 +44,7 @@ public class AuthenticationModule extends Module { JwtHttpHandler.class, JwtCsrfVerifier.class, LoginAction.class, + LogoutAction.class, CredentialsAuthenticator.class, RealmAuthenticator.class, BasicAuthenticator.class, 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 baa29d745ff..391897dde77 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 @@ -161,7 +161,7 @@ public class JwtHttpHandler { jwtCsrfVerifier.refreshState(request, response, (String) token.get(CSRF_JWT_PARAM), sessionTimeoutInSeconds); } - void removeToken(HttpServletRequest request, HttpServletResponse response) { + public void removeToken(HttpServletRequest request, HttpServletResponse response) { response.addCookie(createCookie(request, JWT_COOKIE, null, 0)); jwtCsrfVerifier.removeState(request, response); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/authentication/UserSessionInitializer.java b/server/sonar-server/src/main/java/org/sonar/server/authentication/UserSessionInitializer.java index f63d4efd7d8..a466d6f5656 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/authentication/UserSessionInitializer.java +++ b/server/sonar-server/src/main/java/org/sonar/server/authentication/UserSessionInitializer.java @@ -42,6 +42,7 @@ import static org.sonar.server.authentication.AuthenticationError.handleAuthenti import static org.sonar.server.authentication.event.AuthenticationEvent.Method; import static org.sonar.server.authentication.event.AuthenticationEvent.Source; import static org.sonar.server.authentication.ws.LoginAction.AUTH_LOGIN_URL; +import static org.sonar.server.authentication.ws.LogoutAction.AUTH_LOGOUT_URL; import static org.sonar.server.authentication.ws.ValidateAction.AUTH_VALIDATE_URL; import static org.sonar.server.user.ServerUserSession.createForAnonymous; import static org.sonar.server.user.ServerUserSession.createForUser; @@ -64,7 +65,7 @@ public class UserSessionInitializer { "/sessions/*", "/api/system/db_migration_status", "/api/system/status", "/api/system/migrate_db", "/api/server/index", "/api/server/setup", "/api/server/version", - AUTH_LOGIN_URL, AUTH_VALIDATE_URL); + AUTH_LOGIN_URL, AUTH_VALIDATE_URL, AUTH_LOGOUT_URL); private static final UrlPattern URL_PATTERN = UrlPattern.builder() .includes("/*") diff --git a/server/sonar-server/src/main/java/org/sonar/server/authentication/ws/AuthenticationWs.java b/server/sonar-server/src/main/java/org/sonar/server/authentication/ws/AuthenticationWs.java index e55a96f5dc7..f17864b081e 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/authentication/ws/AuthenticationWs.java +++ b/server/sonar-server/src/main/java/org/sonar/server/authentication/ws/AuthenticationWs.java @@ -32,6 +32,7 @@ public class AuthenticationWs implements WebService { controller.setDescription("Handle authentication."); defineLoginAction(controller); + defineLogoutAction(controller); defineValidateAction(controller); controller.done(); @@ -61,4 +62,12 @@ public class AuthenticationWs implements WebService { .setRequired(true); } + private static void defineLogoutAction(NewController controller) { + controller.createAction("logout") + .setDescription("Logout a user.") + .setSince("6.3") + .setPost(true) + .setHandler(ServletFilterHandler.INSTANCE); + } + } diff --git a/server/sonar-server/src/main/java/org/sonar/server/authentication/ws/LoginAction.java b/server/sonar-server/src/main/java/org/sonar/server/authentication/ws/LoginAction.java index c7afb3f134c..e0e0dff34fd 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/authentication/ws/LoginAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/authentication/ws/LoginAction.java @@ -45,13 +45,12 @@ import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED; import static org.apache.commons.lang.StringUtils.isEmpty; import static org.sonar.server.authentication.event.AuthenticationEvent.Method; import static org.sonar.server.authentication.event.AuthenticationEvent.Source; +import static org.sonarqube.ws.client.WsRequest.Method.POST; public class LoginAction extends ServletFilter { public static final String AUTH_LOGIN_URL = "/api/authentication/login"; - private static final String POST = "POST"; - private final DbClient dbClient; private final CredentialsAuthenticator credentialsAuthenticator; private final JwtHttpHandler jwtHttpHandler; @@ -77,7 +76,7 @@ public class LoginAction extends ServletFilter { HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; - if (!request.getMethod().equals(POST)) { + if (!request.getMethod().equals(POST.name())) { response.setStatus(HTTP_BAD_REQUEST); return; } 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 new file mode 100644 index 00000000000..b951204ab7e --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/authentication/ws/LogoutAction.java @@ -0,0 +1,73 @@ +/* + * 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.ws; + +import java.io.IOException; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.sonar.api.web.ServletFilter; +import org.sonar.server.authentication.JwtHttpHandler; + +import static java.net.HttpURLConnection.HTTP_BAD_REQUEST; +import static org.sonarqube.ws.client.WsRequest.Method.POST; + +public class LogoutAction extends ServletFilter { + + public static final String AUTH_LOGOUT_URL = "/api/authentication/logout"; + + private final JwtHttpHandler jwtHttpHandler; + + public LogoutAction(JwtHttpHandler jwtHttpHandler) { + this.jwtHttpHandler = jwtHttpHandler; + } + + @Override + public UrlPattern doGetPattern() { + return UrlPattern.create(AUTH_LOGOUT_URL); + } + + @Override + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException { + HttpServletRequest request = (HttpServletRequest) servletRequest; + HttpServletResponse response = (HttpServletResponse) servletResponse; + + if (!request.getMethod().equals(POST.name())) { + response.setStatus(HTTP_BAD_REQUEST); + return; + } + jwtHttpHandler.removeToken(request, response); + } + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + // Nothing to do + } + + @Override + public void destroy() { + // Nothing to do + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/authentication/AuthenticationModuleTest.java b/server/sonar-server/src/test/java/org/sonar/server/authentication/AuthenticationModuleTest.java new file mode 100644 index 00000000000..ca37d899066 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/authentication/AuthenticationModuleTest.java @@ -0,0 +1,37 @@ +/* + * 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 org.junit.Test; +import org.sonar.core.platform.ComponentContainer; + +import static org.assertj.core.api.Assertions.assertThat; + +public class AuthenticationModuleTest { + + @Test + public void verify_count_of_added_components() { + ComponentContainer container = new ComponentContainer(); + new AuthenticationModule().configure(container); + assertThat(container.size()).isEqualTo(2 + 20); + } + +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/authentication/UserSessionInitializerTest.java b/server/sonar-server/src/test/java/org/sonar/server/authentication/UserSessionInitializerTest.java index b57103037e9..8c08aad4059 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/authentication/UserSessionInitializerTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/authentication/UserSessionInitializerTest.java @@ -96,6 +96,7 @@ public class UserSessionInitializerTest { assertPathIsNotIgnored("/api/server_id/show"); assertPathIsIgnored("/api/authentication/login"); + assertPathIsIgnored("/api/authentication/logout"); assertPathIsIgnored("/api/authentication/validate"); assertPathIsIgnored("/batch/index"); assertPathIsIgnored("/batch/file"); diff --git a/server/sonar-server/src/test/java/org/sonar/server/authentication/ws/AuthenticationWsTest.java b/server/sonar-server/src/test/java/org/sonar/server/authentication/ws/AuthenticationWsTest.java index 6fabde5e670..980e4a2e433 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/authentication/ws/AuthenticationWsTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/authentication/ws/AuthenticationWsTest.java @@ -19,13 +19,13 @@ */ package org.sonar.server.authentication.ws; -import static org.assertj.core.api.Assertions.assertThat; - import org.junit.Test; import org.sonar.api.server.ws.WebService; import org.sonar.server.ws.ServletFilterHandler; import org.sonar.server.ws.WsTester; +import static org.assertj.core.api.Assertions.assertThat; + public class AuthenticationWsTest { WsTester tester = new WsTester(new AuthenticationWs()); @@ -35,7 +35,7 @@ public class AuthenticationWsTest { WebService.Controller controller = tester.controller("api/authentication"); assertThat(controller).isNotNull(); assertThat(controller.description()).isNotEmpty(); - assertThat(controller.actions()).hasSize(2); + assertThat(controller.actions()).hasSize(3); WebService.Action validate = controller.action("validate"); assertThat(validate).isNotNull(); @@ -48,5 +48,11 @@ public class AuthenticationWsTest { assertThat(login.handler()).isInstanceOf(ServletFilterHandler.class); assertThat(login.isPost()).isTrue(); assertThat(login.params()).hasSize(2); + + WebService.Action logout = controller.action("logout"); + assertThat(logout).isNotNull(); + assertThat(logout.handler()).isInstanceOf(ServletFilterHandler.class); + assertThat(logout.isPost()).isTrue(); + assertThat(logout.params()).isEmpty(); } } 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 new file mode 100644 index 00000000000..fad0f70d92c --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/authentication/ws/LogoutActionTest.java @@ -0,0 +1,84 @@ +/* + * 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.ws; + +import java.io.IOException; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.junit.Rule; +import org.junit.Test; +import org.sonar.api.utils.System2; +import org.sonar.db.DbTester; +import org.sonar.server.authentication.JwtHttpHandler; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; + +public class LogoutActionTest { + + @Rule + public DbTester dbTester = DbTester.create(System2.INSTANCE); + + HttpServletRequest request = mock(HttpServletRequest.class); + HttpServletResponse response = mock(HttpServletResponse.class); + FilterChain chain = mock(FilterChain.class); + + JwtHttpHandler jwtHttpHandler = mock(JwtHttpHandler.class); + + LogoutAction underTest = new LogoutAction(jwtHttpHandler); + + @Test + public void do_get_pattern() throws Exception { + assertThat(underTest.doGetPattern().matches("/api/authentication/logout")).isTrue(); + assertThat(underTest.doGetPattern().matches("/api/authentication/login")).isFalse(); + assertThat(underTest.doGetPattern().matches("/api/authentication/logou")).isFalse(); + assertThat(underTest.doGetPattern().matches("/api/authentication/logoutthing")).isFalse(); + assertThat(underTest.doGetPattern().matches("/foo")).isFalse(); + } + + @Test + public void return_400_on_get_request() throws Exception { + when(request.getMethod()).thenReturn("GET"); + + underTest.doFilter(request, response, chain); + + verifyZeroInteractions(jwtHttpHandler, chain); + verify(response).setStatus(400); + } + + @Test + public void logout() throws Exception { + executeRequest(); + + verify(jwtHttpHandler).removeToken(request, response); + verifyZeroInteractions(chain); + } + + private void executeRequest() throws IOException, ServletException { + when(request.getMethod()).thenReturn("POST"); + underTest.doFilter(request, response, chain); + } +} |