From 68977b2bedc95c738b58bc8be83a00d21e13c32e Mon Sep 17 00:00:00 2001 From: Antoine Vinot Date: Thu, 13 Apr 2023 16:57:56 +0200 Subject: [PATCH] SONAR-19045 Migrate from javaxi.servlet to framework agnostic plugin api classes Co-authored-by: Eric Giffon Co-authored-by: Alain Kermis Co-authored-by: Antoine Vinot Co-authored-by: Jacek Poreda --- gradle.properties | 2 +- server/sonar-auth-bitbucket/build.gradle | 1 + .../bitbucket/BitbucketIdentityProvider.java | 4 +- .../sonar/auth/bitbucket/IntegrationTest.java | 27 ++- server/sonar-auth-github/build.gradle | 1 + .../auth/github/GitHubIdentityProvider.java | 4 +- .../sonar/auth/github/IntegrationTest.java | 27 ++- .../auth/gitlab/GitLabIdentityProvider.java | 4 +- .../sonar/auth/gitlab/IntegrationTest.java | 26 +-- .../auth/ldap/DefaultLdapAuthenticatorIT.java | 4 +- .../ldap/DefaultLdapGroupsProviderIT.java | 4 +- .../auth/ldap/DefaultLdapUsersProviderIT.java | 4 +- .../java/org/sonar/auth/ldap/KerberosIT.java | 10 +- .../java/org/sonar/auth/ldap/LdapRealmIT.java | 6 +- .../sonar/auth/ldap/LdapAuthenticator.java | 8 +- .../sonar/auth/ldap/LdapGroupsProvider.java | 4 +- .../sonar/auth/ldap/LdapUsersProvider.java | 4 +- server/sonar-auth-saml/build.gradle | 1 + .../auth/saml/SamlIdentityProviderIT.java | 36 +++- .../saml/SamlAuthStatusPageGenerator.java | 6 +- .../sonar/auth/saml/SamlAuthenticator.java | 23 ++- .../sonar/auth/saml/SamlIdentityProvider.java | 12 +- .../saml/SamlAuthStatusPageGeneratorTest.java | 8 +- .../auth/saml/SamlAuthenticatorTest.java | 10 +- .../sonar/server/http/JavaxHttpRequest.java | 183 ++++++++++++++++++ .../sonar/server/http/JavaxHttpResponse.java | 109 +++++++++++ .../server/http/JavaxHttpRequestTest.java | 106 ++++++++++ .../server/http/JavaxHttpResponseTest.java | 77 ++++++++ .../authentication/AuthenticationError.java | 14 +- .../authentication/AuthenticationFilter.java | 12 +- .../AuthenticationRedirection.java | 11 +- .../authentication/BaseContextFactory.java | 26 ++- .../authentication/BasicAuthentication.java | 7 +- .../sonar/server/authentication/Cookies.java | 21 +- .../CredentialsAuthentication.java | 5 +- .../CredentialsExternalAuthentication.java | 23 ++- ...DefaultAdminCredentialsVerifierFilter.java | 23 +-- .../GithubWebhookAuthentication.java | 10 +- .../HttpHeadersAuthentication.java | 12 +- .../server/authentication/InitFilter.java | 27 ++- .../authentication/JwtCsrfVerifier.java | 17 +- .../server/authentication/JwtHttpHandler.java | 34 ++-- .../LdapCredentialsAuthentication.java | 8 +- .../OAuth2AuthenticationParameters.java | 10 +- .../OAuth2AuthenticationParametersImpl.java | 17 +- .../authentication/OAuth2CallbackFilter.java | 31 ++- .../authentication/OAuth2ContextFactory.java | 30 ++- .../authentication/OAuthCsrfVerifier.java | 12 +- .../authentication/RequestAuthenticator.java | 6 +- .../RequestAuthenticatorImpl.java | 8 +- .../authentication/ResetPasswordFilter.java | 24 +-- .../SamlValidationCspHeaders.java | 3 +- .../SamlValidationRedirectionFilter.java | 28 ++- .../UserSessionInitializer.java | 14 +- .../event/AuthenticationEvent.java | 10 +- .../event/AuthenticationEventImpl.java | 14 +- .../usertoken/UserTokenAuthentication.java | 11 +- .../BaseContextFactoryTest.java | 36 ++-- .../BasicAuthenticationTest.java | 4 +- .../server/authentication/CookiesTest.java | 16 +- .../CredentialsAuthenticationTest.java | 4 +- ...CredentialsExternalAuthenticationTest.java | 36 ++-- ...ultAdminCredentialsVerifierFilterTest.java | 13 +- .../GithubWebhookAuthenticationTest.java | 22 +-- .../HttpHeadersAuthenticationTest.java | 52 ++--- .../server/authentication/InitFilterTest.java | 14 +- .../authentication/JwtCsrfVerifierTest.java | 12 +- .../authentication/JwtHttpHandlerTest.java | 18 +- .../LdapCredentialsAuthenticationTest.java | 4 +- ...Auth2AuthenticationParametersImplTest.java | 23 ++- .../OAuth2CallbackFilterTest.java | 14 +- .../OAuth2ContextFactoryTest.java | 66 ++++--- .../authentication/OAuthCsrfVerifierTest.java | 27 +-- .../RequestAuthenticatorImplTest.java | 8 +- .../ResetPasswordFilterTest.java | 13 +- .../SamlValidationCspHeadersTest.java | 4 +- .../SamlValidationRedirectionFilterTest.java | 34 ++-- .../UserSessionInitializerTest.java | 12 +- .../event/AuthenticationEventImplTest.java | 46 ++--- .../UserTokenAuthenticationTest.java | 4 +- .../plugins/PluginsRiskConsentFilter.java | 23 +-- .../plugins/PluginsRiskConsentFilterTest.java | 51 +++-- .../server/pushapi/DumbPushResponse.java | 11 +- .../sonar/server/pushapi/TestPushRequest.java | 3 +- .../authentication/ws/LoginActionIT.java | 40 ++-- .../user/ws/ChangePasswordActionIT.java | 25 +-- .../server/authentication/ws/LoginAction.java | 23 +-- .../authentication/ws/LogoutAction.java | 25 +-- .../authentication/ws/ValidateAction.java | 23 +-- .../server/saml/ws/ValidationAction.java | 35 ++-- .../server/saml/ws/ValidationInitAction.java | 21 +- .../server/user/ws/ChangePasswordAction.java | 34 ++-- .../authentication/ws/LogoutActionTest.java | 30 ++- .../authentication/ws/ValidateActionTest.java | 22 +-- .../server/saml/ws/ValidationActionTest.java | 29 +-- .../saml/ws/ValidationInitActionTest.java | 33 ++-- .../org/sonar/server/ws/ServletRequest.java | 6 +- .../org/sonar/server/ws/ServletResponse.java | 7 +- .../sonar/server/ws/ServletRequestTest.java | 12 +- .../sonar/server/ws/ServletResponseTest.java | 9 +- .../web/SonarLintConnectionFilterIT.java | 32 +-- .../platform/web/MasterServletFilter.java | 106 ++++++++-- .../platform/web/RegisterServletFilters.java | 22 ++- .../web/SonarLintConnectionFilter.java | 27 +-- .../platform/web/UserSessionFilter.java | 4 +- .../server/platform/web/WebServiceFilter.java | 26 +-- .../web/WebServiceReroutingFilter.java | 31 ++- .../platform/web/MasterServletFilterTest.java | 94 +++++---- .../web/RegisterServletFiltersTest.java | 10 +- .../platform/web/UserSessionFilterTest.java | 17 +- .../platform/web/WebServiceFilterTest.java | 26 +-- .../web/WebServiceReroutingFilterTest.java | 18 +- .../org/sonar/core/issue/DefaultIssue.java | 7 + .../issue/internal/DefaultExternalIssue.java | 13 ++ .../rule/internal/DefaultAdHocRule.java | 12 ++ 115 files changed, 1647 insertions(+), 921 deletions(-) create mode 100644 server/sonar-webserver-api/src/main/java/org/sonar/server/http/JavaxHttpRequest.java create mode 100644 server/sonar-webserver-api/src/main/java/org/sonar/server/http/JavaxHttpResponse.java create mode 100644 server/sonar-webserver-api/src/test/java/org/sonar/server/http/JavaxHttpRequestTest.java create mode 100644 server/sonar-webserver-api/src/test/java/org/sonar/server/http/JavaxHttpResponseTest.java diff --git a/gradle.properties b/gradle.properties index 06e22e2c3b5..d8fcfd18a4e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ group=org.sonarsource.sonarqube version=10.1 -pluginApiVersion=9.15.0.435 +pluginApiVersion=9.16.0.560 description=Open source platform for continuous inspection of code quality projectTitle=SonarQube org.gradle.jvmargs=-Xmx2048m diff --git a/server/sonar-auth-bitbucket/build.gradle b/server/sonar-auth-bitbucket/build.gradle index 445a0c2be16..8329723c6e2 100644 --- a/server/sonar-auth-bitbucket/build.gradle +++ b/server/sonar-auth-bitbucket/build.gradle @@ -22,4 +22,5 @@ dependencies { testImplementation 'junit:junit' testImplementation 'org.assertj:assertj-core' testImplementation 'org.mockito:mockito-core' + testImplementation project(path: ':server:sonar-webserver-api') } diff --git a/server/sonar-auth-bitbucket/src/main/java/org/sonar/auth/bitbucket/BitbucketIdentityProvider.java b/server/sonar-auth-bitbucket/src/main/java/org/sonar/auth/bitbucket/BitbucketIdentityProvider.java index ad0a29ebda1..58dd889d670 100755 --- a/server/sonar-auth-bitbucket/src/main/java/org/sonar/auth/bitbucket/BitbucketIdentityProvider.java +++ b/server/sonar-auth-bitbucket/src/main/java/org/sonar/auth/bitbucket/BitbucketIdentityProvider.java @@ -33,12 +33,12 @@ import java.util.List; import java.util.Set; import java.util.concurrent.ExecutionException; import javax.annotation.CheckForNull; -import javax.servlet.http.HttpServletRequest; import org.sonar.api.server.ServerSide; import org.sonar.api.server.authentication.Display; import org.sonar.api.server.authentication.OAuth2IdentityProvider; import org.sonar.api.server.authentication.UnauthorizedException; import org.sonar.api.server.authentication.UserIdentity; +import org.sonar.api.server.http.HttpRequest; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; @@ -122,7 +122,7 @@ public class BitbucketIdentityProvider implements OAuth2IdentityProvider { } private void onCallback(CallbackContext context) throws InterruptedException, ExecutionException, IOException { - HttpServletRequest request = context.getRequest(); + HttpRequest request = context.getHttpRequest(); OAuth20Service scribe = newScribeBuilder(context).build(scribeApi); String code = request.getParameter(OAuthConstants.CODE); OAuth2AccessToken accessToken = scribe.getAccessToken(code); diff --git a/server/sonar-auth-bitbucket/src/test/java/org/sonar/auth/bitbucket/IntegrationTest.java b/server/sonar-auth-bitbucket/src/test/java/org/sonar/auth/bitbucket/IntegrationTest.java index cce09beccf3..9b47ec7abc3 100644 --- a/server/sonar-auth-bitbucket/src/test/java/org/sonar/auth/bitbucket/IntegrationTest.java +++ b/server/sonar-auth-bitbucket/src/test/java/org/sonar/auth/bitbucket/IntegrationTest.java @@ -36,7 +36,10 @@ import org.sonar.api.config.internal.MapSettings; import org.sonar.api.server.authentication.OAuth2IdentityProvider; import org.sonar.api.server.authentication.UnauthorizedException; import org.sonar.api.server.authentication.UserIdentity; +import org.sonar.api.server.http.HttpRequest; +import org.sonar.api.server.http.HttpResponse; import org.sonar.api.utils.System2; +import org.sonar.server.http.JavaxHttpRequest; import static java.lang.String.format; import static java.net.URLEncoder.encode; @@ -253,14 +256,24 @@ public class IntegrationTest { return CALLBACK_URL; } + @Override + public HttpRequest getHttpRequest() { + return new JavaxHttpRequest(request); + } + + @Override + public HttpResponse getHttpResponse() { + throw new UnsupportedOperationException("not used"); + } + @Override public HttpServletRequest getRequest() { - return request; + throw new UnsupportedOperationException("deprecated"); } @Override public HttpServletResponse getResponse() { - throw new UnsupportedOperationException("not used"); + throw new UnsupportedOperationException("deprecated"); } } @@ -287,6 +300,16 @@ public class IntegrationTest { return CALLBACK_URL; } + @Override + public HttpRequest getHttpRequest() { + return null; + } + + @Override + public HttpResponse getHttpResponse() { + return null; + } + @Override public HttpServletRequest getRequest() { return null; diff --git a/server/sonar-auth-github/build.gradle b/server/sonar-auth-github/build.gradle index 811d44118a3..f78f4ba0b22 100644 --- a/server/sonar-auth-github/build.gradle +++ b/server/sonar-auth-github/build.gradle @@ -22,4 +22,5 @@ dependencies { testImplementation 'junit:junit' testImplementation 'org.assertj:assertj-core' testImplementation 'org.mockito:mockito-core' + testImplementation project(path: ':server:sonar-webserver-api') } diff --git a/server/sonar-auth-github/src/main/java/org/sonar/auth/github/GitHubIdentityProvider.java b/server/sonar-auth-github/src/main/java/org/sonar/auth/github/GitHubIdentityProvider.java index caf4ddc05db..3acf65ff2cd 100644 --- a/server/sonar-auth-github/src/main/java/org/sonar/auth/github/GitHubIdentityProvider.java +++ b/server/sonar-auth-github/src/main/java/org/sonar/auth/github/GitHubIdentityProvider.java @@ -24,11 +24,11 @@ import com.github.scribejava.core.model.OAuth2AccessToken; import com.github.scribejava.core.oauth.OAuth20Service; import java.io.IOException; import java.util.concurrent.ExecutionException; -import javax.servlet.http.HttpServletRequest; import org.sonar.api.server.authentication.Display; import org.sonar.api.server.authentication.OAuth2IdentityProvider; import org.sonar.api.server.authentication.UnauthorizedException; import org.sonar.api.server.authentication.UserIdentity; +import org.sonar.api.server.http.HttpRequest; import static com.google.common.base.Preconditions.checkState; import static java.lang.String.format; @@ -106,7 +106,7 @@ public class GitHubIdentityProvider implements OAuth2IdentityProvider { private void onCallback(CallbackContext context) throws InterruptedException, ExecutionException, IOException { context.verifyCsrfState(); - HttpServletRequest request = context.getRequest(); + HttpRequest request = context.getHttpRequest(); OAuth20Service scribe = newScribeBuilder(context).build(scribeApi); String code = request.getParameter("code"); OAuth2AccessToken accessToken = scribe.getAccessToken(code); diff --git a/server/sonar-auth-github/src/test/java/org/sonar/auth/github/IntegrationTest.java b/server/sonar-auth-github/src/test/java/org/sonar/auth/github/IntegrationTest.java index 390ba079e9e..3d8a1f0d8d3 100644 --- a/server/sonar-auth-github/src/test/java/org/sonar/auth/github/IntegrationTest.java +++ b/server/sonar-auth-github/src/test/java/org/sonar/auth/github/IntegrationTest.java @@ -37,7 +37,10 @@ import org.sonar.api.config.internal.MapSettings; import org.sonar.api.server.authentication.OAuth2IdentityProvider; import org.sonar.api.server.authentication.UnauthorizedException; import org.sonar.api.server.authentication.UserIdentity; +import org.sonar.api.server.http.HttpRequest; +import org.sonar.api.server.http.HttpResponse; import org.sonar.api.utils.System2; +import org.sonar.server.http.JavaxHttpRequest; import static java.lang.String.format; import static org.assertj.core.api.Assertions.assertThat; @@ -403,14 +406,24 @@ public class IntegrationTest { return CALLBACK_URL; } + @Override + public HttpRequest getHttpRequest() { + return new JavaxHttpRequest(request); + } + + @Override + public HttpResponse getHttpResponse() { + throw new UnsupportedOperationException("not used"); + } + @Override public HttpServletRequest getRequest() { - return request; + throw new UnsupportedOperationException("deprecated"); } @Override public HttpServletResponse getResponse() { - throw new UnsupportedOperationException("not used"); + throw new UnsupportedOperationException("deprecated"); } } @@ -437,6 +450,16 @@ public class IntegrationTest { return CALLBACK_URL; } + @Override + public HttpRequest getHttpRequest() { + return null; + } + + @Override + public HttpResponse getHttpResponse() { + return null; + } + @Override public HttpServletRequest getRequest() { return null; diff --git a/server/sonar-auth-gitlab/src/main/java/org/sonar/auth/gitlab/GitLabIdentityProvider.java b/server/sonar-auth-gitlab/src/main/java/org/sonar/auth/gitlab/GitLabIdentityProvider.java index a425e98f752..042c40678eb 100644 --- a/server/sonar-auth-gitlab/src/main/java/org/sonar/auth/gitlab/GitLabIdentityProvider.java +++ b/server/sonar-auth-gitlab/src/main/java/org/sonar/auth/gitlab/GitLabIdentityProvider.java @@ -30,10 +30,10 @@ import java.util.List; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.stream.Stream; -import javax.servlet.http.HttpServletRequest; import org.sonar.api.server.authentication.Display; import org.sonar.api.server.authentication.OAuth2IdentityProvider; import org.sonar.api.server.authentication.UserIdentity; +import org.sonar.api.server.http.HttpRequest; import static com.google.common.base.Preconditions.checkState; import static java.util.stream.Collectors.toSet; @@ -109,7 +109,7 @@ public class GitLabIdentityProvider implements OAuth2IdentityProvider { } private void onCallback(CallbackContext context) throws InterruptedException, ExecutionException, IOException { - HttpServletRequest request = context.getRequest(); + HttpRequest request = context.getHttpRequest(); OAuth20Service scribe = newScribeBuilder(context, gitLabSettings.syncUserGroups()).build(scribeApi); String code = request.getParameter(OAuthConstants.CODE); OAuth2AccessToken accessToken = scribe.getAccessToken(code); diff --git a/server/sonar-auth-gitlab/src/test/java/org/sonar/auth/gitlab/IntegrationTest.java b/server/sonar-auth-gitlab/src/test/java/org/sonar/auth/gitlab/IntegrationTest.java index 3f8d35e3f6f..dfeabc36e14 100644 --- a/server/sonar-auth-gitlab/src/test/java/org/sonar/auth/gitlab/IntegrationTest.java +++ b/server/sonar-auth-gitlab/src/test/java/org/sonar/auth/gitlab/IntegrationTest.java @@ -19,7 +19,6 @@ */ package org.sonar.auth.gitlab; -import javax.servlet.http.HttpServletRequest; import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; import org.assertj.core.api.Assertions; @@ -31,6 +30,7 @@ import org.mockito.Mockito; import org.sonar.api.config.internal.MapSettings; import org.sonar.api.server.authentication.OAuth2IdentityProvider; import org.sonar.api.server.authentication.UserIdentity; +import org.sonar.api.server.http.HttpRequest; import static java.lang.String.format; import static org.assertj.core.api.Assertions.assertThat; @@ -76,9 +76,9 @@ public class IntegrationTest { OAuth2IdentityProvider.CallbackContext callbackContext = Mockito.mock(OAuth2IdentityProvider.CallbackContext.class); when(callbackContext.getCallbackUrl()).thenReturn("http://server/callback"); - HttpServletRequest httpServletRequest = Mockito.mock(HttpServletRequest.class); - when(httpServletRequest.getParameter("code")).thenReturn(ANY_CODE_VALUE); - when(callbackContext.getRequest()).thenReturn(httpServletRequest); + HttpRequest httpRequest = Mockito.mock(HttpRequest.class); + when(httpRequest.getParameter("code")).thenReturn(ANY_CODE_VALUE); + when(callbackContext.getHttpRequest()).thenReturn(httpRequest); gitlab.enqueue(new MockResponse().setBody( "{\n" + " \"access_token\": \"de6780bc506a0446309bd9362820ba8aed28aa506c71eedbe1c5c4f9dd350e54\",\n" + " \"token_type\": \"bearer\",\n" + " \"expires_in\": 7200,\n" @@ -104,9 +104,9 @@ public class IntegrationTest { OAuth2IdentityProvider.CallbackContext callbackContext = Mockito.mock(OAuth2IdentityProvider.CallbackContext.class); when(callbackContext.getCallbackUrl()).thenReturn("http://server/callback"); - HttpServletRequest httpServletRequest = Mockito.mock(HttpServletRequest.class); - when(httpServletRequest.getParameter("code")).thenReturn(ANY_CODE_VALUE); - when(callbackContext.getRequest()).thenReturn(httpServletRequest); + HttpRequest httpRequest = Mockito.mock(HttpRequest.class); + when(httpRequest.getParameter("code")).thenReturn(ANY_CODE_VALUE); + when(callbackContext.getHttpRequest()).thenReturn(httpRequest); gitlab.enqueue(new MockResponse().setBody( "{\n" + " \"access_token\": \"de6780bc506a0446309bd9362820ba8aed28aa506c71eedbe1c5c4f9dd350e54\",\n" + " \"token_type\": \"bearer\",\n" + " \"expires_in\": 7200,\n" @@ -133,9 +133,9 @@ public class IntegrationTest { OAuth2IdentityProvider.CallbackContext callbackContext = Mockito.mock(OAuth2IdentityProvider.CallbackContext.class); when(callbackContext.getCallbackUrl()).thenReturn("http://server/callback"); - HttpServletRequest httpServletRequest = Mockito.mock(HttpServletRequest.class); - when(httpServletRequest.getParameter("code")).thenReturn(ANY_CODE_VALUE); - when(callbackContext.getRequest()).thenReturn(httpServletRequest); + HttpRequest httpRequest = Mockito.mock(HttpRequest.class); + when(httpRequest.getParameter("code")).thenReturn(ANY_CODE_VALUE); + when(callbackContext.getHttpRequest()).thenReturn(httpRequest); gitlab.enqueue(new MockResponse().setBody( "{\n" + " \"access_token\": \"de6780bc506a0446309bd9362820ba8aed28aa506c71eedbe1c5c4f9dd350e54\",\n" + " \"token_type\": \"bearer\",\n" + " \"expires_in\": 7200,\n" @@ -173,9 +173,9 @@ public class IntegrationTest { OAuth2IdentityProvider.CallbackContext callbackContext = Mockito.mock(OAuth2IdentityProvider.CallbackContext.class); when(callbackContext.getCallbackUrl()).thenReturn("http://server/callback"); - HttpServletRequest httpServletRequest = Mockito.mock(HttpServletRequest.class); - when(httpServletRequest.getParameter("code")).thenReturn(ANY_CODE_VALUE); - when(callbackContext.getRequest()).thenReturn(httpServletRequest); + HttpRequest httpRequest = Mockito.mock(HttpRequest.class); + when(httpRequest.getParameter("code")).thenReturn(ANY_CODE_VALUE); + when(callbackContext.getHttpRequest()).thenReturn(httpRequest); gitlab.enqueue(new MockResponse().setBody( "{\n" + " \"access_token\": \"de6780bc506a0446309bd9362820ba8aed28aa506c71eedbe1c5c4f9dd350e54\",\n" + " \"token_type\": \"bearer\",\n" + " \"expires_in\": 7200,\n" diff --git a/server/sonar-auth-ldap/src/it/java/org/sonar/auth/ldap/DefaultLdapAuthenticatorIT.java b/server/sonar-auth-ldap/src/it/java/org/sonar/auth/ldap/DefaultLdapAuthenticatorIT.java index 1f3ee94fc24..e9264d06275 100644 --- a/server/sonar-auth-ldap/src/it/java/org/sonar/auth/ldap/DefaultLdapAuthenticatorIT.java +++ b/server/sonar-auth-ldap/src/it/java/org/sonar/auth/ldap/DefaultLdapAuthenticatorIT.java @@ -19,9 +19,9 @@ */ package org.sonar.auth.ldap; -import javax.servlet.http.HttpServletRequest; import org.junit.ClassRule; import org.junit.Test; +import org.sonar.api.server.http.HttpRequest; import org.sonar.auth.ldap.server.LdapServer; import static org.assertj.core.api.Assertions.assertThat; @@ -163,7 +163,7 @@ public class DefaultLdapAuthenticatorIT { } private static LdapAuthenticator.Context createContext(String username, String password) { - return new LdapAuthenticator.Context(username, password, mock(HttpServletRequest.class)); + return new LdapAuthenticator.Context(username, password, mock(HttpRequest.class)); } } diff --git a/server/sonar-auth-ldap/src/it/java/org/sonar/auth/ldap/DefaultLdapGroupsProviderIT.java b/server/sonar-auth-ldap/src/it/java/org/sonar/auth/ldap/DefaultLdapGroupsProviderIT.java index 0534682719a..b505fd38972 100644 --- a/server/sonar-auth-ldap/src/it/java/org/sonar/auth/ldap/DefaultLdapGroupsProviderIT.java +++ b/server/sonar-auth-ldap/src/it/java/org/sonar/auth/ldap/DefaultLdapGroupsProviderIT.java @@ -20,10 +20,10 @@ package org.sonar.auth.ldap; import java.util.Collection; -import javax.servlet.http.HttpServletRequest; import org.junit.ClassRule; import org.junit.Test; import org.sonar.api.config.internal.MapSettings; +import org.sonar.api.server.http.HttpRequest; import org.sonar.auth.ldap.server.LdapServer; import static org.assertj.core.api.Assertions.assertThat; @@ -175,7 +175,7 @@ public class DefaultLdapGroupsProviderIT { } private static LdapGroupsProvider.Context createContext(String serverName, String userName) { - return new LdapGroupsProvider.Context(serverName, userName, mock(HttpServletRequest.class)); + return new LdapGroupsProvider.Context(serverName, userName, mock(HttpRequest.class)); } } diff --git a/server/sonar-auth-ldap/src/it/java/org/sonar/auth/ldap/DefaultLdapUsersProviderIT.java b/server/sonar-auth-ldap/src/it/java/org/sonar/auth/ldap/DefaultLdapUsersProviderIT.java index ea6894e7e80..91d0592b7c3 100644 --- a/server/sonar-auth-ldap/src/it/java/org/sonar/auth/ldap/DefaultLdapUsersProviderIT.java +++ b/server/sonar-auth-ldap/src/it/java/org/sonar/auth/ldap/DefaultLdapUsersProviderIT.java @@ -19,10 +19,10 @@ */ package org.sonar.auth.ldap; -import javax.servlet.http.HttpServletRequest; import org.junit.ClassRule; import org.junit.Test; import org.sonar.api.config.internal.MapSettings; +import org.sonar.api.server.http.HttpRequest; import org.sonar.auth.ldap.server.LdapServer; import static org.assertj.core.api.Assertions.assertThat; @@ -92,6 +92,6 @@ public class DefaultLdapUsersProviderIT { } private static LdapUsersProvider.Context createContext(String serverKey, String username) { - return new LdapUsersProvider.Context(serverKey, username, mock(HttpServletRequest.class)); + return new LdapUsersProvider.Context(serverKey, username, mock(HttpRequest.class)); } } diff --git a/server/sonar-auth-ldap/src/it/java/org/sonar/auth/ldap/KerberosIT.java b/server/sonar-auth-ldap/src/it/java/org/sonar/auth/ldap/KerberosIT.java index 08f1dfc5714..0f933d5e148 100644 --- a/server/sonar-auth-ldap/src/it/java/org/sonar/auth/ldap/KerberosIT.java +++ b/server/sonar-auth-ldap/src/it/java/org/sonar/auth/ldap/KerberosIT.java @@ -20,13 +20,13 @@ package org.sonar.auth.ldap; import java.io.File; -import javax.servlet.http.HttpServletRequest; import org.junit.Before; import org.junit.ClassRule; import org.junit.Test; import org.mockito.Mockito; import org.sonar.api.config.Configuration; import org.sonar.api.config.internal.MapSettings; +import org.sonar.api.server.http.HttpRequest; import org.sonar.auth.ldap.server.LdapServer; import static org.assertj.core.api.Assertions.assertThat; @@ -54,14 +54,14 @@ public class KerberosIT { @Test public void test_wrong_password() { - LdapAuthenticator.Context wrongPasswordContext = new LdapAuthenticator.Context("Godin@EXAMPLE.ORG", "wrong_user_password", Mockito.mock(HttpServletRequest.class)); + LdapAuthenticator.Context wrongPasswordContext = new LdapAuthenticator.Context("Godin@EXAMPLE.ORG", "wrong_user_password", Mockito.mock(HttpRequest.class)); assertThat(authenticator.doAuthenticate(wrongPasswordContext).isSuccess()).isFalse(); } @Test public void test_correct_password() { - LdapAuthenticator.Context correctPasswordContext = new LdapAuthenticator.Context("Godin@EXAMPLE.ORG", "user_password", Mockito.mock(HttpServletRequest.class)); + LdapAuthenticator.Context correctPasswordContext = new LdapAuthenticator.Context("Godin@EXAMPLE.ORG", "user_password", Mockito.mock(HttpRequest.class)); assertThat(authenticator.doAuthenticate(correctPasswordContext).isSuccess()).isTrue(); } @@ -70,14 +70,14 @@ public class KerberosIT { public void test_default_realm() { // Using default realm from krb5.conf: - LdapAuthenticator.Context defaultRealmContext = new LdapAuthenticator.Context("Godin", "user_password", Mockito.mock(HttpServletRequest.class)); + LdapAuthenticator.Context defaultRealmContext = new LdapAuthenticator.Context("Godin", "user_password", Mockito.mock(HttpRequest.class)); assertThat(authenticator.doAuthenticate(defaultRealmContext).isSuccess()).isTrue(); } @Test public void test_groups() { LdapGroupsProvider groupsProvider = ldapRealm.getGroupsProvider(); - LdapGroupsProvider.Context groupsContext = new LdapGroupsProvider.Context("default", "godin", Mockito.mock(HttpServletRequest.class)); + LdapGroupsProvider.Context groupsContext = new LdapGroupsProvider.Context("default", "godin", Mockito.mock(HttpRequest.class)); assertThat(groupsProvider.doGetGroups(groupsContext)) .containsOnly("sonar-users"); } diff --git a/server/sonar-auth-ldap/src/it/java/org/sonar/auth/ldap/LdapRealmIT.java b/server/sonar-auth-ldap/src/it/java/org/sonar/auth/ldap/LdapRealmIT.java index 4075ca0311d..318bfd701fc 100644 --- a/server/sonar-auth-ldap/src/it/java/org/sonar/auth/ldap/LdapRealmIT.java +++ b/server/sonar-auth-ldap/src/it/java/org/sonar/auth/ldap/LdapRealmIT.java @@ -19,12 +19,12 @@ */ package org.sonar.auth.ldap; -import javax.servlet.http.HttpServletRequest; import org.junit.ClassRule; import org.junit.Test; import org.mockito.Mockito; import org.sonar.api.config.Configuration; import org.sonar.api.config.internal.MapSettings; +import org.sonar.api.server.http.HttpRequest; import org.sonar.auth.ldap.server.LdapServer; import static org.assertj.core.api.Assertions.assertThat; @@ -117,12 +117,12 @@ public class LdapRealmIT { LdapGroupsProvider groupsProvider = realm.getGroupsProvider(); assertThat(groupsProvider).isInstanceOf(LdapGroupsProvider.class).isInstanceOf(DefaultLdapGroupsProvider.class); - LdapUsersProvider.Context userContext = new DefaultLdapUsersProvider.Context("", "tester", Mockito.mock(HttpServletRequest.class)); + LdapUsersProvider.Context userContext = new DefaultLdapUsersProvider.Context("", "tester", Mockito.mock(HttpRequest.class)); assertThatThrownBy(() -> usersProvider.doGetUserDetails(userContext)) .isInstanceOf(LdapException.class) .hasMessage("Unable to retrieve details for user tester and server key : No user mapping found."); - LdapGroupsProvider.Context groupsContext = new DefaultLdapGroupsProvider.Context("default", "tester", Mockito.mock(HttpServletRequest.class)); + LdapGroupsProvider.Context groupsContext = new DefaultLdapGroupsProvider.Context("default", "tester", Mockito.mock(HttpRequest.class)); assertThatThrownBy(() -> groupsProvider.doGetGroups(groupsContext)) .isInstanceOf(LdapException.class) .hasMessage("Unable to retrieve groups for user tester in server with key "); diff --git a/server/sonar-auth-ldap/src/main/java/org/sonar/auth/ldap/LdapAuthenticator.java b/server/sonar-auth-ldap/src/main/java/org/sonar/auth/ldap/LdapAuthenticator.java index 33f1912b4a2..dfb8a93ecbd 100644 --- a/server/sonar-auth-ldap/src/main/java/org/sonar/auth/ldap/LdapAuthenticator.java +++ b/server/sonar-auth-ldap/src/main/java/org/sonar/auth/ldap/LdapAuthenticator.java @@ -20,7 +20,7 @@ package org.sonar.auth.ldap; import javax.annotation.Nullable; -import javax.servlet.http.HttpServletRequest; +import org.sonar.api.server.http.HttpRequest; import static java.util.Objects.requireNonNull; @@ -35,9 +35,9 @@ public interface LdapAuthenticator { final class Context { private String username; private String password; - private HttpServletRequest request; + private HttpRequest request; - public Context(@Nullable String username, @Nullable String password, HttpServletRequest request) { + public Context(@Nullable String username, @Nullable String password, HttpRequest request) { requireNonNull(request); this.request = request; this.username = username; @@ -58,7 +58,7 @@ public interface LdapAuthenticator { return password; } - public HttpServletRequest getRequest() { + public HttpRequest getRequest() { return request; } } diff --git a/server/sonar-auth-ldap/src/main/java/org/sonar/auth/ldap/LdapGroupsProvider.java b/server/sonar-auth-ldap/src/main/java/org/sonar/auth/ldap/LdapGroupsProvider.java index 629ead08265..1cbacb929bd 100644 --- a/server/sonar-auth-ldap/src/main/java/org/sonar/auth/ldap/LdapGroupsProvider.java +++ b/server/sonar-auth-ldap/src/main/java/org/sonar/auth/ldap/LdapGroupsProvider.java @@ -20,12 +20,12 @@ package org.sonar.auth.ldap; import java.util.Collection; -import javax.servlet.http.HttpServletRequest; +import org.sonar.api.server.http.HttpRequest; public interface LdapGroupsProvider { Collection doGetGroups(Context context); - record Context(String serverKey, String username, HttpServletRequest request) { + record Context(String serverKey, String username, HttpRequest request) { } } diff --git a/server/sonar-auth-ldap/src/main/java/org/sonar/auth/ldap/LdapUsersProvider.java b/server/sonar-auth-ldap/src/main/java/org/sonar/auth/ldap/LdapUsersProvider.java index 7c3ba105863..73258e59a6d 100644 --- a/server/sonar-auth-ldap/src/main/java/org/sonar/auth/ldap/LdapUsersProvider.java +++ b/server/sonar-auth-ldap/src/main/java/org/sonar/auth/ldap/LdapUsersProvider.java @@ -19,13 +19,13 @@ */ package org.sonar.auth.ldap; -import javax.servlet.http.HttpServletRequest; +import org.sonar.api.server.http.HttpRequest; public interface LdapUsersProvider { LdapUserDetails doGetUserDetails(Context context); - record Context(String serverKey, String username, HttpServletRequest request) { + record Context(String serverKey, String username, HttpRequest request) { } } diff --git a/server/sonar-auth-saml/build.gradle b/server/sonar-auth-saml/build.gradle index db3de357a60..cafc01888bb 100644 --- a/server/sonar-auth-saml/build.gradle +++ b/server/sonar-auth-saml/build.gradle @@ -14,6 +14,7 @@ dependencies { compileOnlyApi 'javax.servlet:javax.servlet-api' compileOnlyApi 'org.json:json' compileOnlyApi project(':server:sonar-db-dao') + compileOnlyApi project(':server:sonar-webserver-api') compileOnlyApi project(':sonar-core') testImplementation 'com.tngtech.java:junit-dataprovider' diff --git a/server/sonar-auth-saml/src/it/java/org/sonar/auth/saml/SamlIdentityProviderIT.java b/server/sonar-auth-saml/src/it/java/org/sonar/auth/saml/SamlIdentityProviderIT.java index f67636785e3..ea83ab6ad49 100644 --- a/server/sonar-auth-saml/src/it/java/org/sonar/auth/saml/SamlIdentityProviderIT.java +++ b/server/sonar-auth-saml/src/it/java/org/sonar/auth/saml/SamlIdentityProviderIT.java @@ -37,10 +37,14 @@ import org.sonar.api.config.internal.MapSettings; import org.sonar.api.server.authentication.OAuth2IdentityProvider; import org.sonar.api.server.authentication.UnauthorizedException; import org.sonar.api.server.authentication.UserIdentity; -import org.sonar.api.utils.System2; +import org.sonar.api.server.http.HttpRequest; +import org.sonar.api.server.http.HttpResponse; import org.sonar.api.testfixtures.log.LogAndArguments; import org.sonar.api.testfixtures.log.LogTester; +import org.sonar.api.utils.System2; import org.sonar.db.DbTester; +import org.sonar.server.http.JavaxHttpRequest; +import org.sonar.server.http.JavaxHttpResponse; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -366,14 +370,24 @@ public class SamlIdentityProviderIT { return SQ_CALLBACK_URL; } + @Override + public HttpRequest getHttpRequest() { + return new JavaxHttpRequest(mock(HttpServletRequest.class)); + } + + @Override + public HttpResponse getHttpResponse() { + return new JavaxHttpResponse(response); + } + @Override public HttpServletRequest getRequest() { - return mock(HttpServletRequest.class); + throw new UnsupportedOperationException("deprecated"); } @Override public HttpServletResponse getResponse() { - return response; + throw new UnsupportedOperationException("deprecated"); } } @@ -392,7 +406,7 @@ public class SamlIdentityProviderIT { this.expectedCallbackUrl = expectedCallbackUrl; Map parameterMap = new HashMap<>(); parameterMap.put("SAMLResponse", new String[]{loadResponse(encodedResponseFile)}); - when(getRequest().getParameterMap()).thenReturn(parameterMap); + when(((JavaxHttpRequest) getHttpRequest()).getDelegate().getParameterMap()).thenReturn(parameterMap); } @@ -430,14 +444,24 @@ public class SamlIdentityProviderIT { return this.expectedCallbackUrl; } + @Override + public HttpRequest getHttpRequest() { + return new JavaxHttpRequest(request); + } + + @Override + public HttpResponse getHttpResponse() { + return new JavaxHttpResponse(response); + } + @Override public HttpServletRequest getRequest() { - return this.request; + return null; } @Override public HttpServletResponse getResponse() { - return this.response; + return null; } } } diff --git a/server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/SamlAuthStatusPageGenerator.java b/server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/SamlAuthStatusPageGenerator.java index 5c774e2d501..15dcca746e6 100644 --- a/server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/SamlAuthStatusPageGenerator.java +++ b/server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/SamlAuthStatusPageGenerator.java @@ -25,8 +25,8 @@ import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.Base64; import java.util.Map; -import javax.servlet.http.HttpServletRequest; import org.json.JSONObject; +import org.sonar.api.server.http.HttpRequest; public final class SamlAuthStatusPageGenerator { @@ -38,7 +38,7 @@ public final class SamlAuthStatusPageGenerator { throw new IllegalStateException("This Utility class cannot be instantiated"); } - public static String getSamlAuthStatusHtml(HttpServletRequest request, SamlAuthenticationStatus samlAuthenticationStatus) { + public static String getSamlAuthStatusHtml(HttpRequest request, SamlAuthenticationStatus samlAuthenticationStatus) { Map substitutionsMap = getSubstitutionsMap(request, samlAuthenticationStatus); String htmlTemplate = getPlainTemplate(); @@ -48,7 +48,7 @@ public final class SamlAuthStatusPageGenerator { .reduce(htmlTemplate, (accumulator, pattern) -> accumulator.replace(pattern, substitutionsMap.get(pattern))); } - private static Map getSubstitutionsMap(HttpServletRequest request, SamlAuthenticationStatus samlAuthenticationStatus) { + private static Map getSubstitutionsMap(HttpRequest request, SamlAuthenticationStatus samlAuthenticationStatus) { return Map.of( WEB_CONTEXT, request.getContextPath(), SAML_AUTHENTICATION_STATUS, getBase64EncodedStatus(samlAuthenticationStatus)); diff --git a/server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/SamlAuthenticator.java b/server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/SamlAuthenticator.java index 42e0697fe1c..13d2200bc71 100644 --- a/server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/SamlAuthenticator.java +++ b/server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/SamlAuthenticator.java @@ -38,8 +38,12 @@ import javax.servlet.http.HttpServletResponse; import org.sonar.api.server.authentication.OAuth2IdentityProvider; import org.sonar.api.server.authentication.UnauthorizedException; import org.sonar.api.server.authentication.UserIdentity; +import org.sonar.api.server.http.HttpRequest; +import org.sonar.api.server.http.HttpResponse; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; +import org.sonar.server.http.JavaxHttpRequest; +import org.sonar.server.http.JavaxHttpResponse; import static java.util.Collections.emptySet; import static java.util.Objects.requireNonNull; @@ -62,8 +66,8 @@ public class SamlAuthenticator { this.samlMessageIdChecker = samlMessageIdChecker; } - public UserIdentity buildUserIdentity(OAuth2IdentityProvider.CallbackContext context, HttpServletRequest processedRequest) { - Auth auth = this.initSamlAuth(processedRequest, context.getResponse()); + public UserIdentity buildUserIdentity(OAuth2IdentityProvider.CallbackContext context, HttpRequest processedRequest) { + Auth auth = this.initSamlAuth(processedRequest, context.getHttpResponse()); processResponse(auth); context.verifyCsrfState(STATE_REQUEST_PARAMETER); @@ -83,18 +87,22 @@ public class SamlAuthenticator { return userIdentityBuilder.build(); } - public void initLogin(String callbackUrl, String relayState, HttpServletRequest request, HttpServletResponse response) { + public void initLogin(String callbackUrl, String relayState, HttpRequest request, HttpResponse response) { Auth auth = this.initSamlAuth(callbackUrl, request, response); login(auth, relayState); } - private Auth initSamlAuth(HttpServletRequest request, HttpServletResponse response) { + private Auth initSamlAuth(HttpRequest request, HttpResponse response) { return initSamlAuth(null, request, response); } - private Auth initSamlAuth(@Nullable String callbackUrl, HttpServletRequest request, HttpServletResponse response) { + private Auth initSamlAuth(@Nullable String callbackUrl, HttpRequest request, HttpResponse response) { try { - return new Auth(initSettings(callbackUrl), request, response); + //no way around this as onelogin requires javax request/response + HttpServletRequest httpServletRequest = ((JavaxHttpRequest) request).getDelegate(); + HttpServletResponse httpServletResponse = ((JavaxHttpResponse) response).getDelegate(); + + return new Auth(initSettings(callbackUrl), httpServletRequest, httpServletResponse); } catch (SettingsException e) { throw new IllegalStateException("Failed to create a SAML Auth", e); } @@ -208,7 +216,7 @@ public class SamlAuthenticator { samlMessageIdChecker.check(auth); } - public String getAuthenticationStatusPage(HttpServletRequest request, HttpServletResponse response) { + public String getAuthenticationStatusPage(HttpRequest request, HttpResponse response) { try { Auth auth = initSamlAuth(request, response); return getSamlAuthStatusHtml(request, getSamlAuthenticationStatus(auth, samlSettings)); @@ -217,4 +225,3 @@ public class SamlAuthenticator { } } } - diff --git a/server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/SamlIdentityProvider.java b/server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/SamlIdentityProvider.java index d5784328c5d..e38b8e8fc86 100644 --- a/server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/SamlIdentityProvider.java +++ b/server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/SamlIdentityProvider.java @@ -26,6 +26,8 @@ import org.sonar.api.server.ServerSide; import org.sonar.api.server.authentication.Display; import org.sonar.api.server.authentication.OAuth2IdentityProvider; import org.sonar.api.server.authentication.UserIdentity; +import org.sonar.api.server.http.HttpRequest; +import org.sonar.server.http.JavaxHttpRequest; @ServerSide public class SamlIdentityProvider implements OAuth2IdentityProvider { @@ -74,7 +76,8 @@ public class SamlIdentityProvider implements OAuth2IdentityProvider { @Override public void init(InitContext context) { SamlAuthenticator samlAuthenticator = new SamlAuthenticator(samlSettings, samlMessageIdChecker); - samlAuthenticator.initLogin(context.getCallbackUrl(), context.generateCsrfState(), context.getRequest(), context.getResponse()); + samlAuthenticator.initLogin(context.getCallbackUrl(), context.generateCsrfState(), + context.getHttpRequest(), context.getHttpResponse()); } @Override @@ -86,7 +89,7 @@ public class SamlIdentityProvider implements OAuth2IdentityProvider { // - https://github.com/onelogin/java-saml/issues/198 // - https://github.com/onelogin/java-saml/issues/95 // - HttpServletRequest processedRequest = useProxyHeadersInRequest(context.getRequest()); + HttpRequest processedRequest = useProxyHeadersInRequest(context.getHttpRequest()); SamlAuthenticator samlAuthenticator = new SamlAuthenticator(samlSettings, samlMessageIdChecker); UserIdentity userIdentity = samlAuthenticator.buildUserIdentity(context, processedRequest); @@ -95,10 +98,10 @@ public class SamlIdentityProvider implements OAuth2IdentityProvider { } - private static HttpServletRequest useProxyHeadersInRequest(HttpServletRequest request) { + private static HttpRequest useProxyHeadersInRequest(HttpRequest request) { String forwardedScheme = request.getHeader("X-Forwarded-Proto"); if (forwardedScheme != null) { - request = new HttpServletRequestWrapper(request) { + HttpServletRequest httpServletRequest = new HttpServletRequestWrapper(((JavaxHttpRequest) request).getDelegate()) { @Override public String getScheme() { return forwardedScheme; @@ -110,6 +113,7 @@ public class SamlIdentityProvider implements OAuth2IdentityProvider { return new StringBuffer(HTTPS_PATTERN.matcher(originalURL.toString()).replaceFirst(forwardedScheme + "://")); } }; + return new JavaxHttpRequest(httpServletRequest); } return request; diff --git a/server/sonar-auth-saml/src/test/java/org/sonar/auth/saml/SamlAuthStatusPageGeneratorTest.java b/server/sonar-auth-saml/src/test/java/org/sonar/auth/saml/SamlAuthStatusPageGeneratorTest.java index 9fd398321f2..2f84a89a6d7 100644 --- a/server/sonar-auth-saml/src/test/java/org/sonar/auth/saml/SamlAuthStatusPageGeneratorTest.java +++ b/server/sonar-auth-saml/src/test/java/org/sonar/auth/saml/SamlAuthStatusPageGeneratorTest.java @@ -21,8 +21,8 @@ package org.sonar.auth.saml; import java.util.ArrayList; import java.util.HashMap; -import javax.servlet.http.HttpServletRequest; import org.junit.Test; +import org.sonar.api.server.http.HttpRequest; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; @@ -35,16 +35,16 @@ public class SamlAuthStatusPageGeneratorTest { @Test public void getSamlAuthStatusHtml_whenCalled_shouldGeneratePageWithData() { SamlAuthenticationStatus samlAuthenticationStatus = mock(SamlAuthenticationStatus.class); - HttpServletRequest httpServletRequest = mock(HttpServletRequest.class); + HttpRequest request = mock(HttpRequest.class); when(samlAuthenticationStatus.getStatus()).thenReturn(null); when(samlAuthenticationStatus.getErrors()).thenReturn(new ArrayList<>()); when(samlAuthenticationStatus.getWarnings()).thenReturn(new ArrayList<>()); when(samlAuthenticationStatus.getAvailableAttributes()).thenReturn(new HashMap<>()); when(samlAuthenticationStatus.getMappedAttributes()).thenReturn(new HashMap<>()); - when(httpServletRequest.getContextPath()).thenReturn("context"); + when(request.getContextPath()).thenReturn("context"); - String completeHtmlTemplate = getSamlAuthStatusHtml(httpServletRequest, samlAuthenticationStatus); + String completeHtmlTemplate = getSamlAuthStatusHtml(request, samlAuthenticationStatus); assertThat(completeHtmlTemplate).contains(EMPTY_DATA_RESPONSE); } diff --git a/server/sonar-auth-saml/src/test/java/org/sonar/auth/saml/SamlAuthenticatorTest.java b/server/sonar-auth-saml/src/test/java/org/sonar/auth/saml/SamlAuthenticatorTest.java index 1c473879e3d..fb6e9f2cdeb 100644 --- a/server/sonar-auth-saml/src/test/java/org/sonar/auth/saml/SamlAuthenticatorTest.java +++ b/server/sonar-auth-saml/src/test/java/org/sonar/auth/saml/SamlAuthenticatorTest.java @@ -22,8 +22,12 @@ package org.sonar.auth.saml; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.junit.Test; +import org.sonar.api.server.http.HttpRequest; +import org.sonar.api.server.http.HttpResponse; +import org.sonar.server.http.JavaxHttpRequest; +import org.sonar.server.http.JavaxHttpResponse; -import static org.junit.Assert.*; +import static org.junit.Assert.assertFalse; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -32,8 +36,8 @@ public class SamlAuthenticatorTest { @Test public void authentication_status_with_errors_returned_when_init_fails() { SamlAuthenticator samlAuthenticator = new SamlAuthenticator(mock(SamlSettings.class), mock(SamlMessageIdChecker.class)); - HttpServletRequest request = mock(HttpServletRequest.class); - HttpServletResponse response = mock(HttpServletResponse.class); + HttpRequest request = new JavaxHttpRequest(mock(HttpServletRequest.class)); + HttpResponse response = new JavaxHttpResponse(mock(HttpServletResponse.class)); when(request.getContextPath()).thenReturn("context"); String authenticationStatus = samlAuthenticator.getAuthenticationStatusPage(request, response); diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/http/JavaxHttpRequest.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/http/JavaxHttpRequest.java new file mode 100644 index 00000000000..08332a24c0f --- /dev/null +++ b/server/sonar-webserver-api/src/main/java/org/sonar/server/http/JavaxHttpRequest.java @@ -0,0 +1,183 @@ +/* + * SonarQube + * Copyright (C) 2009-2023 SonarSource SA + * mailto:info 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.http; + +import java.io.BufferedReader; +import java.io.IOException; +import java.util.Arrays; +import java.util.Enumeration; +import javax.servlet.http.HttpServletRequest; +import org.sonar.api.server.http.Cookie; +import org.sonar.api.server.http.HttpRequest; + +/** + * Implementation of {@link HttpRequest} based on a delegate of {@link HttpServletRequest} from the Javax Servlet API. + */ +public class JavaxHttpRequest implements HttpRequest { + + private final HttpServletRequest delegate; + + public JavaxHttpRequest(HttpServletRequest delegate) { + this.delegate = delegate; + } + + public HttpServletRequest getDelegate() { + return delegate; + } + + @Override + public int getServerPort() { + return delegate.getServerPort(); + } + + @Override + public boolean isSecure() { + return delegate.isSecure(); + } + + @Override + public String getScheme() { + return delegate.getScheme(); + } + + @Override + public String getServerName() { + return delegate.getServerName(); + } + + @Override + public String getRequestURL() { + return delegate.getRequestURL().toString(); + } + + @Override + public String getRequestURI() { + return delegate.getRequestURI(); + } + + @Override + public String getQueryString() { + return delegate.getQueryString(); + } + + @Override + public String getContextPath() { + return delegate.getContextPath(); + } + + @Override + public String getParameter(String name) { + return delegate.getParameter(name); + } + + @Override + public String[] getParameterValues(String name) { + return delegate.getParameterValues(name); + } + + @Override + public String getHeader(String name) { + return delegate.getHeader(name); + } + + @Override + public Enumeration getHeaderNames() { + return delegate.getHeaderNames(); + } + + @Override + public Enumeration getHeaders(String name) { + return delegate.getHeaders(name); + } + + @Override + public String getMethod() { + return delegate.getMethod(); + } + + @Override + public String getRemoteAddr() { + return delegate.getRemoteAddr(); + } + + @Override + public void setAttribute(String name, Object value) { + delegate.setAttribute(name, value); + } + + @Override + public String getServletPath() { + return delegate.getServletPath(); + } + + @Override + public BufferedReader getReader() throws IOException { + return delegate.getReader(); + } + + @Override + public Cookie[] getCookies() { + javax.servlet.http.Cookie[] cookies = delegate.getCookies(); + if (cookies != null) { + return Arrays.stream(cookies) + .map(JavaxCookie::new) + .toArray(Cookie[]::new); + } + return new Cookie[0]; + } + + public static class JavaxCookie implements Cookie { + private final javax.servlet.http.Cookie delegate; + + public JavaxCookie(javax.servlet.http.Cookie delegate) { + this.delegate = delegate; + } + + @Override + public String getName() { + return delegate.getName(); + } + + @Override + public String getValue() { + return delegate.getValue(); + } + + @Override + public String getPath() { + return delegate.getPath(); + } + + @Override + public boolean isSecure() { + return delegate.getSecure(); + } + + @Override + public boolean isHttpOnly() { + return delegate.isHttpOnly(); + } + + @Override + public int getMaxAge() { + return delegate.getMaxAge(); + } + } +} diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/http/JavaxHttpResponse.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/http/JavaxHttpResponse.java new file mode 100644 index 00000000000..37f7cf9c977 --- /dev/null +++ b/server/sonar-webserver-api/src/main/java/org/sonar/server/http/JavaxHttpResponse.java @@ -0,0 +1,109 @@ +/* + * SonarQube + * Copyright (C) 2009-2023 SonarSource SA + * mailto:info 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.http; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.util.Collection; +import javax.servlet.http.HttpServletResponse; +import org.sonar.api.server.http.Cookie; +import org.sonar.api.server.http.HttpResponse; + +/** + * Implementation of {@link HttpResponse} based on a delegate of {@link HttpServletResponse} from the Javax Servlet API. + */ +public class JavaxHttpResponse implements HttpResponse { + + private final HttpServletResponse delegate; + + public JavaxHttpResponse(HttpServletResponse delegate) { + this.delegate = delegate; + } + + public HttpServletResponse getDelegate() { + return delegate; + } + + @Override + public void addHeader(String name, String value) { + delegate.addHeader(name, value); + } + + @Override + public String getHeader(String name) { + return delegate.getHeader(name); + } + + @Override + public Collection getHeaders(String name) { + return delegate.getHeaders(name); + } + + @Override + public void setStatus(int status) { + delegate.setStatus(status); + } + + @Override + public int getStatus() { + return delegate.getStatus(); + } + + @Override + public void setContentType(String contentType) { + delegate.setContentType(contentType); + } + + @Override + public PrintWriter getWriter() throws IOException { + return delegate.getWriter(); + } + + @Override + public void setHeader(String name, String value) { + delegate.setHeader(name, value); + } + + @Override + public void sendRedirect(String location) throws IOException { + delegate.sendRedirect(location); + } + + @Override + public void addCookie(Cookie cookie) { + javax.servlet.http.Cookie javaxCookie = new javax.servlet.http.Cookie(cookie.getName(), cookie.getValue()); + javaxCookie.setPath(cookie.getPath()); + javaxCookie.setSecure(cookie.isSecure()); + javaxCookie.setHttpOnly(cookie.isHttpOnly()); + javaxCookie.setMaxAge(cookie.getMaxAge()); + delegate.addCookie(javaxCookie); + } + + @Override + public OutputStream getOutputStream() throws IOException { + return delegate.getOutputStream(); + } + + @Override + public void setCharacterEncoding(String charset) { + delegate.setCharacterEncoding(charset); + } +} diff --git a/server/sonar-webserver-api/src/test/java/org/sonar/server/http/JavaxHttpRequestTest.java b/server/sonar-webserver-api/src/test/java/org/sonar/server/http/JavaxHttpRequestTest.java new file mode 100644 index 00000000000..1415dd43a71 --- /dev/null +++ b/server/sonar-webserver-api/src/test/java/org/sonar/server/http/JavaxHttpRequestTest.java @@ -0,0 +1,106 @@ +/* + * SonarQube + * Copyright (C) 2009-2023 SonarSource SA + * mailto:info 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.http; + +import java.io.BufferedReader; +import java.io.IOException; +import java.util.Collections; +import java.util.Enumeration; +import javax.servlet.http.HttpServletRequest; +import org.junit.Test; +import org.sonar.api.server.http.Cookie; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class JavaxHttpRequestTest { + + @Test + public void delegate_methods() throws IOException { + HttpServletRequest requestMock = mock(HttpServletRequest.class); + Enumeration enumeration = Collections.enumeration(Collections.emptySet()); + when(requestMock.getHeaderNames()).thenReturn(enumeration); + when(requestMock.getRemoteAddr()).thenReturn("192.168.0.1"); + when(requestMock.getServletPath()).thenReturn("/servlet-path"); + BufferedReader bufferedReader = mock(BufferedReader.class); + when(requestMock.getReader()).thenReturn(bufferedReader); + javax.servlet.http.Cookie[] cookies = new javax.servlet.http.Cookie[0]; + when(requestMock.getCookies()).thenReturn(cookies); + when(requestMock.getServerPort()).thenReturn(80); + when(requestMock.isSecure()).thenReturn(true); + when(requestMock.getScheme()).thenReturn("https"); + when(requestMock.getServerName()).thenReturn("hostname"); + when(requestMock.getRequestURL()).thenReturn(new StringBuffer("https://hostname:80/path")); + when(requestMock.getRequestURI()).thenReturn("/path"); + when(requestMock.getQueryString()).thenReturn("param1=value1"); + when(requestMock.getContextPath()).thenReturn("/path"); + when(requestMock.getMethod()).thenReturn("POST"); + when(requestMock.getParameter("param1")).thenReturn("value1"); + when(requestMock.getParameterValues("param1")).thenReturn(new String[] {"value1"}); + when(requestMock.getHeader("header1")).thenReturn("hvalue1"); + Enumeration headers = mock(Enumeration.class); + when(requestMock.getHeaders("header1")).thenReturn(headers); + + JavaxHttpRequest underTest = new JavaxHttpRequest(requestMock); + + assertThat(underTest.getDelegate()).isSameAs(requestMock); + assertThat(underTest.getServerPort()).isEqualTo(80); + assertThat(underTest.isSecure()).isTrue(); + assertThat(underTest.getScheme()).isEqualTo("https"); + assertThat(underTest.getServerName()).isEqualTo("hostname"); + assertThat(underTest.getRequestURL()).isEqualTo("https://hostname:80/path"); + assertThat(underTest.getRequestURI()).isEqualTo("/path"); + assertThat(underTest.getQueryString()).isEqualTo("param1=value1"); + assertThat(underTest.getContextPath()).isEqualTo("/path"); + assertThat(underTest.getMethod()).isEqualTo("POST"); + assertThat(underTest.getParameter("param1")).isEqualTo("value1"); + assertThat(underTest.getParameterValues("param1")).containsExactly("value1"); + assertThat(underTest.getHeader("header1")).isEqualTo("hvalue1"); + assertThat(underTest.getHeaders("header1")).isEqualTo(headers); + assertThat(underTest.getHeaderNames()).isEqualTo(enumeration); + assertThat(underTest.getRemoteAddr()).isEqualTo("192.168.0.1"); + assertThat(underTest.getServletPath()).isEqualTo("/servlet-path"); + assertThat(underTest.getReader()).isEqualTo(bufferedReader); + assertThat(underTest.getCookies()).isEqualTo(cookies); + + underTest.setAttribute("name", "value"); + verify(requestMock).setAttribute("name", "value"); + } + + @Test + public void delegate_methods_for_cookie() { + javax.servlet.http.Cookie mockCookie = new javax.servlet.http.Cookie("name", "value"); + mockCookie.setSecure(true); + mockCookie.setPath("path"); + mockCookie.setHttpOnly(true); + mockCookie.setMaxAge(100); + + Cookie cookie = new JavaxHttpRequest.JavaxCookie(mockCookie); + assertThat(cookie.getName()).isEqualTo("name"); + assertThat(cookie.getValue()).isEqualTo("value"); + assertThat(cookie.getPath()).isEqualTo("path"); + assertThat(cookie.isSecure()).isTrue(); + assertThat(cookie.isHttpOnly()).isTrue(); + assertThat(cookie.getMaxAge()).isEqualTo(100); + } + +} diff --git a/server/sonar-webserver-api/src/test/java/org/sonar/server/http/JavaxHttpResponseTest.java b/server/sonar-webserver-api/src/test/java/org/sonar/server/http/JavaxHttpResponseTest.java new file mode 100644 index 00000000000..8c1ea1717b0 --- /dev/null +++ b/server/sonar-webserver-api/src/test/java/org/sonar/server/http/JavaxHttpResponseTest.java @@ -0,0 +1,77 @@ +/* + * SonarQube + * Copyright (C) 2009-2023 SonarSource SA + * mailto:info 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.http; + +import java.io.IOException; +import java.io.PrintWriter; +import java.util.List; +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletResponse; +import org.junit.Test; +import org.sonar.api.server.http.Cookie; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class JavaxHttpResponseTest { + + @Test + public void delegate_methods() throws IOException { + HttpServletResponse responseMock = mock(HttpServletResponse.class); + when(responseMock.getHeader("h1")).thenReturn("hvalue1"); + when(responseMock.getHeaders("h1")).thenReturn(List.of("hvalue1")); + when(responseMock.getStatus()).thenReturn(200); + ServletOutputStream outputStream = mock(ServletOutputStream.class); + when(responseMock.getOutputStream()).thenReturn(outputStream); + PrintWriter writer = mock(PrintWriter.class); + when(responseMock.getWriter()).thenReturn(writer); + + JavaxHttpResponse underTest = new JavaxHttpResponse(responseMock); + + assertThat(underTest.getDelegate()).isSameAs(responseMock); + assertThat(underTest.getHeader("h1")).isEqualTo("hvalue1"); + assertThat(underTest.getHeaders("h1")).asList().containsExactly("hvalue1"); + assertThat(underTest.getStatus()).isEqualTo(200); + assertThat(underTest.getWriter()).isEqualTo(writer); + assertThat(underTest.getOutputStream()).isEqualTo(outputStream); + + underTest.addHeader("h2", "hvalue2"); + underTest.setHeader("h3", "hvalue3"); + underTest.setStatus(201); + underTest.setContentType("text/plain"); + underTest.sendRedirect("http://redirect"); + underTest.setCharacterEncoding("UTF-8"); + + Cookie cookie = mock(Cookie.class); + when(cookie.getName()).thenReturn("name"); + when(cookie.getValue()).thenReturn("value"); + underTest.addCookie(cookie); + verify(responseMock).addHeader("h2", "hvalue2"); + verify(responseMock).setHeader("h3", "hvalue3"); + verify(responseMock).setStatus(201); + verify(responseMock).setContentType("text/plain"); + verify(responseMock).sendRedirect("http://redirect"); + verify(responseMock).setCharacterEncoding("UTF-8"); + verify(responseMock).addCookie(any(javax.servlet.http.Cookie.class)); + } +} diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/AuthenticationError.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/AuthenticationError.java index 5f0e8354b7e..7a0aaa9eae9 100644 --- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/AuthenticationError.java +++ b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/AuthenticationError.java @@ -19,8 +19,8 @@ */ package org.sonar.server.authentication; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import org.sonar.api.server.http.HttpRequest; +import org.sonar.api.server.http.HttpResponse; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; import org.sonar.server.authentication.event.AuthenticationException; @@ -41,17 +41,17 @@ public final class AuthenticationError { // Utility class } - static void handleError(Exception e, HttpServletRequest request, HttpServletResponse response, String message) { + static void handleError(Exception e, HttpRequest request, HttpResponse response, String message) { LOGGER.warn(message, e); redirectToUnauthorized(request, response); } - public static void handleError(HttpServletRequest request, HttpServletResponse response, String message) { + public static void handleError(HttpRequest request, HttpResponse response, String message) { LOGGER.warn(message); redirectToUnauthorized(request, response); } - static void handleAuthenticationError(AuthenticationException e, HttpServletRequest request, HttpServletResponse response) { + static void handleAuthenticationError(AuthenticationException e, HttpRequest request, HttpResponse response) { String publicMessage = e.getPublicMessage(); if (publicMessage != null && !publicMessage.isEmpty()) { addErrorCookie(request, response, publicMessage); @@ -59,7 +59,7 @@ public final class AuthenticationError { redirectToUnauthorized(request, response); } - public static void addErrorCookie(HttpServletRequest request, HttpServletResponse response, String value) { + public static void addErrorCookie(HttpRequest request, HttpResponse response, String value) { response.addCookie(newCookieBuilder(request) .setName(AUTHENTICATION_ERROR_COOKIE) .setValue(encodeMessage(value)) @@ -68,7 +68,7 @@ public final class AuthenticationError { .build()); } - private static void redirectToUnauthorized(HttpServletRequest request, HttpServletResponse response) { + private static void redirectToUnauthorized(HttpRequest request, HttpResponse response) { redirectTo(response, request.getContextPath() + UNAUTHORIZED_PATH); } } diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/AuthenticationFilter.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/AuthenticationFilter.java index b88fd919602..8fb7f6e7216 100644 --- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/AuthenticationFilter.java +++ b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/AuthenticationFilter.java @@ -20,21 +20,21 @@ package org.sonar.server.authentication; import javax.annotation.CheckForNull; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import org.sonar.api.server.authentication.IdentityProvider; -import org.sonar.api.web.ServletFilter; +import org.sonar.api.server.http.HttpRequest; +import org.sonar.api.server.http.HttpResponse; +import org.sonar.api.web.HttpFilter; import static com.google.common.base.Strings.isNullOrEmpty; import static java.lang.String.format; import static org.sonar.server.authentication.AuthenticationError.handleError; -public abstract class AuthenticationFilter extends ServletFilter { +public abstract class AuthenticationFilter extends HttpFilter { static final String CALLBACK_PATH = "/oauth2/callback/"; private final IdentityProviderRepository identityProviderRepository; - public AuthenticationFilter(IdentityProviderRepository identityProviderRepository) { + protected AuthenticationFilter(IdentityProviderRepository identityProviderRepository) { this.identityProviderRepository = identityProviderRepository; } @@ -43,7 +43,7 @@ public abstract class AuthenticationFilter extends ServletFilter { * case the request is fully handled and caller should not handle it */ @CheckForNull - IdentityProvider resolveProviderOrHandleResponse(HttpServletRequest request, HttpServletResponse response, String path) { + IdentityProvider resolveProviderOrHandleResponse(HttpRequest request, HttpResponse response, String path) { String requestUri = request.getRequestURI(); String providerKey = extractKeyProvider(requestUri, request.getContextPath() + path); if (providerKey == null) { diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/AuthenticationRedirection.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/AuthenticationRedirection.java index 1512ee87fb6..c257c9f4975 100644 --- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/AuthenticationRedirection.java +++ b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/AuthenticationRedirection.java @@ -22,6 +22,7 @@ package org.sonar.server.authentication; import java.io.IOException; import java.io.UnsupportedEncodingException; import javax.servlet.http.HttpServletResponse; +import org.sonar.api.server.http.HttpResponse; import static java.lang.String.format; import static java.net.URLEncoder.encode; @@ -43,7 +44,15 @@ public class AuthenticationRedirection { } } - public static void redirectTo(HttpServletResponse response, String url) { + public static void redirectTo(HttpResponse response, String url) { + try { + response.sendRedirect(url); + } catch (IOException e) { + throw new IllegalStateException(format("Fail to redirect to %s", url), e); + } + } + + static void redirectTo(HttpServletResponse response, String url) { try { response.sendRedirect(url); } catch (IOException e) { diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/BaseContextFactory.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/BaseContextFactory.java index d6c0b366b65..890c110e0e0 100644 --- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/BaseContextFactory.java +++ b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/BaseContextFactory.java @@ -24,8 +24,12 @@ import javax.servlet.http.HttpServletResponse; import org.sonar.api.platform.Server; import org.sonar.api.server.authentication.BaseIdentityProvider; import org.sonar.api.server.authentication.UserIdentity; +import org.sonar.api.server.http.HttpRequest; +import org.sonar.api.server.http.HttpResponse; import org.sonar.db.user.UserDto; import org.sonar.server.authentication.event.AuthenticationEvent.Source; +import org.sonar.server.http.JavaxHttpRequest; +import org.sonar.server.http.JavaxHttpResponse; import org.sonar.server.user.ThreadLocalUserSession; import org.sonar.server.user.UserSessionFactory; @@ -46,31 +50,41 @@ public class BaseContextFactory { this.threadLocalUserSession = threadLocalUserSession; } - public BaseIdentityProvider.Context newContext(HttpServletRequest request, HttpServletResponse response, BaseIdentityProvider identityProvider) { + public BaseIdentityProvider.Context newContext(HttpRequest request, HttpResponse response, BaseIdentityProvider identityProvider) { return new ContextImpl(request, response, identityProvider); } private class ContextImpl implements BaseIdentityProvider.Context { - private final HttpServletRequest request; - private final HttpServletResponse response; + private final HttpRequest request; + private final HttpResponse response; private final BaseIdentityProvider identityProvider; - public ContextImpl(HttpServletRequest request, HttpServletResponse response, BaseIdentityProvider identityProvider) { + public ContextImpl(HttpRequest request, HttpResponse response, BaseIdentityProvider identityProvider) { this.request = request; this.response = response; this.identityProvider = identityProvider; } @Override - public HttpServletRequest getRequest() { + public HttpRequest getHttpRequest() { return request; } @Override - public HttpServletResponse getResponse() { + public HttpResponse getHttpResponse() { return response; } + @Override + public HttpServletRequest getRequest() { + return ((JavaxHttpRequest) request).getDelegate(); + } + + @Override + public HttpServletResponse getResponse() { + return ((JavaxHttpResponse) response).getDelegate(); + } + @Override public String getServerBaseURL() { return server.getPublicRootUrl(); diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/BasicAuthentication.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/BasicAuthentication.java index c3f6a619e92..e012d8c210b 100644 --- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/BasicAuthentication.java +++ b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/BasicAuthentication.java @@ -22,6 +22,7 @@ package org.sonar.server.authentication; import java.util.Base64; import java.util.Optional; import javax.servlet.http.HttpServletRequest; +import org.sonar.api.server.http.HttpRequest; import org.sonar.db.user.UserDto; import org.sonar.server.authentication.event.AuthenticationEvent; import org.sonar.server.authentication.event.AuthenticationException; @@ -49,12 +50,12 @@ public class BasicAuthentication { this.userTokenAuthentication = userTokenAuthentication; } - public Optional authenticate(HttpServletRequest request) { + public Optional authenticate(HttpRequest request) { return extractCredentialsFromHeader(request) .flatMap(credentials -> Optional.ofNullable(authenticate(credentials, request))); } - public static Optional extractCredentialsFromHeader(HttpServletRequest request) { + public static Optional extractCredentialsFromHeader(HttpRequest request) { String authorizationHeader = request.getHeader("Authorization"); if (authorizationHeader == null || !startsWithIgnoreCase(authorizationHeader, "BASIC")) { return Optional.empty(); @@ -86,7 +87,7 @@ public class BasicAuthentication { } } - private UserDto authenticate(Credentials credentials, HttpServletRequest request) { + private UserDto authenticate(Credentials credentials, HttpRequest request) { if (credentials.getPassword().isEmpty()) { Optional userAuthResult = userTokenAuthentication.authenticate(request); if (userAuthResult.isPresent()) { diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/Cookies.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/Cookies.java index 009302b18f0..bc393a722ef 100644 --- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/Cookies.java +++ b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/Cookies.java @@ -22,8 +22,9 @@ 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 org.sonar.api.server.http.Cookie; +import org.sonar.api.server.http.HttpRequest; +import org.sonar.server.http.JavaxHttpRequest.JavaxCookie; import static com.google.common.base.Strings.isNullOrEmpty; import static java.util.Objects.requireNonNull; @@ -44,7 +45,7 @@ public class Cookies { // Only static methods } - public static Optional findCookie(String cookieName, HttpServletRequest request) { + public static Optional findCookie(String cookieName, HttpRequest request) { Cookie[] cookies = request.getCookies(); if (cookies == null) { return Optional.empty(); @@ -54,13 +55,13 @@ public class Cookies { .findFirst(); } - public static CookieBuilder newCookieBuilder(HttpServletRequest request) { + public static CookieBuilder newCookieBuilder(HttpRequest request) { return new CookieBuilder(request); } public static class CookieBuilder { - private final HttpServletRequest request; + private final HttpRequest request; private String name; private String value; @@ -69,7 +70,7 @@ public class Cookies { private String sameSite; - CookieBuilder(HttpServletRequest request) { + CookieBuilder(HttpRequest request) { this.request = request; } @@ -114,12 +115,12 @@ public class Cookies { } public Cookie build() { - Cookie cookie = new Cookie(requireNonNull(name), value); + javax.servlet.http.Cookie cookie = new javax.servlet.http.Cookie(requireNonNull(name), value); cookie.setPath(getContextPath(request)); cookie.setSecure(isHttps(request)); cookie.setHttpOnly(httpOnly); cookie.setMaxAge(expiry); - return cookie; + return new JavaxCookie(cookie); } public String toValueString() { @@ -133,11 +134,11 @@ public class Cookies { return output; } - private static boolean isHttps(HttpServletRequest request) { + private static boolean isHttps(HttpRequest request) { return HTTPS_VALUE.equalsIgnoreCase(request.getHeader(HTTPS_HEADER)); } - private static String getContextPath(HttpServletRequest request) { + private static String getContextPath(HttpRequest request) { String path = request.getContextPath(); return isNullOrEmpty(path) ? "/" : path; } diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/CredentialsAuthentication.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/CredentialsAuthentication.java index 34d6768cab9..8f0f98c2c2e 100644 --- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/CredentialsAuthentication.java +++ b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/CredentialsAuthentication.java @@ -21,6 +21,7 @@ package org.sonar.server.authentication; import java.util.Optional; import javax.servlet.http.HttpServletRequest; +import org.sonar.api.server.http.HttpRequest; import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.user.UserDto; @@ -52,13 +53,13 @@ public class CredentialsAuthentication { this.ldapCredentialsAuthentication = ldapCredentialsAuthentication; } - public UserDto authenticate(Credentials credentials, HttpServletRequest request, Method method) { + public UserDto authenticate(Credentials credentials, HttpRequest request, Method method) { try (DbSession dbSession = dbClient.openSession(false)) { return authenticate(dbSession, credentials, request, method); } } - private UserDto authenticate(DbSession dbSession, Credentials credentials, HttpServletRequest request, Method method) { + private UserDto authenticate(DbSession dbSession, Credentials credentials, HttpRequest request, Method method) { UserDto localUser = dbClient.userDao().selectActiveUserByLogin(dbSession, credentials.getLogin()); if (localUser != null && localUser.isLocal()) { String password = getNonNullPassword(credentials); diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/CredentialsExternalAuthentication.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/CredentialsExternalAuthentication.java index fe39adf964d..e78c5e6c9ff 100644 --- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/CredentialsExternalAuthentication.java +++ b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/CredentialsExternalAuthentication.java @@ -24,6 +24,8 @@ import java.util.HashSet; import java.util.Locale; import java.util.Optional; import javax.servlet.http.HttpServletRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.sonar.api.Startable; import org.sonar.api.config.Configuration; import org.sonar.api.security.Authenticator; @@ -34,12 +36,12 @@ import org.sonar.api.security.UserDetails; import org.sonar.api.server.authentication.Display; import org.sonar.api.server.authentication.IdentityProvider; import org.sonar.api.server.authentication.UserIdentity; -import org.sonar.api.utils.log.Logger; -import org.sonar.api.utils.log.Loggers; +import org.sonar.api.server.http.HttpRequest; import org.sonar.db.user.UserDto; import org.sonar.server.authentication.event.AuthenticationEvent; import org.sonar.server.authentication.event.AuthenticationEvent.Source; import org.sonar.server.authentication.event.AuthenticationException; +import org.sonar.server.http.JavaxHttpRequest; import org.sonar.server.user.SecurityRealmFactory; import static java.util.Objects.requireNonNull; @@ -52,7 +54,7 @@ import static org.sonar.server.user.ExternalIdentity.SQ_AUTHORITY; */ public class CredentialsExternalAuthentication implements Startable { - private static final Logger LOG = Loggers.get(CredentialsExternalAuthentication.class); + private static final Logger LOG = LoggerFactory.getLogger(CredentialsExternalAuthentication.class); private final Configuration config; private final SecurityRealmFactory securityRealmFactory; @@ -82,16 +84,17 @@ public class CredentialsExternalAuthentication implements Startable { } } - public Optional authenticate(Credentials credentials, HttpServletRequest request, AuthenticationEvent.Method method) { + public Optional authenticate(Credentials credentials, HttpRequest request, AuthenticationEvent.Method method) { if (realm == null) { return Optional.empty(); } return Optional.of(doAuthenticate(fixCase(credentials), request, method)); } - private UserDto doAuthenticate(Credentials credentials, HttpServletRequest request, AuthenticationEvent.Method method) { + private UserDto doAuthenticate(Credentials credentials, HttpRequest request, AuthenticationEvent.Method method) { try { - ExternalUsersProvider.Context externalUsersProviderContext = new ExternalUsersProvider.Context(credentials.getLogin(), request); + HttpServletRequest httpServletRequest = ((JavaxHttpRequest) request).getDelegate(); + ExternalUsersProvider.Context externalUsersProviderContext = new ExternalUsersProvider.Context(credentials.getLogin(), request, httpServletRequest); UserDetails details = externalUsersProvider.doGetUserDetails(externalUsersProviderContext); if (details == null) { throw AuthenticationException.newBuilder() @@ -100,7 +103,8 @@ public class CredentialsExternalAuthentication implements Startable { .setMessage("No user details") .build(); } - Authenticator.Context authenticatorContext = new Authenticator.Context(credentials.getLogin(), credentials.getPassword().orElse(null), request); + + Authenticator.Context authenticatorContext = new Authenticator.Context(credentials.getLogin(), credentials.getPassword().orElse(null), request, httpServletRequest); boolean status = authenticator.doAuthenticate(authenticatorContext); if (!status) { throw AuthenticationException.newBuilder() @@ -129,14 +133,15 @@ public class CredentialsExternalAuthentication implements Startable { return Source.realm(method, realm.getName()); } - private UserDto synchronize(String userLogin, UserDetails details, HttpServletRequest request, AuthenticationEvent.Method method) { + private UserDto synchronize(String userLogin, UserDetails details, HttpRequest request, AuthenticationEvent.Method method) { String name = details.getName(); UserIdentity.Builder userIdentityBuilder = UserIdentity.builder() .setName(isEmpty(name) ? userLogin : name) .setEmail(trimToNull(details.getEmail())) .setProviderLogin(userLogin); if (externalGroupsProvider != null) { - ExternalGroupsProvider.Context context = new ExternalGroupsProvider.Context(userLogin, request); + HttpServletRequest httpServletRequest = ((JavaxHttpRequest) request).getDelegate(); + ExternalGroupsProvider.Context context = new ExternalGroupsProvider.Context(userLogin, request, httpServletRequest); Collection groups = externalGroupsProvider.doGetGroups(context); userIdentityBuilder.setGroups(new HashSet<>(groups)); } diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifierFilter.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifierFilter.java index 8c26397300f..d551292c3b0 100644 --- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifierFilter.java +++ b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifierFilter.java @@ -21,21 +21,18 @@ package org.sonar.server.authentication; import java.io.IOException; import java.util.Set; -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.config.Configuration; -import org.sonar.api.web.ServletFilter; +import org.sonar.api.server.http.HttpRequest; +import org.sonar.api.server.http.HttpResponse; +import org.sonar.api.web.FilterChain; +import org.sonar.api.web.HttpFilter; +import org.sonar.api.web.UrlPattern; import org.sonar.server.user.ThreadLocalUserSession; -import static org.sonar.api.web.ServletFilter.UrlPattern.Builder.staticResourcePatterns; +import static org.sonar.api.web.UrlPattern.Builder.staticResourcePatterns; import static org.sonar.server.authentication.AuthenticationRedirection.redirectTo; -public class DefaultAdminCredentialsVerifierFilter extends ServletFilter { +public class DefaultAdminCredentialsVerifierFilter extends HttpFilter { private static final String RESET_PASSWORD_PATH = "/account/reset_password"; private static final String CHANGE_ADMIN_PASSWORD_PATH = "/admin/change_admin_password"; // This property is used by Orchestrator to disable this force redirect. It should never be used in production, which @@ -67,14 +64,12 @@ public class DefaultAdminCredentialsVerifierFilter extends ServletFilter { } @Override - public void init(FilterConfig filterConfig) { + public void init() { // nothing to do } @Override - public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException { - HttpServletRequest request = (HttpServletRequest) servletRequest; - HttpServletResponse response = (HttpServletResponse) servletResponse; + public void doFilter(HttpRequest request, HttpResponse response, FilterChain chain) throws IOException { boolean forceRedirect = config .getBoolean(SONAR_FORCE_REDIRECT_DEFAULT_ADMIN_CREDENTIALS) .orElse(true); diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/GithubWebhookAuthentication.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/GithubWebhookAuthentication.java index dcd6fb19850..d64279bd806 100644 --- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/GithubWebhookAuthentication.java +++ b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/GithubWebhookAuthentication.java @@ -22,11 +22,11 @@ package org.sonar.server.authentication; import com.google.common.annotations.VisibleForTesting; import java.security.MessageDigest; import java.util.Optional; -import javax.servlet.http.HttpServletRequest; import org.apache.commons.codec.digest.HmacAlgorithms; import org.apache.commons.codec.digest.HmacUtils; import org.sonar.api.config.internal.Encryption; import org.sonar.api.config.internal.Settings; +import org.sonar.api.server.http.HttpRequest; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; import org.sonar.db.DbClient; @@ -77,7 +77,7 @@ public class GithubWebhookAuthentication { this.encryption = settings.getEncryption(); } - public Optional authenticate(HttpServletRequest request) { + public Optional authenticate(HttpRequest request) { String githubAppId = request.getHeader(GITHUB_APP_ID_HEADER); if (isEmpty(githubAppId)) { return Optional.empty(); @@ -94,7 +94,7 @@ public class GithubWebhookAuthentication { return createAuthResult(request); } - private static String getGithubSignature(HttpServletRequest request, String githubAppId) { + private static String getGithubSignature(HttpRequest request, String githubAppId) { String githubSignature = request.getHeader(GITHUB_SIGNATURE_HEADER); if (isEmpty(githubSignature)) { logAuthenticationProblemAndThrow(format(MSG_UNAUTHENTICATED_GITHUB_CALLS_DENIED, githubAppId)); @@ -102,7 +102,7 @@ public class GithubWebhookAuthentication { return githubSignature; } - private static String getBody(HttpServletRequest request) { + private static String getBody(HttpRequest request) { try { return request.getReader().lines().collect(joining(System.lineSeparator())); } catch (Exception e) { @@ -144,7 +144,7 @@ public class GithubWebhookAuthentication { return MessageDigest.isEqual(githubSignature.getBytes(UTF_8), computedSignature.getBytes(UTF_8)); } - private Optional createAuthResult(HttpServletRequest request) { + private Optional createAuthResult(HttpRequest request) { UserAuthResult userAuthResult = UserAuthResult.withGithubWebhook(); authenticationEvent.loginSuccess(request, GITHUB_WEBHOOK_USER_NAME, AuthenticationEvent.Source.githubWebhook()); return Optional.of(userAuthResult); diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/HttpHeadersAuthentication.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/HttpHeadersAuthentication.java index 30e5a207509..392c1ab1715 100644 --- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/HttpHeadersAuthentication.java +++ b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/HttpHeadersAuthentication.java @@ -29,13 +29,13 @@ import java.util.Locale; import java.util.Map; import java.util.Optional; import javax.annotation.CheckForNull; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import org.sonar.api.Startable; import org.sonar.api.config.Configuration; import org.sonar.api.server.authentication.Display; import org.sonar.api.server.authentication.IdentityProvider; import org.sonar.api.server.authentication.UserIdentity; +import org.sonar.api.server.http.HttpRequest; +import org.sonar.api.server.http.HttpResponse; import org.sonar.api.utils.System2; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; @@ -106,7 +106,7 @@ public class HttpHeadersAuthentication implements Startable { // Nothing to do } - public Optional authenticate(HttpServletRequest request, HttpServletResponse response) { + public Optional authenticate(HttpRequest request, HttpResponse response) { try { return doAuthenticate(request, response); } catch (BadRequestException e) { @@ -117,7 +117,7 @@ public class HttpHeadersAuthentication implements Startable { } } - private Optional doAuthenticate(HttpServletRequest request, HttpServletResponse response) { + private Optional doAuthenticate(HttpRequest request, HttpResponse response) { if (!enabled) { return Optional.empty(); } @@ -137,7 +137,7 @@ public class HttpHeadersAuthentication implements Startable { return Optional.of(userDto); } - private Optional getUserFromToken(HttpServletRequest request, HttpServletResponse response) { + private Optional getUserFromToken(HttpRequest request, HttpResponse response) { Optional token = jwtHttpHandler.getToken(request, response); if (token.isEmpty()) { return Optional.empty(); @@ -175,7 +175,7 @@ public class HttpHeadersAuthentication implements Startable { return headerValuesByNames.get(settingsByKey.get(settingKey).toLowerCase(Locale.ENGLISH)); } - private static Map getHeaders(HttpServletRequest request) { + private static Map getHeaders(HttpRequest request) { Map headers = new HashMap<>(); Collections.list(request.getHeaderNames()).forEach(header -> headers.put(header.toLowerCase(Locale.ENGLISH), request.getHeader(header))); return headers; diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/InitFilter.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/InitFilter.java index fe35388cc5b..50b9e4fbe48 100644 --- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/InitFilter.java +++ b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/InitFilter.java @@ -19,16 +19,14 @@ */ package org.sonar.server.authentication; -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import org.sonar.api.server.authentication.BaseIdentityProvider; import org.sonar.api.server.authentication.IdentityProvider; import org.sonar.api.server.authentication.OAuth2IdentityProvider; import org.sonar.api.server.authentication.UnauthorizedException; +import org.sonar.api.server.http.HttpRequest; +import org.sonar.api.server.http.HttpResponse; +import org.sonar.api.web.FilterChain; +import org.sonar.api.web.UrlPattern; import org.sonar.server.authentication.event.AuthenticationEvent; import org.sonar.server.authentication.event.AuthenticationException; @@ -61,17 +59,14 @@ public class InitFilter extends AuthenticationFilter { } @Override - public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) { - HttpServletRequest httpRequest = (HttpServletRequest) request; - HttpServletResponse httpResponse = (HttpServletResponse) response; - - IdentityProvider provider = resolveProviderOrHandleResponse(httpRequest, httpResponse, INIT_CONTEXT); + public void doFilter(HttpRequest request, HttpResponse response, FilterChain chain) { + IdentityProvider provider = resolveProviderOrHandleResponse(request, response, INIT_CONTEXT); if (provider != null) { - handleProvider(httpRequest, httpResponse, provider); + handleProvider(request, response, provider); } } - private void handleProvider(HttpServletRequest request, HttpServletResponse response, IdentityProvider provider) { + private void handleProvider(HttpRequest request, HttpResponse response, IdentityProvider provider) { try { if (provider instanceof BaseIdentityProvider baseIdentityProvider) { handleBaseIdentityProvider(request, response, baseIdentityProvider); @@ -91,7 +86,7 @@ public class InitFilter extends AuthenticationFilter { } } - private void handleBaseIdentityProvider(HttpServletRequest request, HttpServletResponse response, BaseIdentityProvider provider) { + private void handleBaseIdentityProvider(HttpRequest request, HttpResponse response, BaseIdentityProvider provider) { try { provider.init(baseContextFactory.newContext(request, response, provider)); } catch (UnauthorizedException e) { @@ -103,7 +98,7 @@ public class InitFilter extends AuthenticationFilter { } } - private void handleOAuth2IdentityProvider(HttpServletRequest request, HttpServletResponse response, OAuth2IdentityProvider provider) { + private void handleOAuth2IdentityProvider(HttpRequest request, HttpResponse response, OAuth2IdentityProvider provider) { try { provider.init(oAuth2ContextFactory.newContext(request, response, provider)); } catch (UnauthorizedException e) { @@ -116,7 +111,7 @@ public class InitFilter extends AuthenticationFilter { } @Override - public void init(FilterConfig filterConfig) { + public void init() { // Nothing to do } diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/JwtCsrfVerifier.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/JwtCsrfVerifier.java index c8a3edfc6c2..05e00f772ae 100644 --- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/JwtCsrfVerifier.java +++ b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/JwtCsrfVerifier.java @@ -19,15 +19,14 @@ */ package org.sonar.server.authentication; -import com.google.common.collect.ImmutableSet; import java.math.BigInteger; import java.security.SecureRandom; import java.util.Set; import javax.annotation.CheckForNull; import javax.annotation.Nullable; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang.StringUtils; +import org.sonar.api.server.http.HttpRequest; +import org.sonar.api.server.http.HttpResponse; import org.sonar.server.authentication.event.AuthenticationException; import static org.apache.commons.lang.StringUtils.isBlank; @@ -42,10 +41,10 @@ public class JwtCsrfVerifier { private static final String CSRF_STATE_COOKIE = "XSRF-TOKEN"; private static final String CSRF_HEADER = "X-XSRF-TOKEN"; - private static final Set UPDATE_METHODS = ImmutableSet.of("POST", "PUT", "DELETE"); + private static final Set UPDATE_METHODS = Set.of("POST", "PUT", "DELETE"); private static final String API_URL = "/api"; - public String generateState(HttpServletRequest request, HttpServletResponse response, int timeoutInSeconds) { + public String generateState(HttpRequest request, HttpResponse response, int timeoutInSeconds) { // 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); @@ -59,7 +58,7 @@ public class JwtCsrfVerifier { return state; } - public void verifyState(HttpServletRequest request, @Nullable String csrfState, @Nullable String login) { + public void verifyState(HttpRequest request, @Nullable String csrfState, @Nullable String login) { if (!shouldRequestBeChecked(request)) { return; } @@ -85,7 +84,7 @@ public class JwtCsrfVerifier { return null; } - public void refreshState(HttpServletRequest request, HttpServletResponse response, String csrfState, int timeoutInSeconds) { + public void refreshState(HttpRequest request, HttpResponse response, String csrfState, int timeoutInSeconds) { response.addHeader(SET_COOKIE, newCookieBuilder(request) .setName(CSRF_STATE_COOKIE) @@ -96,11 +95,11 @@ public class JwtCsrfVerifier { .toValueString()); } - public void removeState(HttpServletRequest request, HttpServletResponse response) { + public void removeState(HttpRequest request, HttpResponse response) { response.addCookie(newCookieBuilder(request).setName(CSRF_STATE_COOKIE).setValue(null).setHttpOnly(false).setExpiry(0).build()); } - private static boolean shouldRequestBeChecked(HttpServletRequest request) { + private static boolean shouldRequestBeChecked(HttpRequest request) { if (UPDATE_METHODS.contains(request.getMethod())) { String path = request.getRequestURI().replaceFirst(request.getContextPath(), ""); return path.startsWith(API_URL); diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/JwtHttpHandler.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/JwtHttpHandler.java index fb89b6d5261..7a7cd16ea29 100644 --- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/JwtHttpHandler.java +++ b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/JwtHttpHandler.java @@ -26,11 +26,11 @@ import java.util.Date; import java.util.Map; import java.util.Optional; import javax.annotation.Nullable; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import org.sonar.api.config.Configuration; import org.sonar.api.server.ServerSide; +import org.sonar.api.server.http.Cookie; +import org.sonar.api.server.http.HttpRequest; +import org.sonar.api.server.http.HttpResponse; import org.sonar.api.utils.System2; import org.sonar.db.DbClient; import org.sonar.db.DbSession; @@ -80,7 +80,7 @@ public class JwtHttpHandler { this.jwtCsrfVerifier = jwtCsrfVerifier; } - public void generateToken(UserDto user, Map properties, HttpServletRequest request, HttpServletResponse response) { + public void generateToken(UserDto user, Map properties, HttpRequest request, HttpResponse response) { String csrfState = jwtCsrfVerifier.generateState(request, response, sessionTimeoutInSeconds); long expirationTime = system2.now() + sessionTimeoutInSeconds * 1000L; SessionTokenDto sessionToken = createSessionToken(user, expirationTime); @@ -108,16 +108,16 @@ public class JwtHttpHandler { } } - public void generateToken(UserDto user, HttpServletRequest request, HttpServletResponse response) { + public void generateToken(UserDto user, HttpRequest request, HttpResponse response) { generateToken(user, Collections.emptyMap(), request, response); } - public Optional validateToken(HttpServletRequest request, HttpServletResponse response) { + public Optional validateToken(HttpRequest request, HttpResponse response) { Optional token = getToken(request, response); return token.map(Token::getUserDto); } - public Optional getToken(HttpServletRequest request, HttpServletResponse response) { + public Optional getToken(HttpRequest request, HttpResponse response) { Optional encodedToken = getTokenFromCookie(request); if (!encodedToken.isPresent()) { return Optional.empty(); @@ -127,9 +127,9 @@ public class JwtHttpHandler { } } - private static Optional getTokenFromCookie(HttpServletRequest request) { + private static Optional getTokenFromCookie(HttpRequest request) { Optional jwtCookie = findCookie(JWT_COOKIE, request); - if (!jwtCookie.isPresent()) { + if (jwtCookie.isEmpty()) { return Optional.empty(); } Cookie cookie = jwtCookie.get(); @@ -140,15 +140,15 @@ public class JwtHttpHandler { return Optional.of(token); } - private Optional validateToken(DbSession dbSession, String tokenEncoded, HttpServletRequest request, HttpServletResponse response) { + private Optional validateToken(DbSession dbSession, String tokenEncoded, HttpRequest request, HttpResponse response) { Optional claims = jwtSerializer.decode(tokenEncoded); - if (!claims.isPresent()) { + if (claims.isEmpty()) { return Optional.empty(); } Claims token = claims.get(); Optional sessionToken = dbClient.sessionTokensDao().selectByUuid(dbSession, token.getId()); - if (!sessionToken.isPresent()) { + if (sessionToken.isEmpty()) { return Optional.empty(); } // Check on expiration is already done when decoding the JWT token, but here is done a double check with the expiration date from DB. @@ -175,7 +175,7 @@ public class JwtHttpHandler { return new Date(lastFreshTime); } - private void refreshToken(DbSession dbSession, SessionTokenDto tokenFromDb, Claims tokenFromCookie, HttpServletRequest request, HttpServletResponse response) { + private void refreshToken(DbSession dbSession, SessionTokenDto tokenFromDb, Claims tokenFromCookie, HttpRequest request, HttpResponse response) { long expirationTime = system2.now() + sessionTimeoutInSeconds * 1000L; String refreshToken = jwtSerializer.refresh(tokenFromCookie, expirationTime); response.addHeader(SET_COOKIE, createJwtSession(request, JWT_COOKIE, refreshToken, sessionTimeoutInSeconds)); @@ -185,13 +185,13 @@ public class JwtHttpHandler { dbSession.commit(); } - public void removeToken(HttpServletRequest request, HttpServletResponse response) { + public void removeToken(HttpRequest request, HttpResponse response) { removeSessionToken(request); response.addCookie(createCookie(request, JWT_COOKIE, null, 0)); jwtCsrfVerifier.removeState(request, response); } - private void removeSessionToken(HttpServletRequest request) { + private void removeSessionToken(HttpRequest request) { Optional jwtCookie = findCookie(JWT_COOKIE, request); if (!jwtCookie.isPresent()) { return; @@ -206,11 +206,11 @@ public class JwtHttpHandler { } } - private static Cookie createCookie(HttpServletRequest request, String name, @Nullable String value, int expirationInSeconds) { + private static Cookie createCookie(HttpRequest request, String name, @Nullable String value, int expirationInSeconds) { return newCookieBuilder(request).setName(name).setValue(value).setHttpOnly(true).setExpiry(expirationInSeconds).build(); } - private static String createJwtSession(HttpServletRequest request, String name, @Nullable String value, int expirationInSeconds) { + private static String createJwtSession(HttpRequest request, String name, @Nullable String value, int expirationInSeconds) { return newCookieBuilder(request).setName(name).setValue(value).setHttpOnly(true).setExpiry(expirationInSeconds).setSameSite(SAMESITE_LAX).toValueString(); } diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/LdapCredentialsAuthentication.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/LdapCredentialsAuthentication.java index 7278efdd6a6..371d40741b3 100644 --- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/LdapCredentialsAuthentication.java +++ b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/LdapCredentialsAuthentication.java @@ -23,11 +23,11 @@ import java.util.Collection; import java.util.HashSet; import java.util.Locale; import java.util.Optional; -import javax.servlet.http.HttpServletRequest; import org.sonar.api.config.Configuration; import org.sonar.api.server.authentication.Display; import org.sonar.api.server.authentication.IdentityProvider; import org.sonar.api.server.authentication.UserIdentity; +import org.sonar.api.server.http.HttpRequest; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; import org.sonar.auth.ldap.LdapAuthenticationResult; @@ -69,14 +69,14 @@ public class LdapCredentialsAuthentication { this.ldapGroupsProvider = ldapRealm.getGroupsProvider(); } - public Optional authenticate(Credentials credentials, HttpServletRequest request, AuthenticationEvent.Method method) { + public Optional authenticate(Credentials credentials, HttpRequest request, AuthenticationEvent.Method method) { if (isLdapAuthActivated) { return Optional.of(doAuthenticate(fixCase(credentials), request, method)); } return Optional.empty(); } - private UserDto doAuthenticate(Credentials credentials, HttpServletRequest request, AuthenticationEvent.Method method) { + private UserDto doAuthenticate(Credentials credentials, HttpRequest request, AuthenticationEvent.Method method) { try { LdapAuthenticator.Context ldapAuthenticatorContext = new LdapAuthenticator.Context(credentials.getLogin(), credentials.getPassword().orElse(null), request); LdapAuthenticationResult authenticationResult = ldapAuthenticator.doAuthenticate(ldapAuthenticatorContext); @@ -117,7 +117,7 @@ public class LdapCredentialsAuthentication { return Source.realm(method, "ldap"); } - private UserDto synchronize(String userLogin, String serverKey, LdapUserDetails userDetails, HttpServletRequest request, AuthenticationEvent.Method method) { + private UserDto synchronize(String userLogin, String serverKey, LdapUserDetails userDetails, HttpRequest request, AuthenticationEvent.Method method) { String name = userDetails.getName(); UserIdentity.Builder userIdentityBuilder = UserIdentity.builder() .setName(isEmpty(name) ? userLogin : name) diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/OAuth2AuthenticationParameters.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/OAuth2AuthenticationParameters.java index fbff32ebeb5..9600183b65c 100644 --- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/OAuth2AuthenticationParameters.java +++ b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/OAuth2AuthenticationParameters.java @@ -21,9 +21,9 @@ package org.sonar.server.authentication; import java.util.Optional; import javax.servlet.FilterConfig; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import org.sonar.api.server.authentication.OAuth2IdentityProvider; +import org.sonar.api.server.http.HttpRequest; +import org.sonar.api.server.http.HttpResponse; /** * This class is used to store some parameters during the OAuth2 authentication process, by using a cookie. @@ -33,10 +33,10 @@ import org.sonar.api.server.authentication.OAuth2IdentityProvider; */ public interface OAuth2AuthenticationParameters { - void init(HttpServletRequest request, HttpServletResponse response); + void init(HttpRequest request, HttpResponse response); - Optional getReturnTo(HttpServletRequest request); + Optional getReturnTo(HttpRequest request); - void delete(HttpServletRequest request, HttpServletResponse response); + void delete(HttpRequest request, HttpResponse response); } diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/OAuth2AuthenticationParametersImpl.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/OAuth2AuthenticationParametersImpl.java index 27fc6955cb2..bfaceb6de05 100644 --- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/OAuth2AuthenticationParametersImpl.java +++ b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/OAuth2AuthenticationParametersImpl.java @@ -30,8 +30,9 @@ import java.util.Map; import java.util.Optional; import java.util.regex.Pattern; import javax.annotation.Nullable; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import org.sonar.api.server.http.Cookie; +import org.sonar.api.server.http.HttpRequest; +import org.sonar.api.server.http.HttpResponse; import static java.net.URLDecoder.decode; import static java.nio.charset.StandardCharsets.UTF_8; @@ -56,7 +57,7 @@ public class OAuth2AuthenticationParametersImpl implements OAuth2AuthenticationP }.getType(); @Override - public void init(HttpServletRequest request, HttpServletResponse response) { + public void init(HttpRequest request, HttpResponse response) { String returnTo = request.getParameter(RETURN_TO_PARAMETER); Map parameters = new HashMap<>(); Optional sanitizeRedirectUrl = sanitizeRedirectUrl(returnTo); @@ -73,14 +74,14 @@ public class OAuth2AuthenticationParametersImpl implements OAuth2AuthenticationP } @Override - public Optional getReturnTo(HttpServletRequest request) { + public Optional getReturnTo(HttpRequest request) { return getParameter(request, RETURN_TO_PARAMETER) .flatMap(OAuth2AuthenticationParametersImpl::sanitizeRedirectUrl); } - private static Optional getParameter(HttpServletRequest request, String parameterKey) { - Optional cookie = findCookie(AUTHENTICATION_COOKIE_NAME, request); - if (!cookie.isPresent()) { + private static Optional getParameter(HttpRequest request, String parameterKey) { + Optional cookie = findCookie(AUTHENTICATION_COOKIE_NAME, request); + if (cookie.isEmpty()) { return empty(); } @@ -92,7 +93,7 @@ public class OAuth2AuthenticationParametersImpl implements OAuth2AuthenticationP } @Override - public void delete(HttpServletRequest request, HttpServletResponse response) { + public void delete(HttpRequest request, HttpResponse response) { response.addCookie(newCookieBuilder(request) .setName(AUTHENTICATION_COOKIE_NAME) .setValue(null) diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/OAuth2CallbackFilter.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/OAuth2CallbackFilter.java index c3b5a664073..6717fd02667 100644 --- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/OAuth2CallbackFilter.java +++ b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/OAuth2CallbackFilter.java @@ -19,15 +19,13 @@ */ package org.sonar.server.authentication; -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import org.sonar.api.server.authentication.IdentityProvider; import org.sonar.api.server.authentication.OAuth2IdentityProvider; import org.sonar.api.server.authentication.UnauthorizedException; +import org.sonar.api.server.http.HttpRequest; +import org.sonar.api.server.http.HttpResponse; +import org.sonar.api.web.FilterChain; +import org.sonar.api.web.UrlPattern; import org.sonar.server.authentication.event.AuthenticationEvent; import org.sonar.server.authentication.event.AuthenticationException; import org.sonar.server.user.ThreadLocalUserSession; @@ -59,20 +57,17 @@ public class OAuth2CallbackFilter extends AuthenticationFilter { } @Override - public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) { - HttpServletRequest httpRequest = (HttpServletRequest) request; - HttpServletResponse httpResponse = (HttpServletResponse) response; - - IdentityProvider provider = resolveProviderOrHandleResponse(httpRequest, httpResponse, CALLBACK_PATH); + public void doFilter(HttpRequest request, HttpResponse response, FilterChain chain) { + IdentityProvider provider = resolveProviderOrHandleResponse(request, response, CALLBACK_PATH); if (provider != null) { - handleProvider(httpRequest, (HttpServletResponse) response, provider); + handleProvider(request, response, provider); } } - private void handleProvider(HttpServletRequest request, HttpServletResponse response, IdentityProvider provider) { + private void handleProvider(HttpRequest request, HttpResponse response, IdentityProvider provider) { try { if (provider instanceof OAuth2IdentityProvider oAuth2IdentityProvider) { - handleOAuth2Provider(response, request, oAuth2IdentityProvider); + handleOAuth2Provider(request, response, oAuth2IdentityProvider); } else { handleError(request, response, format("Not an OAuth2IdentityProvider: %s", provider.getClass())); } @@ -86,8 +81,8 @@ public class OAuth2CallbackFilter extends AuthenticationFilter { } } - private void handleOAuth2Provider(HttpServletResponse response, HttpServletRequest httpRequest, OAuth2IdentityProvider oAuth2Provider) { - OAuth2IdentityProvider.CallbackContext context = oAuth2ContextFactory.newCallback(httpRequest, response, oAuth2Provider); + private void handleOAuth2Provider(HttpRequest request, HttpResponse response, OAuth2IdentityProvider oAuth2Provider) { + OAuth2IdentityProvider.CallbackContext context = oAuth2ContextFactory.newCallback(request, response, oAuth2Provider); try { oAuth2Provider.callback(context); } catch (UnauthorizedException e) { @@ -98,7 +93,7 @@ public class OAuth2CallbackFilter extends AuthenticationFilter { .build(); } if (threadLocalUserSession.hasSession()) { - authenticationEvent.loginSuccess(httpRequest, threadLocalUserSession.getLogin(), Source.oauth2(oAuth2Provider)); + authenticationEvent.loginSuccess(request, threadLocalUserSession.getLogin(), Source.oauth2(oAuth2Provider)); } else { throw AuthenticationException.newBuilder() .setSource(Source.oauth2(oAuth2Provider)) @@ -108,7 +103,7 @@ public class OAuth2CallbackFilter extends AuthenticationFilter { } @Override - public void init(FilterConfig filterConfig) { + public void init() { // Nothing to do } diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/OAuth2ContextFactory.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/OAuth2ContextFactory.java index 4c952ddcacd..7fc0db4073c 100644 --- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/OAuth2ContextFactory.java +++ b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/OAuth2ContextFactory.java @@ -28,8 +28,12 @@ import org.sonar.api.platform.Server; import org.sonar.api.server.ServerSide; import org.sonar.api.server.authentication.OAuth2IdentityProvider; import org.sonar.api.server.authentication.UserIdentity; +import org.sonar.api.server.http.HttpRequest; +import org.sonar.api.server.http.HttpResponse; import org.sonar.db.user.UserDto; import org.sonar.server.authentication.event.AuthenticationEvent; +import org.sonar.server.http.JavaxHttpRequest; +import org.sonar.server.http.JavaxHttpResponse; import org.sonar.server.user.ThreadLocalUserSession; import org.sonar.server.user.UserSessionFactory; @@ -58,11 +62,11 @@ public class OAuth2ContextFactory { this.oAuthParameters = oAuthParameters; } - public OAuth2IdentityProvider.InitContext newContext(HttpServletRequest request, HttpServletResponse response, OAuth2IdentityProvider identityProvider) { + public OAuth2IdentityProvider.InitContext newContext(HttpRequest request, HttpResponse response, OAuth2IdentityProvider identityProvider) { return new OAuthContextImpl(request, response, identityProvider); } - public OAuth2IdentityProvider.CallbackContext newCallback(HttpServletRequest request, HttpServletResponse response, OAuth2IdentityProvider identityProvider) { + public OAuth2IdentityProvider.CallbackContext newCallback(HttpRequest request, HttpResponse response, OAuth2IdentityProvider identityProvider) { return new OAuthContextImpl(request, response, identityProvider); } @@ -73,11 +77,11 @@ public class OAuth2ContextFactory { public class OAuthContextImpl implements OAuth2IdentityProvider.InitContext, OAuth2IdentityProvider.CallbackContext { - private final HttpServletRequest request; - private final HttpServletResponse response; + private final HttpRequest request; + private final HttpResponse response; private final OAuth2IdentityProvider identityProvider; - public OAuthContextImpl(HttpServletRequest request, HttpServletResponse response, OAuth2IdentityProvider identityProvider) { + public OAuthContextImpl(HttpRequest request, HttpResponse response, OAuth2IdentityProvider identityProvider) { this.request = request; this.response = response; this.identityProvider = identityProvider; @@ -94,15 +98,25 @@ public class OAuth2ContextFactory { } @Override - public HttpServletRequest getRequest() { + public HttpRequest getHttpRequest() { return request; } @Override - public HttpServletResponse getResponse() { + public HttpResponse getHttpResponse() { return response; } + @Override + public HttpServletRequest getRequest() { + return ((JavaxHttpRequest) request).getDelegate(); + } + + @Override + public HttpServletResponse getResponse() { + return ((JavaxHttpResponse) response).getDelegate(); + } + @Override public void redirectTo(String url) { try { @@ -127,7 +141,7 @@ public class OAuth2ContextFactory { try { Optional redirectTo = oAuthParameters.getReturnTo(request); oAuthParameters.delete(request, response); - getResponse().sendRedirect(redirectTo.orElse(server.getContextPath() + "/")); + getHttpResponse().sendRedirect(redirectTo.orElse(server.getContextPath() + "/")); } catch (IOException e) { throw new IllegalStateException("Fail to redirect to requested page", e); } diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/OAuthCsrfVerifier.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/OAuthCsrfVerifier.java index a7acad8720f..3f4732a26fb 100644 --- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/OAuthCsrfVerifier.java +++ b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/OAuthCsrfVerifier.java @@ -21,10 +21,10 @@ package org.sonar.server.authentication; import java.math.BigInteger; import java.security.SecureRandom; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import org.sonar.api.server.authentication.OAuth2IdentityProvider; +import org.sonar.api.server.http.Cookie; +import org.sonar.api.server.http.HttpRequest; +import org.sonar.api.server.http.HttpResponse; import org.sonar.server.authentication.event.AuthenticationException; import static java.lang.String.format; @@ -39,7 +39,7 @@ public class OAuthCsrfVerifier { private static final String CSRF_STATE_COOKIE = "OAUTHSTATE"; private static final String DEFAULT_STATE_PARAMETER_NAME = "state"; - public String generateState(HttpServletRequest request, HttpServletResponse response) { + public String generateState(HttpRequest request, HttpResponse response) { // 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); @@ -47,11 +47,11 @@ public class OAuthCsrfVerifier { return state; } - public void verifyState(HttpServletRequest request, HttpServletResponse response, OAuth2IdentityProvider provider) { + public void verifyState(HttpRequest request, HttpResponse response, OAuth2IdentityProvider provider) { verifyState(request, response, provider, DEFAULT_STATE_PARAMETER_NAME); } - public void verifyState(HttpServletRequest request, HttpServletResponse response, OAuth2IdentityProvider provider, String parameterName) { + public void verifyState(HttpRequest request, HttpResponse response, OAuth2IdentityProvider provider, String parameterName) { Cookie cookie = findCookie(CSRF_STATE_COOKIE, request) .orElseThrow(AuthenticationException.newBuilder() .setSource(Source.oauth2(provider)) diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/RequestAuthenticator.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/RequestAuthenticator.java index edba46fc8a2..235873c9f28 100644 --- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/RequestAuthenticator.java +++ b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/RequestAuthenticator.java @@ -19,9 +19,9 @@ */ package org.sonar.server.authentication; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import org.sonar.api.server.ServerSide; +import org.sonar.api.server.http.HttpRequest; +import org.sonar.api.server.http.HttpResponse; import org.sonar.server.user.UserSession; @ServerSide @@ -30,6 +30,6 @@ public interface RequestAuthenticator { /** * @throws org.sonar.server.authentication.event.AuthenticationException if user is not authenticated */ - UserSession authenticate(HttpServletRequest request, HttpServletResponse response); + UserSession authenticate(HttpRequest request, HttpResponse response); } diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/RequestAuthenticatorImpl.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/RequestAuthenticatorImpl.java index b74f1631c39..861c14d6d0b 100644 --- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/RequestAuthenticatorImpl.java +++ b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/RequestAuthenticatorImpl.java @@ -20,8 +20,8 @@ package org.sonar.server.authentication; import java.util.function.Function; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import org.sonar.api.server.http.HttpRequest; +import org.sonar.api.server.http.HttpResponse; import org.sonar.db.user.UserDto; import org.sonar.server.user.UserSession; import org.sonar.server.user.UserSessionFactory; @@ -64,7 +64,7 @@ public class RequestAuthenticatorImpl implements RequestAuthenticator { } @Override - public UserSession authenticate(HttpServletRequest request, HttpServletResponse response) { + public UserSession authenticate(HttpRequest request, HttpResponse response) { UserAuthResult userAuthResult = loadUser(request, response); if (nonNull(userAuthResult.getUserDto())) { if (TOKEN.equals(userAuthResult.getAuthType())) { @@ -77,7 +77,7 @@ public class RequestAuthenticatorImpl implements RequestAuthenticator { return userSessionFactory.createAnonymous(); } - private UserAuthResult loadUser(HttpServletRequest request, HttpServletResponse response) { + private UserAuthResult loadUser(HttpRequest request, HttpResponse response) { Function> createUserAuthResult = type -> userDto -> new UserAuthResult(userDto, type); // SSO authentication should come first in order to update JWT if user from header is not the same is user from JWT return httpHeadersAuthentication.authenticate(request, response).map(createUserAuthResult.apply(SSO)) diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/ResetPasswordFilter.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/ResetPasswordFilter.java index 311c93d5b0e..68f494d988d 100644 --- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/ResetPasswordFilter.java +++ b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/ResetPasswordFilter.java @@ -21,20 +21,17 @@ package org.sonar.server.authentication; import java.io.IOException; import java.util.Set; -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.api.server.http.HttpRequest; +import org.sonar.api.server.http.HttpResponse; +import org.sonar.api.web.FilterChain; +import org.sonar.api.web.HttpFilter; +import org.sonar.api.web.UrlPattern; import org.sonar.server.user.ThreadLocalUserSession; -import static org.sonar.api.web.ServletFilter.UrlPattern.Builder.staticResourcePatterns; +import static org.sonar.api.web.UrlPattern.Builder.staticResourcePatterns; import static org.sonar.server.authentication.AuthenticationRedirection.redirectTo; -public class ResetPasswordFilter extends ServletFilter { +public class ResetPasswordFilter extends HttpFilter { private static final String RESET_PASSWORD_PATH = "/account/reset_password"; private static final Set SKIPPED_URLS = Set.of( @@ -57,15 +54,12 @@ public class ResetPasswordFilter extends ServletFilter { } @Override - public void init(FilterConfig filterConfig) { + public void init() { // nothing to do } @Override - public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException { - HttpServletRequest request = (HttpServletRequest) servletRequest; - HttpServletResponse response = (HttpServletResponse) servletResponse; - + public void doFilter(HttpRequest request, HttpResponse response, FilterChain chain) throws IOException { if (userSession.hasSession() && userSession.isLoggedIn() && userSession.shouldResetPassword()) { redirectTo(response, request.getContextPath() + RESET_PASSWORD_PATH); } diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/SamlValidationCspHeaders.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/SamlValidationCspHeaders.java index 863449a733a..8f96b039e7a 100644 --- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/SamlValidationCspHeaders.java +++ b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/SamlValidationCspHeaders.java @@ -23,6 +23,7 @@ import java.math.BigInteger; import java.security.SecureRandom; import java.util.List; import javax.servlet.http.HttpServletResponse; +import org.sonar.api.server.http.HttpResponse; public class SamlValidationCspHeaders { @@ -30,7 +31,7 @@ public class SamlValidationCspHeaders { throw new IllegalStateException("Utility class, cannot be instantiated"); } - public static String addCspHeadersWithNonceToResponse(HttpServletResponse httpResponse) { + public static String addCspHeadersWithNonceToResponse(HttpResponse httpResponse) { final String nonce = getNonce(); List cspPolicies = List.of( diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/SamlValidationRedirectionFilter.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/SamlValidationRedirectionFilter.java index a0d9e55b326..4f150a64db8 100644 --- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/SamlValidationRedirectionFilter.java +++ b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/SamlValidationRedirectionFilter.java @@ -25,20 +25,18 @@ import java.net.URI; import java.net.URL; import java.nio.charset.StandardCharsets; import javax.annotation.Nullable; -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang.StringUtils; import org.sonar.api.internal.apachecommons.lang.StringEscapeUtils; import org.sonar.api.platform.Server; -import org.sonar.api.web.ServletFilter; +import org.sonar.api.server.http.HttpRequest; +import org.sonar.api.server.http.HttpResponse; +import org.sonar.api.web.FilterChain; +import org.sonar.api.web.HttpFilter; +import org.sonar.api.web.UrlPattern; import static org.sonar.server.authentication.AuthenticationFilter.CALLBACK_PATH; -public class SamlValidationRedirectionFilter extends ServletFilter { +public class SamlValidationRedirectionFilter extends HttpFilter { public static final String VALIDATION_RELAY_STATE = "validation-query"; public static final String SAML_VALIDATION_CONTROLLER_CONTEXT = "saml"; @@ -58,8 +56,7 @@ public class SamlValidationRedirectionFilter extends ServletFilter { } @Override - public void init(FilterConfig filterConfig) throws ServletException { - super.init(filterConfig); + public void init() { this.redirectionPageTemplate = extractTemplate("validation-redirection.html"); } @@ -73,27 +70,25 @@ public class SamlValidationRedirectionFilter extends ServletFilter { } @Override - public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + public void doFilter(HttpRequest request, HttpResponse response, FilterChain chain) throws IOException { String relayState = request.getParameter(RELAY_STATE_PARAMETER); if (isSamlValidation(relayState)) { - HttpServletResponse httpResponse = (HttpServletResponse) response; - URI redirectionEndpointUrl = URI.create(server.getContextPath() + "/") .resolve(SAML_VALIDATION_CONTROLLER_CONTEXT + "/") .resolve(SAML_VALIDATION_KEY); String samlResponse = StringEscapeUtils.escapeHtml(request.getParameter(SAML_RESPONSE_PARAMETER)); String csrfToken = getCsrfTokenFromRelayState(relayState); - String nonce = SamlValidationCspHeaders.addCspHeadersWithNonceToResponse(httpResponse); + String nonce = SamlValidationCspHeaders.addCspHeadersWithNonceToResponse(response); String template = StringUtils.replaceEachRepeatedly(redirectionPageTemplate, new String[]{"%NONCE%", "%WEB_CONTEXT%", "%VALIDATION_URL%", "%SAML_RESPONSE%", "%CSRF_TOKEN%"}, new String[]{nonce, server.getContextPath(), redirectionEndpointUrl.toString(), samlResponse, csrfToken}); - httpResponse.setContentType("text/html"); - httpResponse.getWriter().print(template); + response.setContentType("text/html"); + response.getWriter().print(template); return; } chain.doFilter(request, response); @@ -112,5 +107,4 @@ public class SamlValidationRedirectionFilter extends ServletFilter { } return ""; } - } diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/UserSessionInitializer.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/UserSessionInitializer.java index fa6be146e7e..8a809380a13 100644 --- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/UserSessionInitializer.java +++ b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/UserSessionInitializer.java @@ -21,11 +21,11 @@ package org.sonar.server.authentication; import java.util.Optional; import java.util.Set; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import org.sonar.api.config.Configuration; import org.sonar.api.server.ServerSide; -import org.sonar.api.web.ServletFilter.UrlPattern; +import org.sonar.api.server.http.HttpRequest; +import org.sonar.api.server.http.HttpResponse; +import org.sonar.api.web.UrlPattern; import org.sonar.db.user.UserTokenDto; import org.sonar.server.authentication.event.AuthenticationEvent; import org.sonar.server.authentication.event.AuthenticationEvent.Source; @@ -39,7 +39,7 @@ import static org.apache.commons.lang.StringUtils.defaultString; import static org.sonar.api.CoreProperties.CORE_FORCE_AUTHENTICATION_DEFAULT_VALUE; import static org.sonar.api.CoreProperties.CORE_FORCE_AUTHENTICATION_PROPERTY; import static org.sonar.api.utils.DateUtils.formatDateTime; -import static org.sonar.api.web.ServletFilter.UrlPattern.Builder.staticResourcePatterns; +import static org.sonar.api.web.UrlPattern.Builder.staticResourcePatterns; import static org.sonar.server.authentication.AuthenticationError.handleAuthenticationError; @ServerSide @@ -97,7 +97,7 @@ public class UserSessionInitializer { this.requestAuthenticator = requestAuthenticator; } - public boolean initUserSession(HttpServletRequest request, HttpServletResponse response) { + public boolean initUserSession(HttpRequest request, HttpResponse response) { String path = request.getRequestURI().replaceFirst(request.getContextPath(), ""); try { // Do not set user session when url is excluded @@ -126,7 +126,7 @@ public class UserSessionInitializer { return provider != AuthenticationEvent.Provider.LOCAL && provider != AuthenticationEvent.Provider.JWT; } - private void loadUserSession(HttpServletRequest request, HttpServletResponse response, boolean urlSupportsSystemPasscode) { + private void loadUserSession(HttpRequest request, HttpResponse response, boolean urlSupportsSystemPasscode) { UserSession session = requestAuthenticator.authenticate(request, response); if (!session.isLoggedIn() && !urlSupportsSystemPasscode && config.getBoolean(CORE_FORCE_AUTHENTICATION_PROPERTY).orElse(CORE_FORCE_AUTHENTICATION_DEFAULT_VALUE)) { // authentication is required @@ -140,7 +140,7 @@ public class UserSessionInitializer { request.setAttribute(ACCESS_LOG_LOGIN, defaultString(session.getLogin(), "-")); } - private static void checkTokenUserSession(HttpServletResponse response, UserSession session) { + private static void checkTokenUserSession(HttpResponse response, UserSession session) { if (session instanceof TokenUserSession tokenUserSession) { UserTokenDto userTokenDto = tokenUserSession.getUserToken(); Optional.ofNullable(userTokenDto.getExpirationDate()).ifPresent(expirationDate -> response.addHeader(SQ_AUTHENTICATION_TOKEN_EXPIRATION, formatDateTime(expirationDate))); diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/event/AuthenticationEvent.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/event/AuthenticationEvent.java index 236132f4076..d64f6525a01 100644 --- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/event/AuthenticationEvent.java +++ b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/event/AuthenticationEvent.java @@ -22,23 +22,23 @@ package org.sonar.server.authentication.event; import java.io.Serializable; import java.util.Objects; import javax.annotation.Nullable; -import javax.servlet.http.HttpServletRequest; import org.sonar.api.server.authentication.BaseIdentityProvider; import org.sonar.api.server.authentication.IdentityProvider; import org.sonar.api.server.authentication.OAuth2IdentityProvider; +import org.sonar.api.server.http.HttpRequest; import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; public interface AuthenticationEvent { - void loginSuccess(HttpServletRequest request, @Nullable String login, Source source); + void loginSuccess(HttpRequest request, @Nullable String login, Source source); - void loginFailure(HttpServletRequest request, AuthenticationException e); + void loginFailure(HttpRequest request, AuthenticationException e); - void logoutSuccess(HttpServletRequest request, @Nullable String login); + void logoutSuccess(HttpRequest request, @Nullable String login); - void logoutFailure(HttpServletRequest request, String errorMessage); + void logoutFailure(HttpRequest request, String errorMessage); enum Method { /** diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/event/AuthenticationEventImpl.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/event/AuthenticationEventImpl.java index 4cd38806e53..6000f752b47 100644 --- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/event/AuthenticationEventImpl.java +++ b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/event/AuthenticationEventImpl.java @@ -22,7 +22,7 @@ package org.sonar.server.authentication.event; import com.google.common.base.Joiner; import java.util.Collections; import javax.annotation.Nullable; -import javax.servlet.http.HttpServletRequest; +import org.sonar.api.server.http.HttpRequest; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; import org.sonar.core.util.stream.MoreCollectors; @@ -34,7 +34,7 @@ public class AuthenticationEventImpl implements AuthenticationEvent { private static final int FLOOD_THRESHOLD = 128; @Override - public void loginSuccess(HttpServletRequest request, @Nullable String login, Source source) { + public void loginSuccess(HttpRequest request, @Nullable String login, Source source) { checkRequest(request); requireNonNull(source, "source can't be null"); if (!LOGGER.isDebugEnabled()) { @@ -46,12 +46,12 @@ public class AuthenticationEventImpl implements AuthenticationEvent { preventLogFlood(emptyIfNull(login))); } - private static String getAllIps(HttpServletRequest request) { + private static String getAllIps(HttpRequest request) { return Collections.list(request.getHeaders("X-Forwarded-For")).stream().collect(MoreCollectors.join(Joiner.on(","))); } @Override - public void loginFailure(HttpServletRequest request, AuthenticationException e) { + public void loginFailure(HttpRequest request, AuthenticationException e) { checkRequest(request); requireNonNull(e, "AuthenticationException can't be null"); if (!LOGGER.isDebugEnabled()) { @@ -66,7 +66,7 @@ public class AuthenticationEventImpl implements AuthenticationEvent { } @Override - public void logoutSuccess(HttpServletRequest request, @Nullable String login) { + public void logoutSuccess(HttpRequest request, @Nullable String login) { checkRequest(request); if (!LOGGER.isDebugEnabled()) { return; @@ -77,7 +77,7 @@ public class AuthenticationEventImpl implements AuthenticationEvent { } @Override - public void logoutFailure(HttpServletRequest request, String errorMessage) { + public void logoutFailure(HttpRequest request, String errorMessage) { checkRequest(request); requireNonNull(errorMessage, "error message can't be null"); if (!LOGGER.isDebugEnabled()) { @@ -88,7 +88,7 @@ public class AuthenticationEventImpl implements AuthenticationEvent { request.getRemoteAddr(), getAllIps(request)); } - private static void checkRequest(HttpServletRequest request) { + private static void checkRequest(HttpRequest request) { requireNonNull(request, "request can't be null"); } diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/usertoken/UserTokenAuthentication.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/usertoken/UserTokenAuthentication.java index e86652beb9b..8ee0e5e4e7f 100644 --- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/usertoken/UserTokenAuthentication.java +++ b/server/sonar-webserver-auth/src/main/java/org/sonar/server/usertoken/UserTokenAuthentication.java @@ -23,6 +23,7 @@ import java.util.Optional; import javax.annotation.Nullable; import javax.servlet.http.HttpServletRequest; import org.apache.commons.lang.StringUtils; +import org.sonar.api.server.http.HttpRequest; import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.user.UserDto; @@ -57,13 +58,13 @@ public class UserTokenAuthentication { this.authenticationEvent = authenticationEvent; } - public Optional authenticate(HttpServletRequest request) { + public Optional authenticate(HttpRequest request) { return findBearerToken(request) .or(() -> findTokenUsedWithBasicAuthentication(request)) .map(userAuthResult -> login(request, userAuthResult)); } - private static Optional findBearerToken(HttpServletRequest request) { + private static Optional findBearerToken(HttpRequest request) { // hack necessary as #org.sonar.server.monitoring.MetricsAction and org.sonar.server.platform.ws.SafeModeMonitoringMetricAction // are providing their own bearer token based authentication mechanism that we can't get rid of for backward compatibility reasons if (request.getServletPath().startsWith(API_MONITORING_METRICS_PATH)) { @@ -77,7 +78,7 @@ public class UserTokenAuthentication { return Optional.empty(); } - private static Optional findTokenUsedWithBasicAuthentication(HttpServletRequest request) { + private static Optional findTokenUsedWithBasicAuthentication(HttpRequest request) { Credentials credentials = extractCredentialsFromHeader(request).orElse(null); if (isTokenWithBasicAuthenticationMethod(credentials)) { return Optional.ofNullable(credentials.getLogin()); @@ -89,13 +90,13 @@ public class UserTokenAuthentication { return Optional.ofNullable(credentials).map(c -> c.getPassword().isEmpty()).orElse(false); } - private UserAuthResult login(HttpServletRequest request, String token) { + private UserAuthResult login(HttpRequest request, String token) { UserAuthResult userAuthResult = authenticateFromUserToken(token, request); authenticationEvent.loginSuccess(request, userAuthResult.getUserDto().getLogin(), AuthenticationEvent.Source.local(AuthenticationEvent.Method.SONARQUBE_TOKEN)); return userAuthResult; } - private UserAuthResult authenticateFromUserToken(String token, HttpServletRequest request) { + private UserAuthResult authenticateFromUserToken(String token, HttpRequest request) { try (DbSession dbSession = dbClient.openSession(false)) { UserTokenDto userToken = authenticate(token); UserDto userDto = dbClient.userDao().selectByUuid(dbSession, userToken.getUserUuid()); diff --git a/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/BaseContextFactoryTest.java b/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/BaseContextFactoryTest.java index 6bf6127d4b2..20f5816d638 100644 --- a/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/BaseContextFactoryTest.java +++ b/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/BaseContextFactoryTest.java @@ -21,7 +21,6 @@ package org.sonar.server.authentication; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; @@ -29,6 +28,8 @@ import org.sonar.api.platform.Server; import org.sonar.api.server.authentication.BaseIdentityProvider; import org.sonar.api.server.authentication.UserIdentity; import org.sonar.db.user.UserDto; +import org.sonar.server.http.JavaxHttpRequest; +import org.sonar.server.http.JavaxHttpResponse; import org.sonar.server.user.TestUserSessionFactory; import org.sonar.server.user.ThreadLocalUserSession; import org.sonar.server.user.UserSession; @@ -51,30 +52,34 @@ public class BaseContextFactoryTest { .setEmail("john@email.com") .build(); - private ThreadLocalUserSession threadLocalUserSession = mock(ThreadLocalUserSession.class); + private final ThreadLocalUserSession threadLocalUserSession = mock(ThreadLocalUserSession.class); - private TestUserRegistrar userIdentityAuthenticator = new TestUserRegistrar(); - private Server server = mock(Server.class); + private final TestUserRegistrar userIdentityAuthenticator = new TestUserRegistrar(); + private final Server server = mock(Server.class); - private HttpServletRequest request = mock(HttpServletRequest.class); - private HttpServletResponse response = mock(HttpServletResponse.class); - private BaseIdentityProvider identityProvider = mock(BaseIdentityProvider.class); - private JwtHttpHandler jwtHttpHandler = mock(JwtHttpHandler.class); - private TestUserSessionFactory userSessionFactory = TestUserSessionFactory.standalone(); + private final HttpServletRequest request = mock(HttpServletRequest.class); + private final HttpServletResponse response = mock(HttpServletResponse.class); + private final BaseIdentityProvider identityProvider = mock(BaseIdentityProvider.class); + private final JwtHttpHandler jwtHttpHandler = mock(JwtHttpHandler.class); + private final TestUserSessionFactory userSessionFactory = TestUserSessionFactory.standalone(); - private BaseContextFactory underTest = new BaseContextFactory(userIdentityAuthenticator, server, jwtHttpHandler, threadLocalUserSession, userSessionFactory); + private final BaseContextFactory underTest = new BaseContextFactory(userIdentityAuthenticator, server, jwtHttpHandler, threadLocalUserSession, userSessionFactory); @Before public void setUp() { when(server.getPublicRootUrl()).thenReturn(PUBLIC_ROOT_URL); when(identityProvider.getName()).thenReturn("GitHub"); when(identityProvider.getKey()).thenReturn("github"); - when(request.getSession()).thenReturn(mock(HttpSession.class)); } @Test public void create_context() { - BaseIdentityProvider.Context context = underTest.newContext(request, response, identityProvider); + JavaxHttpRequest httpRequest = new JavaxHttpRequest(request); + JavaxHttpResponse httpResponse = new JavaxHttpResponse(response); + BaseIdentityProvider.Context context = underTest.newContext(httpRequest, httpResponse, identityProvider); + + assertThat(context.getHttpRequest()).isEqualTo(httpRequest); + assertThat(context.getHttpResponse()).isEqualTo(httpResponse); assertThat(context.getRequest()).isEqualTo(request); assertThat(context.getResponse()).isEqualTo(response); @@ -83,14 +88,17 @@ public class BaseContextFactoryTest { @Test public void authenticate() { - BaseIdentityProvider.Context context = underTest.newContext(request, response, identityProvider); + JavaxHttpRequest httpRequest = new JavaxHttpRequest(request); + JavaxHttpResponse httpResponse = new JavaxHttpResponse(response); + + BaseIdentityProvider.Context context = underTest.newContext(httpRequest, httpResponse, identityProvider); ArgumentCaptor userArgumentCaptor = ArgumentCaptor.forClass(UserDto.class); context.authenticate(USER_IDENTITY); assertThat(userIdentityAuthenticator.isAuthenticated()).isTrue(); verify(threadLocalUserSession).set(any(UserSession.class)); - verify(jwtHttpHandler).generateToken(userArgumentCaptor.capture(), eq(request), eq(response)); + verify(jwtHttpHandler).generateToken(userArgumentCaptor.capture(), eq(httpRequest), eq(httpResponse)); assertThat(userArgumentCaptor.getValue().getExternalId()).isEqualTo(USER_IDENTITY.getProviderId()); assertThat(userArgumentCaptor.getValue().getExternalLogin()).isEqualTo(USER_IDENTITY.getProviderLogin()); assertThat(userArgumentCaptor.getValue().getExternalIdentityProvider()).isEqualTo("github"); diff --git a/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/BasicAuthenticationTest.java b/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/BasicAuthenticationTest.java index fc3a4f9fe4e..eb8ba91b371 100644 --- a/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/BasicAuthenticationTest.java +++ b/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/BasicAuthenticationTest.java @@ -21,10 +21,10 @@ package org.sonar.server.authentication; import java.util.Base64; import java.util.Optional; -import javax.servlet.http.HttpServletRequest; import org.junit.Before; import org.junit.Rule; import org.junit.Test; +import org.sonar.api.server.http.HttpRequest; import org.sonar.api.utils.System2; import org.sonar.db.DbTester; import org.sonar.db.user.UserDto; @@ -67,7 +67,7 @@ public class BasicAuthenticationTest { private final CredentialsAuthentication credentialsAuthentication = mock(CredentialsAuthentication.class); private final UserTokenAuthentication userTokenAuthentication = mock(UserTokenAuthentication.class); - private final HttpServletRequest request = mock(HttpServletRequest.class); + private final HttpRequest request = mock(HttpRequest.class); private final AuthenticationEvent authenticationEvent = mock(AuthenticationEvent.class); diff --git a/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/CookiesTest.java b/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/CookiesTest.java index 09c27ee4c69..986adccce65 100644 --- a/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/CookiesTest.java +++ b/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/CookiesTest.java @@ -19,9 +19,9 @@ */ package org.sonar.server.authentication; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; import org.junit.Test; +import org.sonar.api.server.http.Cookie; +import org.sonar.api.server.http.HttpRequest; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -35,7 +35,7 @@ public class CookiesTest { private static final String HTTPS_HEADER = "X-Forwarded-Proto"; - private HttpServletRequest request = mock(HttpServletRequest.class); + private HttpRequest request = mock(HttpRequest.class); @Test public void create_cookie() { @@ -44,7 +44,7 @@ public class CookiesTest { assertThat(cookie.getValue()).isEqualTo("value"); assertThat(cookie.isHttpOnly()).isTrue(); assertThat(cookie.getMaxAge()).isEqualTo(10); - assertThat(cookie.getSecure()).isFalse(); + assertThat(cookie.isSecure()).isFalse(); assertThat(cookie.getPath()).isEqualTo("/"); } @@ -63,7 +63,7 @@ public class CookiesTest { assertThat(cookie.getValue()).isEqualTo("value"); assertThat(cookie.isHttpOnly()).isTrue(); assertThat(cookie.getMaxAge()).isEqualTo(10); - assertThat(cookie.getSecure()).isFalse(); + assertThat(cookie.isSecure()).isFalse(); assertThat(cookie.getPath()).isEqualTo("/sonarqube"); } @@ -71,21 +71,21 @@ public class CookiesTest { public void create_not_secured_cookie_when_header_is_not_http() { when(request.getHeader(HTTPS_HEADER)).thenReturn("http"); Cookie cookie = newCookieBuilder(request).setName("name").setValue("value").setHttpOnly(true).setExpiry(10).build(); - assertThat(cookie.getSecure()).isFalse(); + assertThat(cookie.isSecure()).isFalse(); } @Test public void create_secured_cookie_when_X_Forwarded_Proto_header_is_https() { when(request.getHeader(HTTPS_HEADER)).thenReturn("https"); Cookie cookie = newCookieBuilder(request).setName("name").setValue("value").setHttpOnly(true).setExpiry(10).build(); - assertThat(cookie.getSecure()).isTrue(); + assertThat(cookie.isSecure()).isTrue(); } @Test public void create_secured_cookie_when_X_Forwarded_Proto_header_is_HTTPS() { when(request.getHeader(HTTPS_HEADER)).thenReturn("HTTPS"); Cookie cookie = newCookieBuilder(request).setName("name").setValue("value").setHttpOnly(true).setExpiry(10).build(); - assertThat(cookie.getSecure()).isTrue(); + assertThat(cookie.isSecure()).isTrue(); } @Test diff --git a/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/CredentialsAuthenticationTest.java b/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/CredentialsAuthenticationTest.java index fdf214ad9f4..7d5bc45ab2e 100644 --- a/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/CredentialsAuthenticationTest.java +++ b/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/CredentialsAuthenticationTest.java @@ -20,10 +20,10 @@ package org.sonar.server.authentication; import java.util.Optional; -import javax.servlet.http.HttpServletRequest; import org.junit.Rule; import org.junit.Test; import org.sonar.api.config.internal.MapSettings; +import org.sonar.api.server.http.HttpRequest; import org.sonar.api.utils.System2; import org.sonar.db.DbClient; import org.sonar.db.DbSession; @@ -60,7 +60,7 @@ public class CredentialsAuthenticationTest { public DbTester dbTester = DbTester.create(System2.INSTANCE); private final DbClient dbClient = dbTester.getDbClient(); private final DbSession dbSession = dbTester.getSession(); - private final HttpServletRequest request = mock(HttpServletRequest.class); + private final HttpRequest request = mock(HttpRequest.class); private final AuthenticationEvent authenticationEvent = mock(AuthenticationEvent.class); private final MapSettings settings = new MapSettings().setProperty("sonar.internal.pbkdf2.iterations", NUMBER_OF_PBKDF2_ITERATIONS); private final CredentialsExternalAuthentication externalAuthentication = mock(CredentialsExternalAuthentication.class); diff --git a/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/CredentialsExternalAuthenticationTest.java b/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/CredentialsExternalAuthenticationTest.java index c3da6a4bc3c..0ff96ed24d1 100644 --- a/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/CredentialsExternalAuthenticationTest.java +++ b/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/CredentialsExternalAuthenticationTest.java @@ -28,9 +28,11 @@ import org.sonar.api.security.ExternalGroupsProvider; import org.sonar.api.security.ExternalUsersProvider; import org.sonar.api.security.SecurityRealm; import org.sonar.api.security.UserDetails; +import org.sonar.api.server.http.HttpRequest; import org.sonar.server.authentication.event.AuthenticationEvent; import org.sonar.server.authentication.event.AuthenticationEvent.Source; import org.sonar.server.authentication.event.AuthenticationException; +import org.sonar.server.http.JavaxHttpRequest; import org.sonar.server.user.SecurityRealmFactory; import static java.util.Arrays.asList; @@ -53,20 +55,21 @@ public class CredentialsExternalAuthenticationTest { private static final String REALM_NAME = "realm name"; - private MapSettings settings = new MapSettings(); + private final MapSettings settings = new MapSettings(); - private SecurityRealmFactory securityRealmFactory = mock(SecurityRealmFactory.class); - private SecurityRealm realm = mock(SecurityRealm.class); - private Authenticator authenticator = mock(Authenticator.class); - private ExternalUsersProvider externalUsersProvider = mock(ExternalUsersProvider.class); - private ExternalGroupsProvider externalGroupsProvider = mock(ExternalGroupsProvider.class); + private final SecurityRealmFactory securityRealmFactory = mock(SecurityRealmFactory.class); + private final SecurityRealm realm = mock(SecurityRealm.class); + private final Authenticator authenticator = mock(Authenticator.class); + private final ExternalUsersProvider externalUsersProvider = mock(ExternalUsersProvider.class); + private final ExternalGroupsProvider externalGroupsProvider = mock(ExternalGroupsProvider.class); - private TestUserRegistrar userIdentityAuthenticator = new TestUserRegistrar(); - private AuthenticationEvent authenticationEvent = mock(AuthenticationEvent.class); + private final TestUserRegistrar userIdentityAuthenticator = new TestUserRegistrar(); + private final AuthenticationEvent authenticationEvent = mock(AuthenticationEvent.class); - private HttpServletRequest request = mock(HttpServletRequest.class); + private final HttpRequest request = new JavaxHttpRequest(mock(HttpServletRequest.class)); - private CredentialsExternalAuthentication underTest = new CredentialsExternalAuthentication(settings.asConfig(), securityRealmFactory, userIdentityAuthenticator, authenticationEvent); + private final CredentialsExternalAuthentication underTest = new CredentialsExternalAuthentication(settings.asConfig(), securityRealmFactory, userIdentityAuthenticator, + authenticationEvent); @Before public void setUp() throws Exception { @@ -184,7 +187,8 @@ public class CredentialsExternalAuthenticationTest { when(externalUsersProvider.doGetUserDetails(any(ExternalUsersProvider.Context.class))).thenReturn(null); - assertThatThrownBy(() -> underTest.authenticate(new Credentials(LOGIN, PASSWORD), request, BASIC)) + Credentials credentials = new Credentials(LOGIN, PASSWORD); + assertThatThrownBy(() -> underTest.authenticate(credentials, request, BASIC)) .hasMessage("No user details") .isInstanceOf(AuthenticationException.class) .hasFieldOrPropertyWithValue("source", Source.realm(BASIC, REALM_NAME)) @@ -200,7 +204,8 @@ public class CredentialsExternalAuthenticationTest { when(authenticator.doAuthenticate(any(Authenticator.Context.class))).thenReturn(false); - assertThatThrownBy(() -> underTest.authenticate(new Credentials(LOGIN, PASSWORD), request, BASIC)) + Credentials credentials = new Credentials(LOGIN, PASSWORD); + assertThatThrownBy(() -> underTest.authenticate(credentials, request, BASIC)) .hasMessage("Realm returned authenticate=false") .isInstanceOf(AuthenticationException.class) .hasFieldOrPropertyWithValue("source", Source.realm(BASIC, REALM_NAME)) @@ -218,7 +223,8 @@ public class CredentialsExternalAuthenticationTest { when(externalUsersProvider.doGetUserDetails(any(ExternalUsersProvider.Context.class))).thenReturn(new UserDetails()); - assertThatThrownBy(() -> underTest.authenticate(new Credentials(LOGIN, PASSWORD), request, SONARQUBE_TOKEN)) + Credentials credentials = new Credentials(LOGIN, PASSWORD); + assertThatThrownBy(() -> underTest.authenticate(credentials, request, SONARQUBE_TOKEN)) .hasMessage(expectedMessage) .isInstanceOf(AuthenticationException.class) .hasFieldOrPropertyWithValue("source", Source.realm(SONARQUBE_TOKEN, REALM_NAME)) @@ -238,7 +244,7 @@ public class CredentialsExternalAuthenticationTest { when(realm.doGetAuthenticator()).thenReturn(null); when(securityRealmFactory.getRealm()).thenReturn(realm); - assertThatThrownBy(() -> underTest.start()) + assertThatThrownBy(underTest::start) .isInstanceOf(NullPointerException.class) .hasMessage("No authenticator available"); } @@ -249,7 +255,7 @@ public class CredentialsExternalAuthenticationTest { when(realm.getUsersProvider()).thenReturn(null); when(securityRealmFactory.getRealm()).thenReturn(realm); - assertThatThrownBy(() -> underTest.start()) + assertThatThrownBy(underTest::start) .isInstanceOf(NullPointerException.class) .hasMessage("No users provider available"); } diff --git a/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifierFilterTest.java b/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifierFilterTest.java index 8cab76ac983..79a1afdf27d 100644 --- a/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifierFilterTest.java +++ b/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifierFilterTest.java @@ -23,14 +23,13 @@ import com.tngtech.java.junit.dataprovider.DataProvider; import com.tngtech.java.junit.dataprovider.DataProviderRunner; import com.tngtech.java.junit.dataprovider.UseDataProvider; import java.util.Optional; -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.sonar.api.config.Configuration; +import org.sonar.api.server.http.HttpRequest; +import org.sonar.api.server.http.HttpResponse; +import org.sonar.api.web.FilterChain; import org.sonar.server.user.ThreadLocalUserSession; import static org.mockito.ArgumentMatchers.any; @@ -43,8 +42,8 @@ import static org.mockito.Mockito.when; @RunWith(DataProviderRunner.class) public class DefaultAdminCredentialsVerifierFilterTest { - private final HttpServletRequest request = mock(HttpServletRequest.class); - private final HttpServletResponse response = mock(HttpServletResponse.class); + private final HttpRequest request = mock(HttpRequest.class); + private final HttpResponse response = mock(HttpResponse.class); private final FilterChain chain = mock(FilterChain.class); private final Configuration config = mock(Configuration.class); private final DefaultAdminCredentialsVerifier defaultAdminCredentialsVerifier = mock(DefaultAdminCredentialsVerifier.class); @@ -66,7 +65,7 @@ public class DefaultAdminCredentialsVerifierFilterTest { @Test public void verify_other_methods() { - underTest.init(mock(FilterConfig.class)); + underTest.init(); underTest.destroy(); verifyNoInteractions(request, response, chain, session); diff --git a/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/GithubWebhookAuthenticationTest.java b/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/GithubWebhookAuthenticationTest.java index b3cde7ceae6..f0bd6768b09 100644 --- a/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/GithubWebhookAuthenticationTest.java +++ b/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/GithubWebhookAuthenticationTest.java @@ -23,13 +23,13 @@ import java.io.BufferedReader; import java.io.IOException; import java.io.StringReader; import java.util.Optional; -import javax.servlet.http.HttpServletRequest; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.mockito.Mockito; import org.sonar.api.config.internal.Encryption; import org.sonar.api.config.internal.Settings; +import org.sonar.api.server.http.HttpRequest; import org.sonar.api.testfixtures.log.LogAndArguments; import org.sonar.api.testfixtures.log.LogTester; import org.sonar.api.utils.log.LoggerLevel; @@ -88,7 +88,7 @@ public class GithubWebhookAuthenticationTest { @Test public void authenticate_withComputedSignatureMatchingGithubSignature_returnsAuthentication() { - HttpServletRequest request = mockRequest(GITHUB_PAYLOAD, GITHUB_SIGNATURE); + HttpRequest request = mockRequest(GITHUB_PAYLOAD, GITHUB_SIGNATURE); Optional authentication = githubWebhookAuthentication.authenticate(request); assertThat(authentication).isPresent(); @@ -104,7 +104,7 @@ public class GithubWebhookAuthenticationTest { @Test public void authenticate_withoutGithubSignatureHeader_throws() { - HttpServletRequest request = mockRequest(GITHUB_PAYLOAD, null); + HttpRequest request = mockRequest(GITHUB_PAYLOAD, null); String expectedMessage = format(MSG_UNAUTHENTICATED_GITHUB_CALLS_DENIED, APP_ID); assertThatExceptionOfType(AuthenticationException.class) @@ -115,7 +115,7 @@ public class GithubWebhookAuthenticationTest { @Test public void authenticate_withoutBody_throws() { - HttpServletRequest request = mockRequest(null, GITHUB_SIGNATURE); + HttpRequest request = mockRequest(null, GITHUB_SIGNATURE); assertThatExceptionOfType(AuthenticationException.class) .isThrownBy(() -> githubWebhookAuthentication.authenticate(request)) @@ -125,7 +125,7 @@ public class GithubWebhookAuthenticationTest { @Test public void authenticate_withExceptionWhileReadingBody_throws() throws IOException { - HttpServletRequest request = mockRequest(GITHUB_PAYLOAD, GITHUB_SIGNATURE); + HttpRequest request = mockRequest(GITHUB_PAYLOAD, GITHUB_SIGNATURE); when(request.getReader()).thenThrow(new IOException()); assertThatExceptionOfType(AuthenticationException.class) @@ -136,7 +136,7 @@ public class GithubWebhookAuthenticationTest { @Test public void authenticate_withoutAppId_returnsEmpty() { - HttpServletRequest request = mockRequest(null, GITHUB_SIGNATURE); + HttpRequest request = mockRequest(null, GITHUB_SIGNATURE); when(request.getHeader(GITHUB_APP_ID_HEADER)).thenReturn(null); assertThat(githubWebhookAuthentication.authenticate(request)).isEmpty(); @@ -145,7 +145,7 @@ public class GithubWebhookAuthenticationTest { @Test public void authenticate_withWrongPayload_throws() { - HttpServletRequest request = mockRequest(GITHUB_PAYLOAD + "_", GITHUB_SIGNATURE); + HttpRequest request = mockRequest(GITHUB_PAYLOAD + "_", GITHUB_SIGNATURE); assertThatExceptionOfType(AuthenticationException.class) .isThrownBy(() -> githubWebhookAuthentication.authenticate(request)) @@ -155,7 +155,7 @@ public class GithubWebhookAuthenticationTest { @Test public void authenticate_withWrongSignature_throws() { - HttpServletRequest request = mockRequest(GITHUB_PAYLOAD, GITHUB_SIGNATURE + "_"); + HttpRequest request = mockRequest(GITHUB_PAYLOAD, GITHUB_SIGNATURE + "_"); assertThatExceptionOfType(AuthenticationException.class) .isThrownBy(() -> githubWebhookAuthentication.authenticate(request)) @@ -165,7 +165,7 @@ public class GithubWebhookAuthenticationTest { @Test public void authenticate_whenNoWebhookSecret_throws() { - HttpServletRequest request = mockRequest(GITHUB_PAYLOAD, GITHUB_SIGNATURE); + HttpRequest request = mockRequest(GITHUB_PAYLOAD, GITHUB_SIGNATURE); db.getDbClient().almSettingDao().update(db.getSession(), almSettingDto.setWebhookSecret(null), true); db.commit(); @@ -176,8 +176,8 @@ public class GithubWebhookAuthenticationTest { assertThat(logTester.getLogs(LoggerLevel.WARN)).extracting(LogAndArguments::getFormattedMsg).contains(expectedMessage); } - private static HttpServletRequest mockRequest(@Nullable String payload, @Nullable String gitHubSignature) { - HttpServletRequest request = mock(HttpServletRequest.class, Mockito.RETURNS_DEEP_STUBS); + private static HttpRequest mockRequest(@Nullable String payload, @Nullable String gitHubSignature) { + HttpRequest request = mock(HttpRequest.class, Mockito.RETURNS_DEEP_STUBS); try { StringReader stringReader = new StringReader(requireNonNullElse(payload, "")); BufferedReader bufferedReader = new BufferedReader(stringReader); diff --git a/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/HttpHeadersAuthenticationTest.java b/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/HttpHeadersAuthenticationTest.java index 0e1975a2f4d..500f4d9176d 100644 --- a/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/HttpHeadersAuthenticationTest.java +++ b/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/HttpHeadersAuthenticationTest.java @@ -25,13 +25,13 @@ import java.util.HashMap; import java.util.Map; import java.util.Optional; import javax.annotation.Nullable; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.sonar.api.config.internal.MapSettings; import org.sonar.api.impl.utils.AlwaysIncreasingSystem2; +import org.sonar.api.server.http.HttpRequest; +import org.sonar.api.server.http.HttpResponse; import org.sonar.api.utils.System2; import org.sonar.db.DbTester; import org.sonar.db.audit.AuditPersister; @@ -96,7 +96,7 @@ public class HttpHeadersAuthenticationTest { private final UserRegistrarImpl userIdentityAuthenticator = new UserRegistrarImpl(db.getDbClient(), new UserUpdater(mock(NewUserNotifier.class), db.getDbClient(), defaultGroupFinder, settings.asConfig(), mock(AuditPersister.class), localAuthentication), defaultGroupFinder, mock(ManagedInstanceService.class)); - private final HttpServletResponse response = mock(HttpServletResponse.class); + private final HttpResponse response = mock(HttpResponse.class); private final JwtHttpHandler jwtHttpHandler = mock(JwtHttpHandler.class); private final AuthenticationEvent authenticationEvent = mock(AuthenticationEvent.class); private final HttpHeadersAuthentication underTest = new HttpHeadersAuthentication(system2, settings.asConfig(), userIdentityAuthenticator, jwtHttpHandler, @@ -114,7 +114,7 @@ public class HttpHeadersAuthenticationTest { public void create_user_when_authenticating_new_user() { startWithSso(); setNotUserInToken(); - HttpServletRequest request = createRequest(DEFAULT_LOGIN, DEFAULT_NAME, DEFAULT_EMAIL, GROUPS); + HttpRequest request = createRequest(DEFAULT_LOGIN, DEFAULT_NAME, DEFAULT_EMAIL, GROUPS); underTest.authenticate(request, response); @@ -128,7 +128,7 @@ public class HttpHeadersAuthenticationTest { startWithSso(); setNotUserInToken(); - HttpServletRequest request = createRequest(DEFAULT_LOGIN, null, null, null); + HttpRequest request = createRequest(DEFAULT_LOGIN, null, null, null); underTest.authenticate(request, response); verifyUserInDb(DEFAULT_LOGIN, DEFAULT_LOGIN, null, sonarUsers); @@ -141,7 +141,7 @@ public class HttpHeadersAuthenticationTest { setNotUserInToken(); insertUser(newUserDto().setLogin(DEFAULT_LOGIN).setExternalLogin(DEFAULT_LOGIN).setExternalIdentityProvider("sonarqube").setName("old name").setEmail(DEFAULT_USER.getEmail()), group1); // Name, email and groups are different - HttpServletRequest request = createRequest(DEFAULT_LOGIN, DEFAULT_NAME, DEFAULT_EMAIL, GROUP2); + HttpRequest request = createRequest(DEFAULT_LOGIN, DEFAULT_NAME, DEFAULT_EMAIL, GROUP2); underTest.authenticate(request, response); @@ -155,7 +155,7 @@ public class HttpHeadersAuthenticationTest { startWithSso(); setNotUserInToken(); insertUser(DEFAULT_USER, group1); - HttpServletRequest request = createRequest(DEFAULT_LOGIN, DEFAULT_NAME, DEFAULT_EMAIL, ""); + HttpRequest request = createRequest(DEFAULT_LOGIN, DEFAULT_NAME, DEFAULT_EMAIL, ""); underTest.authenticate(request, response); @@ -172,7 +172,7 @@ public class HttpHeadersAuthenticationTest { headerValuesByName.put("X-Forwarded-Login", DEFAULT_LOGIN); headerValuesByName.put("X-Forwarded-Email", DEFAULT_USER.getEmail()); headerValuesByName.put("X-Forwarded-Groups", null); - HttpServletRequest request = createRequest(headerValuesByName); + HttpRequest request = createRequest(headerValuesByName); underTest.authenticate(request, response); @@ -185,7 +185,7 @@ public class HttpHeadersAuthenticationTest { startWithSso(); setNotUserInToken(); insertUser(DEFAULT_USER, group1, sonarUsers); - HttpServletRequest request = createRequest(DEFAULT_LOGIN, DEFAULT_NAME, DEFAULT_EMAIL, null); + HttpRequest request = createRequest(DEFAULT_LOGIN, DEFAULT_NAME, DEFAULT_EMAIL, null); underTest.authenticate(request, response); @@ -198,7 +198,7 @@ public class HttpHeadersAuthenticationTest { startWithSso(); UserDto user = insertUser(DEFAULT_USER, group1); setUserInToken(user, CLOSE_REFRESH_TIME); - HttpServletRequest request = createRequest(DEFAULT_LOGIN, "new name", "new email", GROUP2); + HttpRequest request = createRequest(DEFAULT_LOGIN, "new name", "new email", GROUP2); underTest.authenticate(request, response); @@ -214,7 +214,7 @@ public class HttpHeadersAuthenticationTest { UserDto user = insertUser(DEFAULT_USER, group1); // Refresh time was updated 6 minutes ago => more than 5 minutes setUserInToken(user, NOW - 6 * 60 * 1000L); - HttpServletRequest request = createRequest(DEFAULT_LOGIN, "new name", DEFAULT_USER.getEmail(), GROUP2); + HttpRequest request = createRequest(DEFAULT_LOGIN, "new name", DEFAULT_USER.getEmail(), GROUP2); underTest.authenticate(request, response); @@ -229,7 +229,7 @@ public class HttpHeadersAuthenticationTest { startWithSso(); UserDto user = insertUser(DEFAULT_USER, group1); setUserInToken(user, null); - HttpServletRequest request = createRequest(DEFAULT_LOGIN, "new name", DEFAULT_USER.getEmail(), GROUP2); + HttpRequest request = createRequest(DEFAULT_LOGIN, "new name", DEFAULT_USER.getEmail(), GROUP2); underTest.authenticate(request, response); @@ -246,7 +246,7 @@ public class HttpHeadersAuthenticationTest { UserDto user = insertUser(DEFAULT_USER, group1); // Refresh time was updated 6 minutes ago => less than 10 minutes ago so not updated setUserInToken(user, NOW - 6 * 60 * 1000L); - HttpServletRequest request = createRequest(DEFAULT_LOGIN, "new name", "new email", GROUP2); + HttpRequest request = createRequest(DEFAULT_LOGIN, "new name", "new email", GROUP2); underTest.authenticate(request, response); @@ -261,7 +261,7 @@ public class HttpHeadersAuthenticationTest { startWithSso(); insertUser(DEFAULT_USER, group1); setUserInToken(DEFAULT_USER, CLOSE_REFRESH_TIME); - HttpServletRequest request = createRequest("AnotherLogin", "Another name", "Another email", GROUP2); + HttpRequest request = createRequest("AnotherLogin", "Another name", "Another email", GROUP2); underTest.authenticate(request, response); @@ -278,7 +278,7 @@ public class HttpHeadersAuthenticationTest { settings.setProperty("sonar.web.sso.groupsHeader", "head-groups"); startWithSso(); setNotUserInToken(); - HttpServletRequest request = createRequest(ImmutableMap.of("head-login", DEFAULT_LOGIN, "head-name", DEFAULT_NAME, "head-email", DEFAULT_EMAIL, "head-groups", GROUPS)); + HttpRequest request = createRequest(ImmutableMap.of("head-login", DEFAULT_LOGIN, "head-name", DEFAULT_NAME, "head-email", DEFAULT_EMAIL, "head-groups", GROUPS)); underTest.authenticate(request, response); @@ -294,7 +294,7 @@ public class HttpHeadersAuthenticationTest { settings.setProperty("sonar.web.sso.groupsHeader", "Groups"); startWithSso(); setNotUserInToken(); - HttpServletRequest request = createRequest(ImmutableMap.of("login", DEFAULT_LOGIN, "name", DEFAULT_NAME, "email", DEFAULT_EMAIL, "groups", GROUPS)); + HttpRequest request = createRequest(ImmutableMap.of("login", DEFAULT_LOGIN, "name", DEFAULT_NAME, "email", DEFAULT_EMAIL, "groups", GROUPS)); underTest.authenticate(request, response); @@ -306,7 +306,7 @@ public class HttpHeadersAuthenticationTest { public void trim_groups() { startWithSso(); setNotUserInToken(); - HttpServletRequest request = createRequest(DEFAULT_LOGIN, null, null, " dev , admin "); + HttpRequest request = createRequest(DEFAULT_LOGIN, null, null, " dev , admin "); underTest.authenticate(request, response); @@ -360,14 +360,14 @@ public class HttpHeadersAuthenticationTest { } private void setUserInToken(UserDto user, @Nullable Long lastRefreshTime) { - when(jwtHttpHandler.getToken(any(HttpServletRequest.class), any(HttpServletResponse.class))) + when(jwtHttpHandler.getToken(any(HttpRequest.class), any(HttpResponse.class))) .thenReturn(Optional.of(new JwtHttpHandler.Token( user, lastRefreshTime == null ? Collections.emptyMap() : ImmutableMap.of("ssoLastRefreshTime", lastRefreshTime)))); } private void setNotUserInToken() { - when(jwtHttpHandler.getToken(any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(Optional.empty()); + when(jwtHttpHandler.getToken(any(HttpRequest.class), any(HttpResponse.class))).thenReturn(Optional.empty()); } private UserDto insertUser(UserDto user, GroupDto... groups) { @@ -377,13 +377,13 @@ public class HttpHeadersAuthenticationTest { return user; } - private static HttpServletRequest createRequest(Map headerValuesByName) { - HttpServletRequest request = mock(HttpServletRequest.class); + private static HttpRequest createRequest(Map headerValuesByName) { + HttpRequest request = mock(HttpRequest.class); setHeaders(request, headerValuesByName); return request; } - private static HttpServletRequest createRequest(String login, @Nullable String name, @Nullable String email, @Nullable String groups) { + private static HttpRequest createRequest(String login, @Nullable String name, @Nullable String email, @Nullable String groups) { Map headerValuesByName = new HashMap<>(); headerValuesByName.put("X-Forwarded-Login", login); if (name != null) { @@ -395,12 +395,12 @@ public class HttpHeadersAuthenticationTest { if (groups != null) { headerValuesByName.put("X-Forwarded-Groups", groups); } - HttpServletRequest request = mock(HttpServletRequest.class); + HttpRequest request = mock(HttpRequest.class); setHeaders(request, headerValuesByName); return request; } - private static void setHeaders(HttpServletRequest request, Map valuesByName) { + private static void setHeaders(HttpRequest request, Map valuesByName) { valuesByName.forEach((key, value) -> when(request.getHeader(key)).thenReturn(value)); when(request.getHeaderNames()).thenReturn(Collections.enumeration(valuesByName.keySet())); } @@ -434,10 +434,10 @@ public class HttpHeadersAuthenticationTest { } private void verifyTokenIsUpdated(long refreshTime) { - verify(jwtHttpHandler).generateToken(any(UserDto.class), eq(ImmutableMap.of("ssoLastRefreshTime", refreshTime)), any(HttpServletRequest.class), any(HttpServletResponse.class)); + verify(jwtHttpHandler).generateToken(any(UserDto.class), eq(ImmutableMap.of("ssoLastRefreshTime", refreshTime)), any(HttpRequest.class), any(HttpResponse.class)); } private void verifyTokenIsNotUpdated() { - verify(jwtHttpHandler, never()).generateToken(any(UserDto.class), anyMap(), any(HttpServletRequest.class), any(HttpServletResponse.class)); + verify(jwtHttpHandler, never()).generateToken(any(UserDto.class), anyMap(), any(HttpRequest.class), any(HttpResponse.class)); } } diff --git a/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/InitFilterTest.java b/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/InitFilterTest.java index cc84c22a853..89a8abc4a69 100644 --- a/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/InitFilterTest.java +++ b/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/InitFilterTest.java @@ -19,10 +19,6 @@ */ package org.sonar.server.authentication; -import javax.servlet.FilterChain; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -33,7 +29,11 @@ import org.sonar.api.server.authentication.Display; import org.sonar.api.server.authentication.IdentityProvider; import org.sonar.api.server.authentication.OAuth2IdentityProvider; import org.sonar.api.server.authentication.UnauthorizedException; +import org.sonar.api.server.http.Cookie; +import org.sonar.api.server.http.HttpRequest; +import org.sonar.api.server.http.HttpResponse; import org.sonar.api.testfixtures.log.LogTester; +import org.sonar.api.web.FilterChain; import org.sonar.server.authentication.event.AuthenticationEvent; import org.sonar.server.authentication.event.AuthenticationException; @@ -59,8 +59,8 @@ public class InitFilterTest { private BaseContextFactory baseContextFactory = mock(BaseContextFactory.class); private OAuth2ContextFactory oAuth2ContextFactory = mock(OAuth2ContextFactory.class); - private HttpServletRequest request = mock(HttpServletRequest.class); - private HttpServletResponse response = mock(HttpServletResponse.class); + private HttpRequest request = mock(HttpRequest.class); + private HttpResponse response = mock(HttpResponse.class); private FilterChain chain = mock(FilterChain.class); private FakeOAuth2IdentityProvider oAuth2IdentityProvider = new FakeOAuth2IdentityProvider(OAUTH2_PROVIDER_KEY, true); @@ -203,7 +203,7 @@ public class InitFilterTest { assertThat(cookie.getPath()).isEqualTo("/"); assertThat(cookie.isHttpOnly()).isFalse(); assertThat(cookie.getMaxAge()).isEqualTo(300); - assertThat(cookie.getSecure()).isFalse(); + assertThat(cookie.isSecure()).isFalse(); } @Test diff --git a/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/JwtCsrfVerifierTest.java b/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/JwtCsrfVerifierTest.java index 0393258502e..c5874063957 100644 --- a/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/JwtCsrfVerifierTest.java +++ b/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/JwtCsrfVerifierTest.java @@ -19,12 +19,12 @@ */ package org.sonar.server.authentication; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; +import org.sonar.api.server.http.Cookie; +import org.sonar.api.server.http.HttpRequest; +import org.sonar.api.server.http.HttpResponse; import org.sonar.server.authentication.event.AuthenticationEvent.Source; import org.sonar.server.authentication.event.AuthenticationException; @@ -45,8 +45,8 @@ public class JwtCsrfVerifierTest { private ArgumentCaptor cookieArgumentCaptor = ArgumentCaptor.forClass(Cookie.class); - private HttpServletResponse response = mock(HttpServletResponse.class); - private HttpServletRequest request = mock(HttpServletRequest.class); + private HttpResponse response = mock(HttpResponse.class); + private HttpRequest request = mock(HttpRequest.class); private JwtCsrfVerifier underTest = new JwtCsrfVerifier(); @@ -183,7 +183,7 @@ public class JwtCsrfVerifierTest { assertThat(cookie.getPath()).isEqualTo("/"); assertThat(cookie.isHttpOnly()).isFalse(); assertThat(cookie.getMaxAge()).isEqualTo(TIMEOUT); - assertThat(cookie.getSecure()).isFalse(); + assertThat(cookie.isSecure()).isFalse(); } private void mockPostJavaWsRequest() { diff --git a/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/JwtHttpHandlerTest.java b/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/JwtHttpHandlerTest.java index 06c25b91638..796b2178325 100644 --- a/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/JwtHttpHandlerTest.java +++ b/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/JwtHttpHandlerTest.java @@ -25,21 +25,22 @@ import java.util.Date; import java.util.Map; import java.util.Optional; import javax.annotation.Nullable; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.sonar.api.config.internal.MapSettings; +import org.sonar.api.server.http.Cookie; +import org.sonar.api.server.http.HttpRequest; +import org.sonar.api.server.http.HttpResponse; import org.sonar.api.utils.System2; import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.DbTester; import org.sonar.db.user.SessionTokenDto; import org.sonar.db.user.UserDto; +import org.sonar.server.http.JavaxHttpRequest; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -77,8 +78,8 @@ public class JwtHttpHandlerTest { private DbSession dbSession = db.getSession(); private ArgumentCaptor cookieArgumentCaptor = ArgumentCaptor.forClass(Cookie.class); private ArgumentCaptor jwtArgumentCaptor = ArgumentCaptor.forClass(JwtSerializer.JwtSession.class); - private HttpServletRequest request = mock(HttpServletRequest.class); - private HttpServletResponse response = mock(HttpServletResponse.class); + private HttpRequest request = mock(HttpRequest.class); + private HttpResponse response = mock(HttpResponse.class); private HttpSession httpSession = mock(HttpSession.class); private System2 system2 = spy(System2.INSTANCE); private MapSettings settings = new MapSettings(); @@ -90,7 +91,6 @@ public class JwtHttpHandlerTest { @Before public void setUp() { when(system2.now()).thenReturn(NOW); - when(request.getSession()).thenReturn(httpSession); when(jwtSerializer.encode(any(JwtSerializer.JwtSession.class))).thenReturn(JWT_TOKEN); when(jwtCsrfVerifier.generateState(eq(request), eq(response), anyInt())).thenReturn(CSRF_STATE); } @@ -274,7 +274,7 @@ public class JwtHttpHandlerTest { @Test public void validate_token_does_nothing_when_empty_value_in_jwt_cookie() { - when(request.getCookies()).thenReturn(new Cookie[] {new Cookie("JWT-SESSION", "")}); + when(request.getCookies()).thenReturn(new Cookie[] {new JavaxHttpRequest.JavaxCookie(new javax.servlet.http.Cookie("JWT-SESSION", ""))}); underTest.validateToken(request, response); @@ -401,7 +401,7 @@ public class JwtHttpHandlerTest { assertThat(cookie.getPath()).isEqualTo("/"); assertThat(cookie.isHttpOnly()).isTrue(); assertThat(cookie.getMaxAge()).isEqualTo(expiry); - assertThat(cookie.getSecure()).isFalse(); + assertThat(cookie.isSecure()).isFalse(); assertThat(cookie.getValue()).isEqualTo(value); } @@ -425,7 +425,7 @@ public class JwtHttpHandlerTest { } private Cookie addJwtCookie() { - Cookie cookie = new Cookie("JWT-SESSION", JWT_TOKEN); + Cookie cookie = new JavaxHttpRequest.JavaxCookie(new javax.servlet.http.Cookie("JWT-SESSION", JWT_TOKEN)); when(request.getCookies()).thenReturn(new Cookie[] {cookie}); return cookie; } diff --git a/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/LdapCredentialsAuthenticationTest.java b/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/LdapCredentialsAuthenticationTest.java index 53171b04110..285724f04e5 100644 --- a/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/LdapCredentialsAuthenticationTest.java +++ b/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/LdapCredentialsAuthenticationTest.java @@ -20,7 +20,6 @@ package org.sonar.server.authentication; import javax.annotation.Nullable; -import javax.servlet.http.HttpServletRequest; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -29,6 +28,7 @@ import org.mockito.junit.MockitoJUnitRunner; import org.sonar.api.config.internal.MapSettings; import org.sonar.api.server.authentication.IdentityProvider; import org.sonar.api.server.authentication.UserIdentity; +import org.sonar.api.server.http.HttpRequest; import org.sonar.auth.ldap.LdapAuthenticator; import org.sonar.auth.ldap.LdapGroupsProvider; import org.sonar.auth.ldap.LdapRealm; @@ -87,7 +87,7 @@ public class LdapCredentialsAuthenticationTest { private AuthenticationEvent authenticationEvent; @Mock - private HttpServletRequest request = mock(HttpServletRequest.class); + private HttpRequest request = mock(HttpRequest.class); @Mock private LdapAuthenticator ldapAuthenticator; diff --git a/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/OAuth2AuthenticationParametersImplTest.java b/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/OAuth2AuthenticationParametersImplTest.java index b77d4cfe08f..8bebc7b1a1d 100644 --- a/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/OAuth2AuthenticationParametersImplTest.java +++ b/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/OAuth2AuthenticationParametersImplTest.java @@ -22,13 +22,14 @@ package org.sonar.server.authentication; import com.tngtech.java.junit.dataprovider.DataProvider; import com.tngtech.java.junit.dataprovider.DataProviderRunner; import java.util.Optional; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import org.sonar.api.server.http.Cookie; +import org.sonar.api.server.http.HttpRequest; +import org.sonar.api.server.http.HttpResponse; +import org.sonar.server.http.JavaxHttpRequest; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -42,8 +43,8 @@ public class OAuth2AuthenticationParametersImplTest { private static final String AUTHENTICATION_COOKIE_NAME = "AUTH-PARAMS"; private final ArgumentCaptor cookieArgumentCaptor = ArgumentCaptor.forClass(Cookie.class); - private final HttpServletResponse response = mock(HttpServletResponse.class); - private final HttpServletRequest request = mock(HttpServletRequest.class); + private final HttpResponse response = mock(HttpResponse.class); + private final HttpRequest request = mock(HttpRequest.class); private final OAuth2AuthenticationParameters underTest = new OAuth2AuthenticationParametersImpl(); @@ -65,7 +66,7 @@ public class OAuth2AuthenticationParametersImplTest { assertThat(cookie.getPath()).isEqualTo("/"); assertThat(cookie.isHttpOnly()).isTrue(); assertThat(cookie.getMaxAge()).isEqualTo(300); - assertThat(cookie.getSecure()).isFalse(); + assertThat(cookie.isSecure()).isFalse(); } @Test @@ -105,7 +106,7 @@ public class OAuth2AuthenticationParametersImplTest { @Test public void get_return_to_parameter() { - when(request.getCookies()).thenReturn(new Cookie[] {new Cookie(AUTHENTICATION_COOKIE_NAME, "{\"return_to\":\"/admin/settings\"}")}); + when(request.getCookies()).thenReturn(new Cookie[] {wrapCookie(AUTHENTICATION_COOKIE_NAME, "{\"return_to\":\"/admin/settings\"}")}); Optional redirection = underTest.getReturnTo(request); @@ -123,7 +124,7 @@ public class OAuth2AuthenticationParametersImplTest { @Test public void get_return_to_is_empty_when_no_value() { - when(request.getCookies()).thenReturn(new Cookie[] {new Cookie(AUTHENTICATION_COOKIE_NAME, "{}")}); + when(request.getCookies()).thenReturn(new Cookie[] {wrapCookie(AUTHENTICATION_COOKIE_NAME, "{}")}); Optional redirection = underTest.getReturnTo(request); @@ -132,7 +133,7 @@ public class OAuth2AuthenticationParametersImplTest { @Test public void delete() { - when(request.getCookies()).thenReturn(new Cookie[] {new Cookie(AUTHENTICATION_COOKIE_NAME, "{\"return_to\":\"/admin/settings\"}")}); + when(request.getCookies()).thenReturn(new Cookie[] {wrapCookie(AUTHENTICATION_COOKIE_NAME, "{\"return_to\":\"/admin/settings\"}")}); underTest.delete(request, response); @@ -143,4 +144,8 @@ public class OAuth2AuthenticationParametersImplTest { assertThat(updatedCookie.getPath()).isEqualTo("/"); assertThat(updatedCookie.getMaxAge()).isZero(); } + + private JavaxHttpRequest.JavaxCookie wrapCookie(String name, String value) { + return new JavaxHttpRequest.JavaxCookie(new javax.servlet.http.Cookie(name, value)); + } } diff --git a/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/OAuth2CallbackFilterTest.java b/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/OAuth2CallbackFilterTest.java index 5b4699d557e..b00ac7b5470 100644 --- a/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/OAuth2CallbackFilterTest.java +++ b/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/OAuth2CallbackFilterTest.java @@ -19,10 +19,6 @@ */ package org.sonar.server.authentication; -import javax.servlet.FilterChain; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -31,7 +27,11 @@ import org.slf4j.event.Level; import org.sonar.api.server.authentication.OAuth2IdentityProvider; import org.sonar.api.server.authentication.UnauthorizedException; import org.sonar.api.server.authentication.UserIdentity; +import org.sonar.api.server.http.Cookie; +import org.sonar.api.server.http.HttpRequest; +import org.sonar.api.server.http.HttpResponse; import org.sonar.api.testfixtures.log.LogTester; +import org.sonar.api.web.FilterChain; import org.sonar.server.authentication.event.AuthenticationEvent; import org.sonar.server.authentication.event.AuthenticationException; import org.sonar.server.user.ThreadLocalUserSession; @@ -56,8 +56,8 @@ public class OAuth2CallbackFilterTest { private OAuth2ContextFactory oAuth2ContextFactory = mock(OAuth2ContextFactory.class); - private HttpServletRequest request = mock(HttpServletRequest.class); - private HttpServletResponse response = mock(HttpServletResponse.class); + private HttpRequest request = mock(HttpRequest.class); + private HttpResponse response = mock(HttpResponse.class); private FilterChain chain = mock(FilterChain.class); private FakeOAuth2IdentityProvider oAuth2IdentityProvider = new WellbehaveFakeOAuth2IdentityProvider(OAUTH2_PROVIDER_KEY, true, LOGIN); @@ -174,7 +174,7 @@ public class OAuth2CallbackFilterTest { assertThat(cookie.getPath()).isEqualTo("/"); assertThat(cookie.isHttpOnly()).isFalse(); assertThat(cookie.getMaxAge()).isEqualTo(300); - assertThat(cookie.getSecure()).isFalse(); + assertThat(cookie.isSecure()).isFalse(); } @Test diff --git a/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/OAuth2ContextFactoryTest.java b/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/OAuth2ContextFactoryTest.java index 507391bd9ce..db2abe7a56e 100644 --- a/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/OAuth2ContextFactoryTest.java +++ b/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/OAuth2ContextFactoryTest.java @@ -19,20 +19,20 @@ */ package org.sonar.server.authentication; -import com.google.common.collect.ImmutableSet; import java.util.Optional; -import java.util.Set; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.sonar.api.platform.Server; import org.sonar.api.server.authentication.OAuth2IdentityProvider; import org.sonar.api.server.authentication.UserIdentity; +import org.sonar.api.server.http.HttpRequest; +import org.sonar.api.server.http.HttpResponse; import org.sonar.db.user.UserDto; -import org.sonar.server.authentication.OAuth2ContextFactory.OAuthContextImpl; +import org.sonar.server.http.JavaxHttpRequest; +import org.sonar.server.http.JavaxHttpResponse; import org.sonar.server.user.TestUserSessionFactory; import org.sonar.server.user.ThreadLocalUserSession; import org.sonar.server.user.UserSession; @@ -56,25 +56,27 @@ public class OAuth2ContextFactoryTest { .setEmail("john@email.com") .build(); + private final ThreadLocalUserSession threadLocalUserSession = mock(ThreadLocalUserSession.class); + private final TestUserRegistrar userIdentityAuthenticator = new TestUserRegistrar(); + private final Server server = mock(Server.class); + private final OAuthCsrfVerifier csrfVerifier = mock(OAuthCsrfVerifier.class); + private final JwtHttpHandler jwtHttpHandler = mock(JwtHttpHandler.class); + private final TestUserSessionFactory userSessionFactory = TestUserSessionFactory.standalone(); + private final OAuth2AuthenticationParameters oAuthParameters = mock(OAuth2AuthenticationParameters.class); + private final HttpServletRequest request = mock(HttpServletRequest.class); + private final HttpServletResponse response = mock(HttpServletResponse.class); - private ThreadLocalUserSession threadLocalUserSession = mock(ThreadLocalUserSession.class); - private TestUserRegistrar userIdentityAuthenticator = new TestUserRegistrar(); - private Server server = mock(Server.class); - private OAuthCsrfVerifier csrfVerifier = mock(OAuthCsrfVerifier.class); - private JwtHttpHandler jwtHttpHandler = mock(JwtHttpHandler.class); - private TestUserSessionFactory userSessionFactory = TestUserSessionFactory.standalone(); - private OAuth2AuthenticationParameters oAuthParameters = mock(OAuth2AuthenticationParameters.class); - private HttpServletRequest request = mock(HttpServletRequest.class); - private HttpServletResponse response = mock(HttpServletResponse.class); - private HttpSession session = mock(HttpSession.class); - private OAuth2IdentityProvider identityProvider = mock(OAuth2IdentityProvider.class); + private final HttpRequest httpRequest = new JavaxHttpRequest(request); + private final HttpResponse httpResponse = new JavaxHttpResponse(response); - private OAuth2ContextFactory underTest = new OAuth2ContextFactory(threadLocalUserSession, userIdentityAuthenticator, server, csrfVerifier, jwtHttpHandler, userSessionFactory, + private final OAuth2IdentityProvider identityProvider = mock(OAuth2IdentityProvider.class); + + private final OAuth2ContextFactory underTest = new OAuth2ContextFactory(threadLocalUserSession, userIdentityAuthenticator, server, csrfVerifier, jwtHttpHandler, + userSessionFactory, oAuthParameters); @Before public void setUp() { - when(request.getSession()).thenReturn(session); when(identityProvider.getKey()).thenReturn(PROVIDER_KEY); when(identityProvider.getName()).thenReturn(PROVIDER_NAME); } @@ -85,8 +87,12 @@ public class OAuth2ContextFactoryTest { OAuth2IdentityProvider.InitContext context = newInitContext(); + assertThat(context.getHttpRequest()).isEqualTo(httpRequest); + assertThat(context.getHttpResponse()).isEqualTo(httpResponse); + assertThat(context.getRequest()).isEqualTo(request); assertThat(context.getResponse()).isEqualTo(response); + assertThat(context.getCallbackUrl()).isEqualTo("https://mydomain.com/oauth2/callback/github"); } @@ -96,7 +102,7 @@ public class OAuth2ContextFactoryTest { context.generateCsrfState(); - verify(csrfVerifier).generateState(request, response); + verify(csrfVerifier).generateState(httpRequest, httpResponse); } @Test @@ -114,8 +120,8 @@ public class OAuth2ContextFactoryTest { OAuth2IdentityProvider.CallbackContext callback = newCallbackContext(); - assertThat(callback.getRequest()).isEqualTo(request); - assertThat(callback.getResponse()).isEqualTo(response); + assertThat(callback.getHttpRequest()).isEqualTo(httpRequest); + assertThat(callback.getHttpResponse()).isEqualTo(httpResponse); assertThat(callback.getCallbackUrl()).isEqualTo("https://mydomain.com/oauth2/callback/github"); } @@ -128,7 +134,7 @@ public class OAuth2ContextFactoryTest { assertThat(userIdentityAuthenticator.isAuthenticated()).isTrue(); verify(threadLocalUserSession).set(any(UserSession.class)); ArgumentCaptor userArgumentCaptor = ArgumentCaptor.forClass(UserDto.class); - verify(jwtHttpHandler).generateToken(userArgumentCaptor.capture(), eq(request), eq(response)); + verify(jwtHttpHandler).generateToken(userArgumentCaptor.capture(), eq(httpRequest), eq(httpResponse)); assertThat(userArgumentCaptor.getValue().getExternalId()).isEqualTo(USER_IDENTITY.getProviderId()); assertThat(userArgumentCaptor.getValue().getExternalLogin()).isEqualTo(USER_IDENTITY.getProviderLogin()); assertThat(userArgumentCaptor.getValue().getExternalIdentityProvider()).isEqualTo(PROVIDER_KEY); @@ -137,7 +143,7 @@ public class OAuth2ContextFactoryTest { @Test public void redirect_to_home() throws Exception { when(server.getContextPath()).thenReturn(""); - when(oAuthParameters.getReturnTo(request)).thenReturn(Optional.empty()); + when(oAuthParameters.getReturnTo(httpRequest)).thenReturn(Optional.empty()); OAuth2IdentityProvider.CallbackContext callback = newCallbackContext(); callback.redirectToRequestedPage(); @@ -148,7 +154,7 @@ public class OAuth2ContextFactoryTest { @Test public void redirect_to_home_with_context() throws Exception { when(server.getContextPath()).thenReturn("/sonarqube"); - when(oAuthParameters.getReturnTo(request)).thenReturn(Optional.empty()); + when(oAuthParameters.getReturnTo(httpRequest)).thenReturn(Optional.empty()); OAuth2IdentityProvider.CallbackContext callback = newCallbackContext(); callback.redirectToRequestedPage(); @@ -158,7 +164,7 @@ public class OAuth2ContextFactoryTest { @Test public void redirect_to_requested_page() throws Exception { - when(oAuthParameters.getReturnTo(request)).thenReturn(Optional.of("/admin/settings")); + when(oAuthParameters.getReturnTo(httpRequest)).thenReturn(Optional.of("/admin/settings")); when(server.getContextPath()).thenReturn(""); OAuth2IdentityProvider.CallbackContext callback = newCallbackContext(); @@ -169,7 +175,7 @@ public class OAuth2ContextFactoryTest { @Test public void redirect_to_requested_page_does_not_need_context() throws Exception { - when(oAuthParameters.getReturnTo(request)).thenReturn(Optional.of("/admin/settings")); + when(oAuthParameters.getReturnTo(httpRequest)).thenReturn(Optional.of("/admin/settings")); when(server.getContextPath()).thenReturn("/other"); OAuth2IdentityProvider.CallbackContext callback = newCallbackContext(); @@ -184,26 +190,26 @@ public class OAuth2ContextFactoryTest { callback.verifyCsrfState(); - verify(csrfVerifier).verifyState(request, response, identityProvider); + verify(csrfVerifier).verifyState(httpRequest, httpResponse, identityProvider); } @Test public void delete_oauth2_parameters_during_redirection() { - when(oAuthParameters.getReturnTo(request)).thenReturn(Optional.of("/admin/settings")); + when(oAuthParameters.getReturnTo(httpRequest)).thenReturn(Optional.of("/admin/settings")); when(server.getContextPath()).thenReturn(""); OAuth2IdentityProvider.CallbackContext callback = newCallbackContext(); callback.redirectToRequestedPage(); - verify(oAuthParameters).delete(request, response); + verify(oAuthParameters).delete(httpRequest, httpResponse); } private OAuth2IdentityProvider.InitContext newInitContext() { - return underTest.newContext(request, response, identityProvider); + return underTest.newContext(httpRequest, httpResponse, identityProvider); } private OAuth2IdentityProvider.CallbackContext newCallbackContext() { - return underTest.newCallback(request, response, identityProvider); + return underTest.newCallback(httpRequest, httpResponse, identityProvider); } } diff --git a/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/OAuthCsrfVerifierTest.java b/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/OAuthCsrfVerifierTest.java index 2ddce9d2432..186794739d0 100644 --- a/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/OAuthCsrfVerifierTest.java +++ b/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/OAuthCsrfVerifierTest.java @@ -19,16 +19,17 @@ */ package org.sonar.server.authentication; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.sonar.api.platform.Server; import org.sonar.api.server.authentication.OAuth2IdentityProvider; +import org.sonar.api.server.http.Cookie; +import org.sonar.api.server.http.HttpRequest; +import org.sonar.api.server.http.HttpResponse; import org.sonar.server.authentication.event.AuthenticationEvent; import org.sonar.server.authentication.event.AuthenticationException; +import org.sonar.server.http.JavaxHttpRequest; import static org.apache.commons.codec.digest.DigestUtils.sha1Hex; import static org.apache.commons.codec.digest.DigestUtils.sha256Hex; @@ -45,8 +46,8 @@ public class OAuthCsrfVerifierTest { private OAuth2IdentityProvider identityProvider = mock(OAuth2IdentityProvider.class); private Server server = mock(Server.class); - private HttpServletResponse response = mock(HttpServletResponse.class); - private HttpServletRequest request = mock(HttpServletRequest.class); + private HttpResponse response = mock(HttpResponse.class); + private HttpRequest request = mock(HttpRequest.class); private OAuthCsrfVerifier underTest = new OAuthCsrfVerifier(); @@ -69,7 +70,7 @@ public class OAuthCsrfVerifierTest { @Test public void verify_state() { String state = "state"; - when(request.getCookies()).thenReturn(new Cookie[]{new Cookie("OAUTHSTATE", sha256Hex(state))}); + when(request.getCookies()).thenReturn(new Cookie[]{wrapCookie("OAUTHSTATE", sha256Hex(state))}); when(request.getParameter("aStateParameter")).thenReturn(state); underTest.verifyState(request, response, identityProvider, "aStateParameter"); @@ -85,7 +86,7 @@ public class OAuthCsrfVerifierTest { @Test public void verify_state_using_default_state_parameter() { String state = "state"; - when(request.getCookies()).thenReturn(new Cookie[]{new Cookie("OAUTHSTATE", sha256Hex(state))}); + when(request.getCookies()).thenReturn(new Cookie[]{wrapCookie("OAUTHSTATE", sha256Hex(state))}); when(request.getParameter("state")).thenReturn(state); underTest.verifyState(request, response, identityProvider); @@ -100,7 +101,7 @@ public class OAuthCsrfVerifierTest { @Test public void fail_with_AuthenticationException_when_state_cookie_is_not_the_same_as_state_parameter() { - when(request.getCookies()).thenReturn(new Cookie[]{new Cookie("OAUTHSTATE", sha1Hex("state"))}); + when(request.getCookies()).thenReturn(new Cookie[]{wrapCookie("OAUTHSTATE", sha1Hex("state"))}); when(request.getParameter("state")).thenReturn("other value"); assertThatThrownBy(() -> underTest.verifyState(request, response, identityProvider)) @@ -111,7 +112,7 @@ public class OAuthCsrfVerifierTest { @Test public void fail_with_AuthenticationException_when_state_cookie_is_null() { - when(request.getCookies()).thenReturn(new Cookie[]{new Cookie("OAUTHSTATE", null)}); + when(request.getCookies()).thenReturn(new Cookie[]{wrapCookie("OAUTHSTATE", null)}); when(request.getParameter("state")).thenReturn("state"); assertThatThrownBy(() -> underTest.verifyState(request, response, identityProvider)) @@ -122,7 +123,7 @@ public class OAuthCsrfVerifierTest { @Test public void fail_with_AuthenticationException_when_state_parameter_is_empty() { - when(request.getCookies()).thenReturn(new Cookie[]{new Cookie("OAUTHSTATE", sha1Hex("state"))}); + when(request.getCookies()).thenReturn(new Cookie[]{wrapCookie("OAUTHSTATE", sha1Hex("state"))}); when(request.getParameter("state")).thenReturn(""); assertThatThrownBy(() -> underTest.verifyState(request, response, identityProvider)) @@ -147,6 +148,10 @@ public class OAuthCsrfVerifierTest { assertThat(cookie.getPath()).isEqualTo("/"); assertThat(cookie.isHttpOnly()).isTrue(); assertThat(cookie.getMaxAge()).isEqualTo(-1); - assertThat(cookie.getSecure()).isFalse(); + assertThat(cookie.isSecure()).isFalse(); + } + + private JavaxHttpRequest.JavaxCookie wrapCookie(String name, String value) { + return new JavaxHttpRequest.JavaxCookie(new javax.servlet.http.Cookie(name, value)); } } diff --git a/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/RequestAuthenticatorImplTest.java b/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/RequestAuthenticatorImplTest.java index 1e5e79dc301..e4dcfb40ce3 100644 --- a/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/RequestAuthenticatorImplTest.java +++ b/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/RequestAuthenticatorImplTest.java @@ -20,10 +20,10 @@ package org.sonar.server.authentication; import java.util.Optional; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import org.junit.Before; import org.junit.Test; +import org.sonar.api.server.http.HttpRequest; +import org.sonar.api.server.http.HttpResponse; import org.sonar.db.user.UserDto; import org.sonar.db.user.UserTokenDto; import org.sonar.server.tester.AnonymousMockUserSession; @@ -47,8 +47,8 @@ public class RequestAuthenticatorImplTest { private static final UserDto A_USER = newUserDto(); private static final UserTokenDto A_USER_TOKEN = mockUserTokenDto(A_USER); - private final HttpServletRequest request = mock(HttpServletRequest.class); - private final HttpServletResponse response = mock(HttpServletResponse.class); + private final HttpRequest request = mock(HttpRequest.class); + private final HttpResponse response = mock(HttpResponse.class); private final JwtHttpHandler jwtHttpHandler = mock(JwtHttpHandler.class); private final BasicAuthentication basicAuthentication = mock(BasicAuthentication.class); private final UserTokenAuthentication userTokenAuthentication = mock(UserTokenAuthentication.class); diff --git a/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/ResetPasswordFilterTest.java b/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/ResetPasswordFilterTest.java index 05ce7bdbfdd..3e9b78e6375 100644 --- a/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/ResetPasswordFilterTest.java +++ b/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/ResetPasswordFilterTest.java @@ -22,13 +22,12 @@ package org.sonar.server.authentication; import com.tngtech.java.junit.dataprovider.DataProvider; import com.tngtech.java.junit.dataprovider.DataProviderRunner; import com.tngtech.java.junit.dataprovider.UseDataProvider; -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.sonar.api.server.http.HttpRequest; +import org.sonar.api.server.http.HttpResponse; +import org.sonar.api.web.FilterChain; import org.sonar.server.user.ThreadLocalUserSession; import static org.mockito.ArgumentMatchers.any; @@ -41,8 +40,8 @@ import static org.mockito.Mockito.when; @RunWith(DataProviderRunner.class) public class ResetPasswordFilterTest { - private final HttpServletRequest request = mock(HttpServletRequest.class); - private final HttpServletResponse response = mock(HttpServletResponse.class); + private final HttpRequest request = mock(HttpRequest.class); + private final HttpResponse response = mock(HttpResponse.class); private final FilterChain chain = mock(FilterChain.class); private final ThreadLocalUserSession session = mock(ThreadLocalUserSession.class); @@ -62,7 +61,7 @@ public class ResetPasswordFilterTest { @Test public void verify_other_methods() { - underTest.init(mock(FilterConfig.class)); + underTest.init(); underTest.destroy(); verifyNoInteractions(request, response, chain, session); diff --git a/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/SamlValidationCspHeadersTest.java b/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/SamlValidationCspHeadersTest.java index 2ca3c6a6ab6..8d504e2478a 100644 --- a/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/SamlValidationCspHeadersTest.java +++ b/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/SamlValidationCspHeadersTest.java @@ -19,8 +19,8 @@ */ package org.sonar.server.authentication; -import javax.servlet.http.HttpServletResponse; import org.junit.Test; +import org.sonar.api.server.http.HttpResponse; import static org.mockito.ArgumentMatchers.contains; import static org.mockito.ArgumentMatchers.eq; @@ -32,7 +32,7 @@ public class SamlValidationCspHeadersTest { @Test public void addCspHeadersWithNonceToResponse_whenCalled_shouldAddNonceToCspHeaders() { - HttpServletResponse httpServletResponse = mock(HttpServletResponse.class); + HttpResponse httpServletResponse = mock(HttpResponse.class); String nonce = addCspHeadersWithNonceToResponse(httpServletResponse); diff --git a/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/SamlValidationRedirectionFilterTest.java b/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/SamlValidationRedirectionFilterTest.java index bfa90a614ee..13b930fe938 100644 --- a/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/SamlValidationRedirectionFilterTest.java +++ b/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/SamlValidationRedirectionFilterTest.java @@ -25,17 +25,15 @@ import com.tngtech.java.junit.dataprovider.UseDataProvider; import java.io.IOException; import java.io.PrintWriter; import java.util.List; -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; -import org.mockito.internal.verification.VerificationModeFactory; import org.sonar.api.platform.Server; +import org.sonar.api.server.http.HttpRequest; +import org.sonar.api.server.http.HttpResponse; +import org.sonar.api.web.FilterChain; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertThrows; @@ -60,7 +58,7 @@ public class SamlValidationRedirectionFilterTest { Server server = mock(Server.class); doReturn("contextPath").when(server).getContextPath(); underTest = new SamlValidationRedirectionFilter(server); - underTest.init(mock(FilterConfig.class)); + underTest.init(); } @@ -73,9 +71,9 @@ public class SamlValidationRedirectionFilterTest { } @Test - public void do_filter_validation_relay_state_with_csrfToken() throws ServletException, IOException { - HttpServletRequest servletRequest = mock(HttpServletRequest.class); - HttpServletResponse servletResponse = mock(HttpServletResponse.class); + public void do_filter_validation_relay_state_with_csrfToken() throws IOException { + HttpRequest servletRequest = mock(HttpRequest.class); + HttpResponse servletResponse = mock(HttpResponse.class); FilterChain filterChain = mock(FilterChain.class); String validSample = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; @@ -96,9 +94,9 @@ public class SamlValidationRedirectionFilterTest { } @Test - public void do_filter_validation_relay_state_with_malicious_csrfToken() throws ServletException, IOException { - HttpServletRequest servletRequest = mock(HttpServletRequest.class); - HttpServletResponse servletResponse = mock(HttpServletResponse.class); + public void do_filter_validation_relay_state_with_malicious_csrfToken() throws IOException { + HttpRequest servletRequest = mock(HttpRequest.class); + HttpResponse servletResponse = mock(HttpResponse.class); FilterChain filterChain = mock(FilterChain.class); String validSample = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; @@ -121,9 +119,9 @@ public class SamlValidationRedirectionFilterTest { } @Test - public void do_filter_validation_wrong_SAML_response() throws ServletException, IOException { - HttpServletRequest servletRequest = mock(HttpServletRequest.class); - HttpServletResponse servletResponse = mock(HttpServletResponse.class); + public void do_filter_validation_wrong_SAML_response() throws IOException { + HttpRequest servletRequest = mock(HttpRequest.class); + HttpResponse servletResponse = mock(HttpResponse.class); FilterChain filterChain = mock(FilterChain.class); String maliciousSaml = "test\" underTest.loginSuccess(null, "login", Source.sso())) + Source sso = Source.sso(); + assertThatThrownBy(() -> underTest.loginSuccess(null, "login", sso)) .isInstanceOf(NullPointerException.class) .hasMessage("request can't be null"); } @@ -69,14 +70,14 @@ public class AuthenticationEventImplTest { public void login_success_fails_with_NPE_if_source_is_null() { logTester.setLevel(LoggerLevel.INFO); - assertThatThrownBy(() -> underTest.loginSuccess(mock(HttpServletRequest.class), "login", null)) + assertThatThrownBy(() -> underTest.loginSuccess(mock(HttpRequest.class), "login", null)) .isInstanceOf(NullPointerException.class) .hasMessage("source can't be null"); } @Test public void login_success_does_not_interact_with_request_if_log_level_is_above_DEBUG() { - HttpServletRequest request = mock(HttpServletRequest.class); + HttpRequest request = mock(HttpRequest.class); logTester.setLevel(LoggerLevel.INFO); underTest.loginSuccess(request, "login", Source.sso()); @@ -115,7 +116,7 @@ public class AuthenticationEventImplTest { @Test public void login_success_logs_X_Forwarded_For_header_from_request() { - HttpServletRequest request = mockRequest("1.2.3.4", asList("2.3.4.5")); + HttpRequest request = mockRequest("1.2.3.4", asList("2.3.4.5")); underTest.loginSuccess(request, "foo", Source.realm(Method.EXTERNAL, "bar")); verifyLog("login success [method|EXTERNAL][provider|REALM|bar][IP|1.2.3.4|2.3.4.5][login|foo]"); @@ -123,7 +124,7 @@ public class AuthenticationEventImplTest { @Test public void login_success_logs_X_Forwarded_For_header_from_request_and_supports_multiple_headers() { - HttpServletRequest request = mockRequest("1.2.3.4", asList("2.3.4.5", "6.5.4.3"), asList("9.5.6.7"), asList("6.3.2.4")); + HttpRequest request = mockRequest("1.2.3.4", asList("2.3.4.5", "6.5.4.3"), asList("9.5.6.7"), asList("6.3.2.4")); underTest.loginSuccess(request, "foo", Source.realm(Method.EXTERNAL, "bar")); verifyLog("login success [method|EXTERNAL][provider|REALM|bar][IP|1.2.3.4|2.3.4.5,6.5.4.3,9.5.6.7,6.3.2.4][login|foo]"); @@ -133,7 +134,8 @@ public class AuthenticationEventImplTest { public void login_failure_fails_with_NPE_if_request_is_null() { logTester.setLevel(LoggerLevel.INFO); - assertThatThrownBy(() -> underTest.loginFailure(null, newBuilder().setSource(Source.sso()).build())) + AuthenticationException exception = newBuilder().setSource(Source.sso()).build(); + assertThatThrownBy(() -> underTest.loginFailure(null, exception)) .isInstanceOf(NullPointerException.class) .hasMessage("request can't be null"); } @@ -142,14 +144,14 @@ public class AuthenticationEventImplTest { public void login_failure_fails_with_NPE_if_AuthenticationException_is_null() { logTester.setLevel(LoggerLevel.INFO); - assertThatThrownBy(() -> underTest.loginFailure(mock(HttpServletRequest.class), null)) + assertThatThrownBy(() -> underTest.loginFailure(mock(HttpRequest.class), null)) .isInstanceOf(NullPointerException.class) .hasMessage("AuthenticationException can't be null"); } @Test public void login_failure_does_not_interact_with_arguments_if_log_level_is_above_DEBUG() { - HttpServletRequest request = mock(HttpServletRequest.class); + HttpRequest request = mock(HttpRequest.class); AuthenticationException exception = mock(AuthenticationException.class); logTester.setLevel(LoggerLevel.INFO); @@ -218,7 +220,7 @@ public class AuthenticationEventImplTest { .setMessage("Hop la!") .setLogin("foo") .build(); - HttpServletRequest request = mockRequest("1.2.3.4", asList("2.3.4.5")); + HttpRequest request = mockRequest("1.2.3.4", asList("2.3.4.5")); underTest.loginFailure(request, exception); verifyLog("login failure [cause|Hop la!][method|EXTERNAL][provider|REALM|bar][IP|1.2.3.4|2.3.4.5][login|foo]"); @@ -231,7 +233,7 @@ public class AuthenticationEventImplTest { .setMessage("Boom!") .setLogin("foo") .build(); - HttpServletRequest request = mockRequest("1.2.3.4", asList("2.3.4.5", "6.5.4.3"), asList("9.5.6.7"), asList("6.3.2.4")); + HttpRequest request = mockRequest("1.2.3.4", asList("2.3.4.5", "6.5.4.3"), asList("9.5.6.7"), asList("6.3.2.4")); underTest.loginFailure(request, exception); verifyLog("login failure [cause|Boom!][method|EXTERNAL][provider|REALM|bar][IP|1.2.3.4|2.3.4.5,6.5.4.3,9.5.6.7,6.3.2.4][login|foo]"); @@ -248,7 +250,7 @@ public class AuthenticationEventImplTest { @Test public void logout_success_does_not_interact_with_request_if_log_level_is_above_DEBUG() { - HttpServletRequest request = mock(HttpServletRequest.class); + HttpRequest request = mock(HttpRequest.class); logTester.setLevel(LoggerLevel.INFO); underTest.logoutSuccess(request, "foo"); @@ -279,7 +281,7 @@ public class AuthenticationEventImplTest { @Test public void logout_success_logs_X_Forwarded_For_header_from_request() { - HttpServletRequest request = mockRequest("1.2.3.4", asList("2.3.4.5")); + HttpRequest request = mockRequest("1.2.3.4", asList("2.3.4.5")); underTest.logoutSuccess(request, "foo"); verifyLog("logout success [IP|1.2.3.4|2.3.4.5][login|foo]"); @@ -287,7 +289,7 @@ public class AuthenticationEventImplTest { @Test public void logout_success_logs_X_Forwarded_For_header_from_request_and_supports_multiple_headers() { - HttpServletRequest request = mockRequest("1.2.3.4", asList("2.3.4.5", "6.5.4.3"), asList("9.5.6.7"), asList("6.3.2.4")); + HttpRequest request = mockRequest("1.2.3.4", asList("2.3.4.5", "6.5.4.3"), asList("9.5.6.7"), asList("6.3.2.4")); underTest.logoutSuccess(request, "foo"); verifyLog("logout success [IP|1.2.3.4|2.3.4.5,6.5.4.3,9.5.6.7,6.3.2.4][login|foo]"); @@ -306,14 +308,14 @@ public class AuthenticationEventImplTest { public void login_fails_with_NPE_if_error_message_is_null() { logTester.setLevel(LoggerLevel.INFO); - assertThatThrownBy(() -> underTest.logoutFailure(mock(HttpServletRequest.class), null)) + assertThatThrownBy(() -> underTest.logoutFailure(mock(HttpRequest.class), null)) .isInstanceOf(NullPointerException.class) .hasMessage("error message can't be null"); } @Test public void logout_does_not_interact_with_request_if_log_level_is_above_DEBUG() { - HttpServletRequest request = mock(HttpServletRequest.class); + HttpRequest request = mock(HttpRequest.class); logTester.setLevel(LoggerLevel.INFO); underTest.logoutFailure(request, "bad csrf"); @@ -337,7 +339,7 @@ public class AuthenticationEventImplTest { @Test public void logout_logs_X_Forwarded_For_header_from_request() { - HttpServletRequest request = mockRequest("1.2.3.4", asList("2.3.4.5")); + HttpRequest request = mockRequest("1.2.3.4", asList("2.3.4.5")); underTest.logoutFailure(request, "bad token"); verifyLog("logout failure [error|bad token][IP|1.2.3.4|2.3.4.5]"); @@ -345,7 +347,7 @@ public class AuthenticationEventImplTest { @Test public void logout_logs_X_Forwarded_For_header_from_request_and_supports_multiple_headers() { - HttpServletRequest request = mockRequest("1.2.3.4", asList("2.3.4.5", "6.5.4.3"), asList("9.5.6.7"), asList("6.3.2.4")); + HttpRequest request = mockRequest("1.2.3.4", asList("2.3.4.5", "6.5.4.3"), asList("9.5.6.7"), asList("6.3.2.4")); underTest.logoutFailure(request, "bad token"); verifyLog("logout failure [error|bad token][IP|1.2.3.4|2.3.4.5,6.5.4.3,9.5.6.7,6.3.2.4]"); @@ -357,12 +359,12 @@ public class AuthenticationEventImplTest { .containsOnly(expected); } - private static HttpServletRequest mockRequest() { + private static HttpRequest mockRequest() { return mockRequest(""); } - private static HttpServletRequest mockRequest(String remoteAddr, List... remoteIps) { - HttpServletRequest res = mock(HttpServletRequest.class); + private static HttpRequest mockRequest(String remoteAddr, List... remoteIps) { + HttpRequest res = mock(HttpRequest.class); when(res.getRemoteAddr()).thenReturn(remoteAddr); when(res.getHeaders("X-Forwarded-For")) .thenReturn(Collections.enumeration( diff --git a/server/sonar-webserver-auth/src/test/java/org/sonar/server/usertoken/UserTokenAuthenticationTest.java b/server/sonar-webserver-auth/src/test/java/org/sonar/server/usertoken/UserTokenAuthenticationTest.java index 03b0fa8135f..597f85f7cf3 100644 --- a/server/sonar-webserver-auth/src/test/java/org/sonar/server/usertoken/UserTokenAuthenticationTest.java +++ b/server/sonar-webserver-auth/src/test/java/org/sonar/server/usertoken/UserTokenAuthenticationTest.java @@ -26,11 +26,11 @@ import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.util.Base64; import java.util.Optional; -import javax.servlet.http.HttpServletRequest; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.sonar.api.server.http.HttpRequest; import org.sonar.api.utils.System2; import org.sonar.db.DbTester; import org.sonar.db.user.UserDto; @@ -78,7 +78,7 @@ public class UserTokenAuthenticationTest { private final TokenGenerator tokenGenerator = mock(TokenGenerator.class); private final UserLastConnectionDatesUpdater userLastConnectionDatesUpdater = mock(UserLastConnectionDatesUpdater.class); private final AuthenticationEvent authenticationEvent = mock(AuthenticationEvent.class); - private final HttpServletRequest request = mock(HttpServletRequest.class); + private final HttpRequest request = mock(HttpRequest.class); private final UserTokenAuthentication underTest = new UserTokenAuthentication(tokenGenerator, db.getDbClient(), userLastConnectionDatesUpdater, authenticationEvent); @Before diff --git a/server/sonar-webserver-core/src/main/java/org/sonar/server/plugins/PluginsRiskConsentFilter.java b/server/sonar-webserver-core/src/main/java/org/sonar/server/plugins/PluginsRiskConsentFilter.java index 355f060d6c6..8d7470f2a36 100644 --- a/server/sonar-webserver-core/src/main/java/org/sonar/server/plugins/PluginsRiskConsentFilter.java +++ b/server/sonar-webserver-core/src/main/java/org/sonar/server/plugins/PluginsRiskConsentFilter.java @@ -21,25 +21,22 @@ package org.sonar.server.plugins; import java.io.IOException; import java.util.Set; -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.config.Configuration; -import org.sonar.api.web.ServletFilter; +import org.sonar.api.server.http.HttpRequest; +import org.sonar.api.server.http.HttpResponse; +import org.sonar.api.web.FilterChain; +import org.sonar.api.web.HttpFilter; +import org.sonar.api.web.UrlPattern; import org.sonar.core.extension.PluginRiskConsent; import org.sonar.server.user.ThreadLocalUserSession; -import static org.sonar.api.web.ServletFilter.UrlPattern.Builder.staticResourcePatterns; +import static org.sonar.api.web.UrlPattern.Builder.staticResourcePatterns; import static org.sonar.core.config.CorePropertyDefinitions.PLUGINS_RISK_CONSENT; import static org.sonar.core.extension.PluginRiskConsent.NOT_ACCEPTED; import static org.sonar.core.extension.PluginRiskConsent.REQUIRED; import static org.sonar.server.authentication.AuthenticationRedirection.redirectTo; -public class PluginsRiskConsentFilter extends ServletFilter { +public class PluginsRiskConsentFilter extends HttpFilter { private static final String PLUGINS_RISK_CONSENT_PATH = "/admin/plugin_risk_consent"; //NOSONAR this path will be the same in every environment @@ -57,14 +54,12 @@ public class PluginsRiskConsentFilter extends ServletFilter { } @Override - public void init(FilterConfig filterConfig) throws ServletException { + public void init() { //nothing to do } @Override - public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException { - HttpServletRequest request = (HttpServletRequest) servletRequest; - HttpServletResponse response = (HttpServletResponse) servletResponse; + public void doFilter(HttpRequest request, HttpResponse response, FilterChain chain) throws IOException{ PluginRiskConsent riskConsent = PluginRiskConsent.valueOf(config.get(PLUGINS_RISK_CONSENT).orElse(NOT_ACCEPTED.name())); if (userSession.hasSession() && userSession.isLoggedIn() diff --git a/server/sonar-webserver-core/src/test/java/org/sonar/server/plugins/PluginsRiskConsentFilterTest.java b/server/sonar-webserver-core/src/test/java/org/sonar/server/plugins/PluginsRiskConsentFilterTest.java index f7de78c4887..57528179457 100644 --- a/server/sonar-webserver-core/src/test/java/org/sonar/server/plugins/PluginsRiskConsentFilterTest.java +++ b/server/sonar-webserver-core/src/test/java/org/sonar/server/plugins/PluginsRiskConsentFilterTest.java @@ -20,15 +20,14 @@ package org.sonar.server.plugins; import java.util.Optional; -import javax.servlet.FilterChain; -import javax.servlet.ServletRequest; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; import org.sonar.api.config.Configuration; -import org.sonar.api.web.ServletFilter; +import org.sonar.api.server.http.HttpRequest; +import org.sonar.api.server.http.HttpResponse; +import org.sonar.api.web.FilterChain; +import org.sonar.api.web.UrlPattern; import org.sonar.core.extension.PluginRiskConsent; import org.sonar.server.user.ThreadLocalUserSession; @@ -44,8 +43,8 @@ public class PluginsRiskConsentFilterTest { private Configuration configuration; private ThreadLocalUserSession userSession; - private ServletRequest servletRequest; - private HttpServletResponse servletResponse; + private HttpRequest request; + private HttpResponse response; private FilterChain chain; @Before @@ -54,8 +53,8 @@ public class PluginsRiskConsentFilterTest { when(configuration.get(PLUGINS_RISK_CONSENT)).thenReturn(Optional.of(PluginRiskConsent.REQUIRED.name())); userSession = mock(ThreadLocalUserSession.class); - servletRequest = mock(HttpServletRequest.class); - servletResponse = mock(HttpServletResponse.class); + request = mock(HttpRequest.class); + response = mock(HttpResponse.class); chain = mock(FilterChain.class); } @@ -65,9 +64,9 @@ public class PluginsRiskConsentFilterTest { when(userSession.hasSession()).thenReturn(true); - consentFilter.doFilter(servletRequest, servletResponse, chain); + consentFilter.doFilter(request, response, chain); - verify(servletResponse, times(0)).sendRedirect(Mockito.anyString()); + verify(response, times(0)).sendRedirect(Mockito.anyString()); } @Test @@ -77,9 +76,9 @@ public class PluginsRiskConsentFilterTest { when(userSession.hasSession()).thenReturn(true); when(userSession.isLoggedIn()).thenReturn(false); - consentFilter.doFilter(servletRequest, servletResponse, chain); + consentFilter.doFilter(request, response, chain); - verify(servletResponse, times(0)).sendRedirect(Mockito.anyString()); + verify(response, times(0)).sendRedirect(Mockito.anyString()); } @Test @@ -90,9 +89,9 @@ public class PluginsRiskConsentFilterTest { when(userSession.isLoggedIn()).thenReturn(false); when(configuration.get(PLUGINS_RISK_CONSENT)).thenReturn(Optional.of(PluginRiskConsent.REQUIRED.name())); - consentFilter.doFilter(servletRequest, servletResponse, chain); + consentFilter.doFilter(request, response, chain); - verify(servletResponse, times(0)).sendRedirect(Mockito.anyString()); + verify(response, times(0)).sendRedirect(Mockito.anyString()); } @Test @@ -103,9 +102,9 @@ public class PluginsRiskConsentFilterTest { when(userSession.isLoggedIn()).thenReturn(false); when(configuration.get(PLUGINS_RISK_CONSENT)).thenReturn(Optional.of(PluginRiskConsent.ACCEPTED.name())); - consentFilter.doFilter(servletRequest, servletResponse, chain); + consentFilter.doFilter(request, response, chain); - verify(servletResponse, times(0)).sendRedirect(Mockito.anyString()); + verify(response, times(0)).sendRedirect(Mockito.anyString()); } @Test @@ -116,9 +115,9 @@ public class PluginsRiskConsentFilterTest { when(userSession.isLoggedIn()).thenReturn(true); when(userSession.isSystemAdministrator()).thenReturn(false); - consentFilter.doFilter(servletRequest, servletResponse, chain); + consentFilter.doFilter(request, response, chain); - verify(servletResponse, times(0)).sendRedirect(Mockito.anyString()); + verify(response, times(0)).sendRedirect(Mockito.anyString()); } @Test @@ -130,9 +129,9 @@ public class PluginsRiskConsentFilterTest { when(userSession.isSystemAdministrator()).thenReturn(false); when(configuration.get(PLUGINS_RISK_CONSENT)).thenReturn(Optional.of(PluginRiskConsent.REQUIRED.name())); - consentFilter.doFilter(servletRequest, servletResponse, chain); + consentFilter.doFilter(request, response, chain); - verify(servletResponse, times(0)).sendRedirect(Mockito.anyString()); + verify(response, times(0)).sendRedirect(Mockito.anyString()); } @Test @@ -143,9 +142,9 @@ public class PluginsRiskConsentFilterTest { when(userSession.isLoggedIn()).thenReturn(true); when(userSession.isSystemAdministrator()).thenReturn(true); - consentFilter.doFilter(servletRequest, servletResponse, chain); + consentFilter.doFilter(request, response, chain); - verify(servletResponse, times(1)).sendRedirect(Mockito.anyString()); + verify(response, times(1)).sendRedirect(Mockito.anyString()); } @Test @@ -157,16 +156,16 @@ public class PluginsRiskConsentFilterTest { when(userSession.isSystemAdministrator()).thenReturn(true); when(configuration.get(PLUGINS_RISK_CONSENT)).thenReturn(Optional.of(PluginRiskConsent.ACCEPTED.name())); - consentFilter.doFilter(servletRequest, servletResponse, chain); + consentFilter.doFilter(request, response, chain); - verify(servletResponse, times(0)).sendRedirect(Mockito.anyString()); + verify(response, times(0)).sendRedirect(Mockito.anyString()); } @Test public void doGetPattern_excludesNotEmpty() { PluginsRiskConsentFilter consentFilter = new PluginsRiskConsentFilter(configuration, userSession); - ServletFilter.UrlPattern urlPattern = consentFilter.doGetPattern(); + UrlPattern urlPattern = consentFilter.doGetPattern(); assertThat(urlPattern.getExclusions()).isNotEmpty(); diff --git a/server/sonar-webserver-pushapi/src/testFixtures/java/org/sonar/server/pushapi/DumbPushResponse.java b/server/sonar-webserver-pushapi/src/testFixtures/java/org/sonar/server/pushapi/DumbPushResponse.java index 792e919170d..8e1f1397b1a 100644 --- a/server/sonar-webserver-pushapi/src/testFixtures/java/org/sonar/server/pushapi/DumbPushResponse.java +++ b/server/sonar-webserver-pushapi/src/testFixtures/java/org/sonar/server/pushapi/DumbPushResponse.java @@ -32,18 +32,27 @@ import java.util.Map; import javax.annotation.CheckForNull; import javax.servlet.http.HttpServletResponse; import org.apache.commons.io.IOUtils; +import org.sonar.api.server.http.HttpResponse; import org.sonar.api.server.ws.Response; import org.sonar.api.utils.text.JsonWriter; import org.sonar.api.utils.text.XmlWriter; +import org.sonar.server.http.JavaxHttpResponse; import org.sonar.server.ws.ServletResponse; import org.sonar.server.ws.TestableResponse; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; public class DumbPushResponse extends ServletResponse implements TestableResponse { public DumbPushResponse() { - super(mock(HttpServletResponse.class)); + super(initMock()); + } + + private static HttpResponse initMock() { + JavaxHttpResponse mock = mock(JavaxHttpResponse.class); + when(mock.getDelegate()).thenReturn(mock(HttpServletResponse.class)); + return mock; } private DumbPushResponse.InMemoryStream stream; diff --git a/server/sonar-webserver-pushapi/src/testFixtures/java/org/sonar/server/pushapi/TestPushRequest.java b/server/sonar-webserver-pushapi/src/testFixtures/java/org/sonar/server/pushapi/TestPushRequest.java index 6dab22ea152..83cf2280a05 100644 --- a/server/sonar-webserver-pushapi/src/testFixtures/java/org/sonar/server/pushapi/TestPushRequest.java +++ b/server/sonar-webserver-pushapi/src/testFixtures/java/org/sonar/server/pushapi/TestPushRequest.java @@ -41,6 +41,7 @@ import com.google.common.base.Throwables; import java.util.Map; import java.util.Optional; import javax.servlet.AsyncContext; +import org.sonar.server.http.JavaxHttpRequest; import org.sonar.server.ws.ServletRequest; import org.sonar.server.ws.TestRequest; import org.sonar.server.ws.TestResponse; @@ -52,7 +53,7 @@ public class TestPushRequest extends ServletRequest { private TestRequest testRequest = new TestRequest(); public TestPushRequest() { - super(null); + super(mock(JavaxHttpRequest.class)); } @Override diff --git a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/authentication/ws/LoginActionIT.java b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/authentication/ws/LoginActionIT.java index 364c0b4f25a..e9f7ce8169d 100644 --- a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/authentication/ws/LoginActionIT.java +++ b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/authentication/ws/LoginActionIT.java @@ -19,14 +19,14 @@ */ package org.sonar.server.authentication.ws; -import javax.servlet.FilterChain; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import org.junit.Before; import org.junit.Rule; import org.junit.Test; +import org.sonar.api.server.http.HttpRequest; +import org.sonar.api.server.http.HttpResponse; import org.sonar.api.server.ws.WebService; import org.sonar.api.utils.System2; +import org.sonar.api.web.FilterChain; import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.DbTester; @@ -60,23 +60,23 @@ public class LoginActionIT { @Rule public DbTester dbTester = DbTester.create(System2.INSTANCE); - private DbClient dbClient = dbTester.getDbClient(); + private final DbClient dbClient = dbTester.getDbClient(); - private DbSession dbSession = dbTester.getSession(); + private final DbSession dbSession = dbTester.getSession(); - private ThreadLocalUserSession threadLocalUserSession = new ThreadLocalUserSession(); + private final ThreadLocalUserSession threadLocalUserSession = new ThreadLocalUserSession(); - private HttpServletRequest request = mock(HttpServletRequest.class); - private HttpServletResponse response = mock(HttpServletResponse.class); - private FilterChain chain = mock(FilterChain.class); + private final HttpRequest request = mock(HttpRequest.class); + private final HttpResponse response = mock(HttpResponse.class); + private final FilterChain chain = mock(FilterChain.class); - private CredentialsAuthentication credentialsAuthentication = mock(CredentialsAuthentication.class); - private JwtHttpHandler jwtHttpHandler = mock(JwtHttpHandler.class); - private AuthenticationEvent authenticationEvent = mock(AuthenticationEvent.class); - private TestUserSessionFactory userSessionFactory = TestUserSessionFactory.standalone(); + private final CredentialsAuthentication credentialsAuthentication = mock(CredentialsAuthentication.class); + private final JwtHttpHandler jwtHttpHandler = mock(JwtHttpHandler.class); + private final AuthenticationEvent authenticationEvent = mock(AuthenticationEvent.class); + private final TestUserSessionFactory userSessionFactory = TestUserSessionFactory.standalone(); - private UserDto user = UserTesting.newUserDto().setLogin(LOGIN); - private LoginAction underTest = new LoginAction(credentialsAuthentication, jwtHttpHandler, threadLocalUserSession, authenticationEvent, userSessionFactory); + private final UserDto user = UserTesting.newUserDto().setLogin(LOGIN); + private final LoginAction underTest = new LoginAction(credentialsAuthentication, jwtHttpHandler, threadLocalUserSession, authenticationEvent, userSessionFactory); @Before public void setUp() { @@ -131,7 +131,7 @@ public class LoginActionIT { } @Test - public void return_authorized_code_when_unauthorized_exception_is_thrown() throws Exception { + public void return_authorized_code_when_unauthorized_exception_is_thrown() { doThrow(new UnauthorizedException("error !")).when(credentialsAuthentication).authenticate(new Credentials(LOGIN, PASSWORD), request, FORM); executeRequest(LOGIN, PASSWORD); @@ -142,28 +142,28 @@ public class LoginActionIT { } @Test - public void return_unauthorized_code_when_no_login() throws Exception { + public void return_unauthorized_code_when_no_login() { executeRequest(null, PASSWORD); verify(response).setStatus(401); verify(authenticationEvent).loginFailure(eq(request), any(AuthenticationException.class)); } @Test - public void return_unauthorized_code_when_empty_login() throws Exception { + public void return_unauthorized_code_when_empty_login() { executeRequest("", PASSWORD); verify(response).setStatus(401); verify(authenticationEvent).loginFailure(eq(request), any(AuthenticationException.class)); } @Test - public void return_unauthorized_code_when_no_password() throws Exception { + public void return_unauthorized_code_when_no_password() { executeRequest(LOGIN, null); verify(response).setStatus(401); verify(authenticationEvent).loginFailure(eq(request), any(AuthenticationException.class)); } @Test - public void return_unauthorized_code_when_empty_password() throws Exception { + public void return_unauthorized_code_when_empty_password() { executeRequest(LOGIN, ""); verify(response).setStatus(401); verify(authenticationEvent).loginFailure(eq(request), any(AuthenticationException.class)); diff --git a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/user/ws/ChangePasswordActionIT.java b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/user/ws/ChangePasswordActionIT.java index a1fdd23020d..83b30b34dba 100644 --- a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/user/ws/ChangePasswordActionIT.java +++ b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/user/ws/ChangePasswordActionIT.java @@ -22,17 +22,17 @@ package org.sonar.server.user.ws; import java.io.IOException; import java.util.Optional; import javax.annotation.Nullable; -import javax.servlet.FilterChain; import javax.servlet.ServletOutputStream; import javax.servlet.WriteListener; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.sonar.api.config.internal.MapSettings; +import org.sonar.api.server.http.HttpRequest; +import org.sonar.api.server.http.HttpResponse; import org.sonar.api.server.ws.WebService; +import org.sonar.api.web.FilterChain; import org.sonar.db.DbSession; import org.sonar.db.DbTester; import org.sonar.db.audit.NoOpAuditPersister; @@ -75,8 +75,8 @@ public class ChangePasswordActionIT { private final ArgumentCaptor userDtoCaptor = ArgumentCaptor.forClass(UserDto.class); - private final HttpServletRequest request = mock(HttpServletRequest.class); - private final HttpServletResponse response = mock(HttpServletResponse.class); + private final HttpRequest request = mock(HttpRequest.class); + private final HttpResponse response = mock(HttpResponse.class); private final FilterChain chain = mock(FilterChain.class); private final MapSettings settings = new MapSettings().setProperty("sonar.internal.pbkdf2.iterations", "1"); @@ -88,7 +88,7 @@ public class ChangePasswordActionIT { private final JwtHttpHandler jwtHttpHandler = mock(JwtHttpHandler.class); - private final ChangePasswordAction changePasswordAction = new ChangePasswordAction(db.getDbClient(), userUpdater, userSessionRule, localAuthentication, jwtHttpHandler); + private final ChangePasswordAction underTest = new ChangePasswordAction(db.getDbClient(), userUpdater, userSessionRule, localAuthentication, jwtHttpHandler); private ServletOutputStream responseOutputStream; @Before @@ -147,7 +147,8 @@ public class ChangePasswordActionIT { userSessionRule.logIn(user.getLogin()); UserTestData anotherLocalUser = createLocalUser(); - assertThatThrownBy(() -> executeTest(anotherLocalUser.getLogin(), "I dunno", NEW_PASSWORD)) + String anotherLocalUserLogin = anotherLocalUser.getLogin(); + assertThatThrownBy(() -> executeTest(anotherLocalUserLogin, "I dunno", NEW_PASSWORD)) .isInstanceOf(ForbiddenException.class); verifyNoInteractions(jwtHttpHandler); assertThat(findSessionTokenDto(db.getSession(), user.getSessionTokenUuid())).isPresent(); @@ -184,9 +185,10 @@ public class ChangePasswordActionIT { UserDto user = db.users().insertUser(u -> u.setActive(false)); userSessionRule.logIn(user); - assertThatThrownBy(() -> executeTest(user.getLogin(), null, "polop")) + String userLogin = user.getLogin(); + assertThatThrownBy(() -> executeTest(userLogin, null, "polop")) .isInstanceOf(NotFoundException.class) - .hasMessage(format("User with login '%s' has not been found", user.getLogin())); + .hasMessage(format("User with login '%s' has not been found", userLogin)); verifyNoInteractions(jwtHttpHandler); } @@ -218,7 +220,6 @@ public class ChangePasswordActionIT { UserTestData user = createLocalUser(); userSessionRule.logIn(user.userDto()); - executeTest(user.getLogin(), OLD_PASSWORD, null); verify(response).setStatus(HTTP_BAD_REQUEST); assertThat(responseOutputStream).hasToString("{\"result\":\"The 'password' parameter is missing\"}"); @@ -267,7 +268,7 @@ public class ChangePasswordActionIT { WebService.Context context = new WebService.Context(); WebService.NewController newController = context.createController(controllerKey); - changePasswordAction.define(newController); + underTest.define(newController); newController.done(); WebService.Action changePassword = context.controller(controllerKey).action("change_password"); @@ -283,7 +284,7 @@ public class ChangePasswordActionIT { when(request.getParameter(PARAM_LOGIN)).thenReturn(login); when(request.getParameter(PARAM_PREVIOUS_PASSWORD)).thenReturn(oldPassword); when(request.getParameter(PARAM_PASSWORD)).thenReturn(newPassword); - changePasswordAction.doFilter(request, response, chain); + underTest.doFilter(request, response, chain); } private UserTestData createLocalUser(String password) { diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/authentication/ws/LoginAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/authentication/ws/LoginAction.java index 3c8eb7c21e6..a1c0b780f0e 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/authentication/ws/LoginAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/authentication/ws/LoginAction.java @@ -19,14 +19,12 @@ */ package org.sonar.server.authentication.ws; -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import org.sonar.api.server.http.HttpRequest; +import org.sonar.api.server.http.HttpResponse; import org.sonar.api.server.ws.WebService; -import org.sonar.api.web.ServletFilter; +import org.sonar.api.web.FilterChain; +import org.sonar.api.web.HttpFilter; +import org.sonar.api.web.UrlPattern; import org.sonar.db.user.UserDto; import org.sonar.server.authentication.Credentials; import org.sonar.server.authentication.CredentialsAuthentication; @@ -46,7 +44,7 @@ import static org.sonar.server.authentication.event.AuthenticationEvent.Source; import static org.sonar.server.authentication.ws.AuthenticationWs.AUTHENTICATION_CONTROLLER; import static org.sonarqube.ws.client.WsRequest.Method.POST; -public class LoginAction extends ServletFilter implements AuthenticationWsAction { +public class LoginAction extends HttpFilter implements AuthenticationWsAction { private static final String LOGIN_ACTION = "login"; public static final String LOGIN_URL = "/" + AUTHENTICATION_CONTROLLER + "/" + LOGIN_ACTION; @@ -87,10 +85,7 @@ public class LoginAction extends ServletFilter implements AuthenticationWsAction } @Override - public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) { - HttpServletRequest request = (HttpServletRequest) servletRequest; - HttpServletResponse response = (HttpServletResponse) servletResponse; - + public void doFilter(HttpRequest request, HttpResponse response, FilterChain chain) { if (!request.getMethod().equals(POST.name())) { response.setStatus(HTTP_BAD_REQUEST); return; @@ -108,7 +103,7 @@ public class LoginAction extends ServletFilter implements AuthenticationWsAction } } - private UserDto authenticate(HttpServletRequest request) { + private UserDto authenticate(HttpRequest request) { String login = request.getParameter("login"); String password = request.getParameter("password"); if (isEmpty(login) || isEmpty(password)) { @@ -122,7 +117,7 @@ public class LoginAction extends ServletFilter implements AuthenticationWsAction } @Override - public void init(FilterConfig filterConfig) { + public void init() { // Nothing to do } diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/authentication/ws/LogoutAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/authentication/ws/LogoutAction.java index 32990dfa2b0..f655bfa3d7b 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/authentication/ws/LogoutAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/authentication/ws/LogoutAction.java @@ -20,14 +20,12 @@ package org.sonar.server.authentication.ws; import java.util.Optional; -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import org.sonar.api.server.http.HttpRequest; +import org.sonar.api.server.http.HttpResponse; import org.sonar.api.server.ws.WebService; -import org.sonar.api.web.ServletFilter; +import org.sonar.api.web.FilterChain; +import org.sonar.api.web.HttpFilter; +import org.sonar.api.web.UrlPattern; import org.sonar.server.authentication.JwtHttpHandler; import org.sonar.server.authentication.event.AuthenticationEvent; import org.sonar.server.authentication.event.AuthenticationException; @@ -37,7 +35,7 @@ import static java.net.HttpURLConnection.HTTP_BAD_REQUEST; import static org.sonar.server.authentication.ws.AuthenticationWs.AUTHENTICATION_CONTROLLER; import static org.sonarqube.ws.client.WsRequest.Method.POST; -public class LogoutAction extends ServletFilter implements AuthenticationWsAction { +public class LogoutAction extends HttpFilter implements AuthenticationWsAction { private static final String LOGOUT_ACTION = "logout"; public static final String LOGOUT_URL = "/" + AUTHENTICATION_CONTROLLER + "/" + LOGOUT_ACTION; @@ -65,10 +63,7 @@ public class LogoutAction extends ServletFilter implements AuthenticationWsActio } @Override - public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) { - HttpServletRequest request = (HttpServletRequest) servletRequest; - HttpServletResponse response = (HttpServletResponse) servletResponse; - + public void doFilter(HttpRequest request, HttpResponse response, FilterChain chain) { if (!request.getMethod().equals(POST.name())) { response.setStatus(HTTP_BAD_REQUEST); return; @@ -76,7 +71,7 @@ public class LogoutAction extends ServletFilter implements AuthenticationWsActio logout(request, response); } - private void logout(HttpServletRequest request, HttpServletResponse response) { + private void logout(HttpRequest request, HttpResponse response) { generateAuthenticationEvent(request, response); jwtHttpHandler.removeToken(request, response); } @@ -84,7 +79,7 @@ public class LogoutAction extends ServletFilter implements AuthenticationWsActio /** * 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) { + private void generateAuthenticationEvent(HttpRequest request, HttpResponse response) { try { Optional token = jwtHttpHandler.getToken(request, response); String userLogin = token.map(value -> value.getUserDto().getLogin()).orElse(null); @@ -95,7 +90,7 @@ public class LogoutAction extends ServletFilter implements AuthenticationWsActio } @Override - public void init(FilterConfig filterConfig) { + public void init() { // Nothing to do } diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/authentication/ws/ValidateAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/authentication/ws/ValidateAction.java index 1bc34b12997..47648970620 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/authentication/ws/ValidateAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/authentication/ws/ValidateAction.java @@ -22,16 +22,14 @@ package org.sonar.server.authentication.ws; import com.google.common.io.Resources; import java.io.IOException; import java.util.Optional; -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import org.sonar.api.config.Configuration; +import org.sonar.api.server.http.HttpRequest; +import org.sonar.api.server.http.HttpResponse; import org.sonar.api.server.ws.WebService; import org.sonar.api.utils.text.JsonWriter; -import org.sonar.api.web.ServletFilter; +import org.sonar.api.web.FilterChain; +import org.sonar.api.web.HttpFilter; +import org.sonar.api.web.UrlPattern; import org.sonar.db.user.UserDto; import org.sonar.server.authentication.BasicAuthentication; import org.sonar.server.authentication.JwtHttpHandler; @@ -43,7 +41,7 @@ import static org.sonar.api.CoreProperties.CORE_FORCE_AUTHENTICATION_DEFAULT_VAL import static org.sonar.api.CoreProperties.CORE_FORCE_AUTHENTICATION_PROPERTY; import static org.sonar.server.authentication.ws.AuthenticationWs.AUTHENTICATION_CONTROLLER; -public class ValidateAction extends ServletFilter implements AuthenticationWsAction { +public class ValidateAction extends HttpFilter implements AuthenticationWsAction { private static final String VALIDATE_ACTION = "validate"; public static final String VALIDATE_URL = "/" + AUTHENTICATION_CONTROLLER + "/" + VALIDATE_ACTION; @@ -73,10 +71,7 @@ public class ValidateAction extends ServletFilter implements AuthenticationWsAct } @Override - public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException { - HttpServletRequest request = (HttpServletRequest) servletRequest; - HttpServletResponse response = (HttpServletResponse) servletResponse; - + public void doFilter(HttpRequest request, HttpResponse response, FilterChain filterChain) throws IOException { boolean isAuthenticated = authenticate(request, response); response.setContentType(MediaTypes.JSON); @@ -87,7 +82,7 @@ public class ValidateAction extends ServletFilter implements AuthenticationWsAct } } - private boolean authenticate(HttpServletRequest request, HttpServletResponse response) { + private boolean authenticate(HttpRequest request, HttpResponse response) { try { Optional user = jwtHttpHandler.validateToken(request, response); if (user.isPresent()) { @@ -104,7 +99,7 @@ public class ValidateAction extends ServletFilter implements AuthenticationWsAct } @Override - public void init(FilterConfig filterConfig) { + public void init() { // Nothing to do } diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/saml/ws/ValidationAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/saml/ws/ValidationAction.java index c55a99f37f7..8a2b5534180 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/saml/ws/ValidationAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/saml/ws/ValidationAction.java @@ -22,15 +22,14 @@ package org.sonar.server.saml.ws; import java.io.IOException; import java.util.Arrays; import java.util.stream.Collectors; -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; -import javax.servlet.http.HttpServletResponse; +import org.sonar.api.server.http.HttpRequest; +import org.sonar.api.server.http.HttpResponse; import org.sonar.api.server.ws.WebService; -import org.sonar.api.web.ServletFilter; +import org.sonar.api.web.FilterChain; +import org.sonar.api.web.HttpFilter; +import org.sonar.api.web.UrlPattern; import org.sonar.auth.saml.SamlAuthenticator; import org.sonar.auth.saml.SamlIdentityProvider; import org.sonar.server.authentication.AuthenticationError; @@ -39,12 +38,13 @@ import org.sonar.server.authentication.OAuthCsrfVerifier; import org.sonar.server.authentication.SamlValidationCspHeaders; import org.sonar.server.authentication.SamlValidationRedirectionFilter; import org.sonar.server.authentication.event.AuthenticationException; +import org.sonar.server.http.JavaxHttpRequest; import org.sonar.server.user.ThreadLocalUserSession; import org.sonar.server.ws.ServletFilterHandler; import static org.sonar.server.saml.ws.SamlValidationWs.SAML_VALIDATION_CONTROLLER; -public class ValidationAction extends ServletFilter implements SamlAction { +public class ValidationAction extends HttpFilter implements SamlAction { static final String VALIDATION_CALLBACK_KEY = SamlValidationRedirectionFilter.SAML_VALIDATION_KEY; private static final String URL_DELIMITER = "/"; @@ -69,35 +69,32 @@ public class ValidationAction extends ServletFilter implements SamlAction { } @Override - public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { - HttpServletResponse httpResponse = (HttpServletResponse) response; - HttpServletRequest httpRequest = (HttpServletRequest) request; - + public void doFilter(HttpRequest request, HttpResponse response, FilterChain filterChain) throws IOException { try { - oAuthCsrfVerifier.verifyState(httpRequest, httpResponse, samlIdentityProvider, "CSRFToken"); + oAuthCsrfVerifier.verifyState(request, response, samlIdentityProvider, "CSRFToken"); } catch (AuthenticationException exception) { - AuthenticationError.handleError(httpRequest, httpResponse, exception.getMessage()); + AuthenticationError.handleError(request, response, exception.getMessage()); return; } if (!userSession.hasSession() || !userSession.isSystemAdministrator()) { - AuthenticationError.handleError(httpRequest, httpResponse, "User needs to be logged in as system administrator to access this page."); + AuthenticationError.handleError(request, response, "User needs to be logged in as system administrator to access this page."); return; } - httpRequest = new HttpServletRequestWrapper(httpRequest) { + HttpServletRequest httpRequest = new HttpServletRequestWrapper(((JavaxHttpRequest) request).getDelegate()) { @Override public StringBuffer getRequestURL() { return new StringBuffer(oAuth2ContextFactory.generateCallbackUrl(SamlIdentityProvider.KEY)); } }; - httpResponse.setContentType("text/html"); + response.setContentType("text/html"); - String htmlResponse = samlAuthenticator.getAuthenticationStatusPage(httpRequest, httpResponse); - String nonce = SamlValidationCspHeaders.addCspHeadersWithNonceToResponse(httpResponse); + String htmlResponse = samlAuthenticator.getAuthenticationStatusPage(new JavaxHttpRequest(httpRequest), response); + String nonce = SamlValidationCspHeaders.addCspHeadersWithNonceToResponse(response); htmlResponse = htmlResponse.replace("%NONCE%", nonce); - httpResponse.getWriter().print(htmlResponse); + response.getWriter().print(htmlResponse); } @Override diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/saml/ws/ValidationInitAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/saml/ws/ValidationInitAction.java index 6ec10cbf358..9960cb09151 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/saml/ws/ValidationInitAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/saml/ws/ValidationInitAction.java @@ -20,14 +20,12 @@ package org.sonar.server.saml.ws; import java.io.IOException; -import javax.servlet.FilterChain; -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.server.http.HttpRequest; +import org.sonar.api.server.http.HttpResponse; import org.sonar.api.server.ws.WebService; -import org.sonar.api.web.ServletFilter; +import org.sonar.api.web.FilterChain; +import org.sonar.api.web.HttpFilter; +import org.sonar.api.web.UrlPattern; import org.sonar.auth.saml.SamlAuthenticator; import org.sonar.auth.saml.SamlIdentityProvider; import org.sonar.server.authentication.AuthenticationError; @@ -40,7 +38,7 @@ import org.sonar.server.ws.ServletFilterHandler; import static org.sonar.server.authentication.SamlValidationRedirectionFilter.SAML_VALIDATION_CONTROLLER_CONTEXT; import static org.sonar.server.authentication.SamlValidationRedirectionFilter.SAML_VALIDATION_KEY; -public class ValidationInitAction extends ServletFilter implements SamlAction { +public class ValidationInitAction extends HttpFilter implements SamlAction { public static final String VALIDATION_RELAY_STATE = "validation-query"; public static final String VALIDATION_INIT_KEY = "validation_init"; @@ -73,10 +71,7 @@ public class ValidationInitAction extends ServletFilter implements SamlAction { } @Override - public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException { - HttpServletRequest request = (HttpServletRequest) servletRequest; - HttpServletResponse response = (HttpServletResponse) servletResponse; - + public void doFilter(HttpRequest request, HttpResponse response, FilterChain chain) throws IOException { try { userSession.checkIsSystemAdministrator(); } catch (ForbiddenException e) { @@ -84,7 +79,7 @@ public class ValidationInitAction extends ServletFilter implements SamlAction { return; } - String csrfState = oAuthCsrfVerifier.generateState(request,response); + String csrfState = oAuthCsrfVerifier.generateState(request, response); try { samlAuthenticator.initLogin(oAuth2ContextFactory.generateCallbackUrl(SamlIdentityProvider.KEY), diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/user/ws/ChangePasswordAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/user/ws/ChangePasswordAction.java index c437c68568e..accc04598a5 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/user/ws/ChangePasswordAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/user/ws/ChangePasswordAction.java @@ -25,16 +25,15 @@ import com.google.gson.stream.JsonWriter; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.util.Optional; -import javax.servlet.FilterChain; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import org.sonar.api.server.http.HttpRequest; +import org.sonar.api.server.http.HttpResponse; import org.sonar.api.server.ws.Change; import org.sonar.api.server.ws.WebService; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; -import org.sonar.api.web.ServletFilter; +import org.sonar.api.web.FilterChain; +import org.sonar.api.web.HttpFilter; +import org.sonar.api.web.UrlPattern; import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.user.UserDto; @@ -61,7 +60,7 @@ import static org.sonarqube.ws.client.user.UsersWsParameters.PARAM_LOGIN; import static org.sonarqube.ws.client.user.UsersWsParameters.PARAM_PASSWORD; import static org.sonarqube.ws.client.user.UsersWsParameters.PARAM_PREVIOUS_PASSWORD; -public class ChangePasswordAction extends ServletFilter implements BaseUsersWsAction { +public class ChangePasswordAction extends HttpFilter implements BaseUsersWsAction { private static final Logger LOG = Loggers.get(ChangePasswordAction.class); @@ -117,7 +116,7 @@ public class ChangePasswordAction extends ServletFilter implements BaseUsersWsAc } @Override - public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) { + public void doFilter(HttpRequest request, HttpResponse response, FilterChain chain) { userSession.checkLoggedIn(); try (DbSession dbSession = dbClient.openSession(false)) { String login = getParamOrThrow(request, PARAM_LOGIN); @@ -148,7 +147,7 @@ public class ChangePasswordAction extends ServletFilter implements BaseUsersWsAc } } - private static String getParamOrThrow(ServletRequest request, String key) throws PasswordException { + private static String getParamOrThrow(HttpRequest request, String key) throws PasswordException { String value = request.getParameter(key); if (isNullOrEmpty(value)) { throw new PasswordException(format(MSG_PARAMETER_MISSING, key)); @@ -178,16 +177,14 @@ public class ChangePasswordAction extends ServletFilter implements BaseUsersWsAc return user; } - private void deleteTokensAndRefreshSession(ServletRequest request, ServletResponse response, DbSession dbSession, UserDto user) { + private void deleteTokensAndRefreshSession(HttpRequest request, HttpResponse response, DbSession dbSession, UserDto user) { dbClient.sessionTokensDao().deleteByUser(dbSession, user); refreshJwtToken(request, response, user); } - private void refreshJwtToken(ServletRequest request, ServletResponse response, UserDto user) { - HttpServletRequest httpRequest = (HttpServletRequest) request; - HttpServletResponse httpResponse = (HttpServletResponse) response; - jwtHttpHandler.removeToken(httpRequest, httpResponse); - jwtHttpHandler.generateToken(user, httpRequest, httpResponse); + private void refreshJwtToken(HttpRequest request, HttpResponse response, UserDto user) { + jwtHttpHandler.removeToken(request, response); + jwtHttpHandler.generateToken(user, request, response); } private void updatePassword(DbSession dbSession, UserDto user, String newPassword) { @@ -196,14 +193,15 @@ public class ChangePasswordAction extends ServletFilter implements BaseUsersWsAc }); } - private static void setResponseStatus(ServletResponse response, int newStatusCode) { - ((HttpServletResponse) response).setStatus(newStatusCode); + private static void setResponseStatus(HttpResponse response, int newStatusCode) { + response.setStatus(newStatusCode); } - private static void writeJsonResponse(String msg, ServletResponse response) { + private static void writeJsonResponse(String msg, HttpResponse response) { Gson gson = new GsonBuilder() .disableHtmlEscaping() .create(); + try (OutputStream output = response.getOutputStream(); JsonWriter writer = gson.newJsonWriter(new OutputStreamWriter(output, UTF_8))) { response.setContentType(JSON); diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/authentication/ws/LogoutActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/authentication/ws/LogoutActionTest.java index bb7f24fe945..168c29d9335 100644 --- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/authentication/ws/LogoutActionTest.java +++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/authentication/ws/LogoutActionTest.java @@ -21,14 +21,11 @@ package org.sonar.server.authentication.ws; import java.util.Collections; import java.util.Optional; -import javax.servlet.FilterChain; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import org.junit.Rule; import org.junit.Test; +import org.sonar.api.server.http.HttpRequest; +import org.sonar.api.server.http.HttpResponse; import org.sonar.api.server.ws.WebService; -import org.sonar.api.utils.System2; -import org.sonar.db.DbTester; +import org.sonar.api.web.FilterChain; import org.sonar.db.user.UserDto; import org.sonar.server.authentication.JwtHttpHandler; import org.sonar.server.authentication.event.AuthenticationEvent; @@ -49,15 +46,14 @@ public class LogoutActionTest { private static final UserDto USER = newUserDto().setLogin("john"); + private final HttpRequest request = mock(HttpRequest.class); + private final HttpResponse response = mock(HttpResponse.class); + private final FilterChain chain = mock(FilterChain.class); - private HttpServletRequest request = mock(HttpServletRequest.class); - private HttpServletResponse response = mock(HttpServletResponse.class); - private FilterChain chain = mock(FilterChain.class); + private final JwtHttpHandler jwtHttpHandler = mock(JwtHttpHandler.class); + private final AuthenticationEvent authenticationEvent = mock(AuthenticationEvent.class); - private JwtHttpHandler jwtHttpHandler = mock(JwtHttpHandler.class); - private AuthenticationEvent authenticationEvent = mock(AuthenticationEvent.class); - - private LogoutAction underTest = new LogoutAction(jwtHttpHandler, authenticationEvent); + private final LogoutAction underTest = new LogoutAction(jwtHttpHandler, authenticationEvent); @Test public void verify_definition() { @@ -119,12 +115,12 @@ public class LogoutActionTest { public void generate_auth_event_on_failure() { setUser(USER); AuthenticationException exception = AuthenticationException.newBuilder().setMessage("error!").setSource(sso()).build(); - doThrow(exception).when(jwtHttpHandler).getToken(any(HttpServletRequest.class), any(HttpServletResponse.class)); + doThrow(exception).when(jwtHttpHandler).getToken(any(HttpRequest.class), any(HttpResponse.class)); executeRequest(); verify(authenticationEvent).logoutFailure(request, "error!"); - verify(jwtHttpHandler).removeToken(any(HttpServletRequest.class), any(HttpServletResponse.class)); + verify(jwtHttpHandler).removeToken(any(HttpRequest.class), any(HttpResponse.class)); verifyNoInteractions(chain); } @@ -134,12 +130,12 @@ public class LogoutActionTest { } private void setUser(UserDto user) { - when(jwtHttpHandler.getToken(any(HttpServletRequest.class), any(HttpServletResponse.class))) + when(jwtHttpHandler.getToken(any(HttpRequest.class), any(HttpResponse.class))) .thenReturn(Optional.of(new JwtHttpHandler.Token(user, Collections.emptyMap()))); } private void setNoUser() { - when(jwtHttpHandler.getToken(any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(Optional.empty()); + when(jwtHttpHandler.getToken(any(HttpRequest.class), any(HttpResponse.class))).thenReturn(Optional.empty()); } } diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/authentication/ws/ValidateActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/authentication/ws/ValidateActionTest.java index b6258f62367..f41f4797cdf 100644 --- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/authentication/ws/ValidateActionTest.java +++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/authentication/ws/ValidateActionTest.java @@ -22,13 +22,13 @@ package org.sonar.server.authentication.ws; import java.io.PrintWriter; import java.io.StringWriter; import java.util.Optional; -import javax.servlet.FilterChain; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import org.junit.Before; import org.junit.Test; import org.sonar.api.config.internal.MapSettings; +import org.sonar.api.server.http.HttpRequest; +import org.sonar.api.server.http.HttpResponse; import org.sonar.api.server.ws.WebService; +import org.sonar.api.web.FilterChain; import org.sonar.server.authentication.BasicAuthentication; import org.sonar.server.authentication.JwtHttpHandler; import org.sonar.server.authentication.event.AuthenticationException; @@ -45,18 +45,18 @@ import static org.sonar.db.user.UserTesting.newUserDto; public class ValidateActionTest { - StringWriter stringWriter = new StringWriter(); + private final StringWriter stringWriter = new StringWriter(); - HttpServletRequest request = mock(HttpServletRequest.class); - HttpServletResponse response = mock(HttpServletResponse.class); - FilterChain chain = mock(FilterChain.class); + private final HttpRequest request = mock(HttpRequest.class); + private final HttpResponse response = mock(HttpResponse.class); + private final FilterChain chain = mock(FilterChain.class); - BasicAuthentication basicAuthentication = mock(BasicAuthentication.class); - JwtHttpHandler jwtHttpHandler = mock(JwtHttpHandler.class); + private final BasicAuthentication basicAuthentication = mock(BasicAuthentication.class); + private final JwtHttpHandler jwtHttpHandler = mock(JwtHttpHandler.class); - MapSettings settings = new MapSettings(); + private final MapSettings settings = new MapSettings(); - ValidateAction underTest = new ValidateAction(settings.asConfig(), basicAuthentication, jwtHttpHandler); + private final ValidateAction underTest = new ValidateAction(settings.asConfig(), basicAuthentication, jwtHttpHandler); @Before public void setUp() throws Exception { diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/saml/ws/ValidationActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/saml/ws/ValidationActionTest.java index 659f9dc956a..8e293ee5639 100644 --- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/saml/ws/ValidationActionTest.java +++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/saml/ws/ValidationActionTest.java @@ -23,20 +23,22 @@ import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.util.List; -import javax.servlet.FilterChain; -import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.junit.Before; import org.junit.Test; +import org.sonar.api.server.http.HttpRequest; +import org.sonar.api.server.http.HttpResponse; import org.sonar.api.server.ws.WebService; +import org.sonar.api.web.FilterChain; import org.sonar.auth.saml.SamlAuthenticator; import org.sonar.auth.saml.SamlIdentityProvider; import org.sonar.server.authentication.OAuth2ContextFactory; import org.sonar.server.authentication.OAuthCsrfVerifier; -import org.sonar.server.authentication.SamlValidationCspHeaders; import org.sonar.server.authentication.event.AuthenticationEvent; import org.sonar.server.authentication.event.AuthenticationException; +import org.sonar.server.http.JavaxHttpRequest; +import org.sonar.server.http.JavaxHttpResponse; import org.sonar.server.user.ThreadLocalUserSession; import static org.assertj.core.api.Assertions.assertThat; @@ -70,7 +72,6 @@ public class ValidationActionTest { underTest = new ValidationAction(userSession, samlAuthenticator, oAuth2ContextFactory, samlIdentityProvider, oAuthCsrfVerifier); } - @Test public void do_get_pattern() { assertThat(underTest.doGetPattern().matches("/saml/validation")).isTrue(); @@ -81,8 +82,8 @@ public class ValidationActionTest { } @Test - public void do_filter_admin() throws ServletException, IOException { - HttpServletRequest servletRequest = mock(HttpServletRequest.class); + public void do_filter_admin() throws IOException { + HttpServletRequest servletRequest = spy(HttpServletRequest.class); HttpServletResponse servletResponse = mock(HttpServletResponse.class); StringWriter stringWriter = new StringWriter(); doReturn(new PrintWriter(stringWriter)).when(servletResponse).getWriter(); @@ -93,7 +94,7 @@ public class ValidationActionTest { final String mockedHtmlContent = "mocked html content"; doReturn(mockedHtmlContent).when(samlAuthenticator).getAuthenticationStatusPage(any(), any()); - underTest.doFilter(servletRequest, servletResponse, filterChain); + underTest.doFilter(new JavaxHttpRequest(servletRequest), new JavaxHttpResponse(servletResponse), filterChain); verify(samlAuthenticator).getAuthenticationStatusPage(any(), any()); verify(servletResponse).getWriter(); @@ -102,9 +103,9 @@ public class ValidationActionTest { } @Test - public void do_filter_not_authorized() throws ServletException, IOException { - HttpServletRequest servletRequest = spy(HttpServletRequest.class); - HttpServletResponse servletResponse = mock(HttpServletResponse.class); + public void do_filter_not_authorized() throws IOException { + HttpRequest servletRequest = spy(HttpRequest.class); + HttpResponse servletResponse = mock(HttpResponse.class); StringWriter stringWriter = new StringWriter(); doReturn(new PrintWriter(stringWriter)).when(servletResponse).getWriter(); FilterChain filterChain = mock(FilterChain.class); @@ -118,9 +119,9 @@ public class ValidationActionTest { } @Test - public void do_filter_failed_csrf_verification() throws ServletException, IOException { - HttpServletRequest servletRequest = spy(HttpServletRequest.class); - HttpServletResponse servletResponse = mock(HttpServletResponse.class); + public void do_filter_failed_csrf_verification() throws IOException { + HttpRequest servletRequest = spy(HttpRequest.class); + HttpResponse servletResponse = mock(HttpResponse.class); StringWriter stringWriter = new StringWriter(); doReturn(new PrintWriter(stringWriter)).when(servletResponse).getWriter(); FilterChain filterChain = mock(FilterChain.class); @@ -128,7 +129,7 @@ public class ValidationActionTest { doReturn("IdentityProviderName").when(samlIdentityProvider).getName(); doThrow(AuthenticationException.newBuilder() .setSource(AuthenticationEvent.Source.oauth2(samlIdentityProvider)) - .setMessage("Cookie is missing").build()).when(oAuthCsrfVerifier).verifyState(any(),any(),any(), any()); + .setMessage("Cookie is missing").build()).when(oAuthCsrfVerifier).verifyState(any(), any(), any(), any()); doReturn(true).when(userSession).hasSession(); doReturn(true).when(userSession).isSystemAdministrator(); diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/saml/ws/ValidationInitActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/saml/ws/ValidationInitActionTest.java index 5f205f3da56..5a6db55a32e 100644 --- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/saml/ws/ValidationInitActionTest.java +++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/saml/ws/ValidationInitActionTest.java @@ -20,14 +20,13 @@ package org.sonar.server.saml.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.Before; import org.junit.Rule; import org.junit.Test; +import org.sonar.api.server.http.HttpRequest; +import org.sonar.api.server.http.HttpResponse; import org.sonar.api.server.ws.WebService; +import org.sonar.api.web.FilterChain; import org.sonar.auth.saml.SamlAuthenticator; import org.sonar.server.authentication.OAuth2ContextFactory; import org.sonar.server.authentication.OAuthCsrfVerifier; @@ -68,10 +67,10 @@ public class ValidationInitActionTest { } @Test - public void do_filter_as_admin() throws IOException, ServletException { + public void do_filter_as_admin() throws IOException { userSession.logIn().setSystemAdministrator(); - HttpServletRequest servletRequest = mock(HttpServletRequest.class); - HttpServletResponse servletResponse = mock(HttpServletResponse.class); + HttpRequest servletRequest = mock(HttpRequest.class); + HttpResponse servletResponse = mock(HttpResponse.class); FilterChain filterChain = mock(FilterChain.class); String callbackUrl = "http://localhost:9000/api/validation_test"; @@ -86,10 +85,10 @@ public class ValidationInitActionTest { } @Test - public void do_filter_as_admin_with_init_issues() throws IOException, ServletException { + public void do_filter_as_admin_with_init_issues() throws IOException { userSession.logIn().setSystemAdministrator(); - HttpServletRequest servletRequest = mock(HttpServletRequest.class); - HttpServletResponse servletResponse = mock(HttpServletResponse.class); + HttpRequest servletRequest = mock(HttpRequest.class); + HttpResponse servletResponse = mock(HttpResponse.class); FilterChain filterChain = mock(FilterChain.class); String callbackUrl = "http://localhost:9000/api/validation_test"; when(oAuth2ContextFactory.generateCallbackUrl(anyString())) @@ -104,10 +103,10 @@ public class ValidationInitActionTest { } @Test - public void do_filter_as_not_admin() throws IOException, ServletException { + public void do_filter_as_not_admin() throws IOException { userSession.logIn(); - HttpServletRequest servletRequest = mock(HttpServletRequest.class); - HttpServletResponse servletResponse = mock(HttpServletResponse.class); + HttpRequest servletRequest = mock(HttpRequest.class); + HttpResponse servletResponse = mock(HttpResponse.class); FilterChain filterChain = mock(FilterChain.class); String callbackUrl = "http://localhost:9000/api/validation_test"; when(oAuth2ContextFactory.generateCallbackUrl(anyString())) @@ -120,10 +119,10 @@ public class ValidationInitActionTest { } @Test - public void do_filter_as_anonymous() throws IOException, ServletException { + public void do_filter_as_anonymous() throws IOException { userSession.anonymous(); - HttpServletRequest servletRequest = mock(HttpServletRequest.class); - HttpServletResponse servletResponse = mock(HttpServletResponse.class); + HttpRequest servletRequest = mock(HttpRequest.class); + HttpResponse servletResponse = mock(HttpResponse.class); FilterChain filterChain = mock(FilterChain.class); String callbackUrl = "http://localhost:9000/api/validation_test"; when(oAuth2ContextFactory.generateCallbackUrl(anyString())) @@ -149,7 +148,7 @@ public class ValidationInitActionTest { assertThat(validationInitAction.handler()).isNotNull(); } - private void mockCsrfTokenGeneration(HttpServletRequest servletRequest, HttpServletResponse servletResponse) { + private void mockCsrfTokenGeneration(HttpRequest servletRequest, HttpResponse servletResponse) { when(oAuthCsrfVerifier.generateState(servletRequest, servletResponse)).thenReturn("CSRF_TOKEN"); } } diff --git a/server/sonar-webserver-ws/src/main/java/org/sonar/server/ws/ServletRequest.java b/server/sonar-webserver-ws/src/main/java/org/sonar/server/ws/ServletRequest.java index 65d3f4cb52e..79b7bdbf156 100644 --- a/server/sonar-webserver-ws/src/main/java/org/sonar/server/ws/ServletRequest.java +++ b/server/sonar-webserver-ws/src/main/java/org/sonar/server/ws/ServletRequest.java @@ -34,7 +34,9 @@ import javax.servlet.AsyncContext; import javax.servlet.http.HttpServletRequest; import org.sonar.api.impl.ws.PartImpl; import org.sonar.api.impl.ws.ValidatingRequest; +import org.sonar.api.server.http.HttpRequest; import org.sonar.api.utils.log.Loggers; +import org.sonar.server.http.JavaxHttpRequest; import org.sonarqube.ws.MediaTypes; import static com.google.common.base.MoreObjects.firstNonNull; @@ -51,8 +53,8 @@ public class ServletRequest extends ValidatingRequest { private final HttpServletRequest source; - public ServletRequest(HttpServletRequest source) { - this.source = source; + public ServletRequest(HttpRequest source) { + this.source = ((JavaxHttpRequest) source).getDelegate(); } @Override diff --git a/server/sonar-webserver-ws/src/main/java/org/sonar/server/ws/ServletResponse.java b/server/sonar-webserver-ws/src/main/java/org/sonar/server/ws/ServletResponse.java index 676bd9683b5..001ff235882 100644 --- a/server/sonar-webserver-ws/src/main/java/org/sonar/server/ws/ServletResponse.java +++ b/server/sonar-webserver-ws/src/main/java/org/sonar/server/ws/ServletResponse.java @@ -25,9 +25,11 @@ import java.io.OutputStreamWriter; import java.nio.charset.StandardCharsets; import java.util.Collection; import javax.servlet.http.HttpServletResponse; +import org.sonar.api.server.http.HttpResponse; import org.sonar.api.server.ws.Response; import org.sonar.api.utils.text.JsonWriter; import org.sonar.api.utils.text.XmlWriter; +import org.sonar.server.http.JavaxHttpResponse; import static java.nio.charset.StandardCharsets.UTF_8; import static org.sonarqube.ws.MediaTypes.JSON; @@ -37,8 +39,9 @@ public class ServletResponse implements Response { private final ServletStream stream; - public ServletResponse(HttpServletResponse response) { - stream = new ServletStream(response); + public ServletResponse(HttpResponse response) { + HttpServletResponse httpServletResponse = ((JavaxHttpResponse) response).getDelegate(); + stream = new ServletStream(httpServletResponse); } public static class ServletStream implements Stream { diff --git a/server/sonar-webserver-ws/src/test/java/org/sonar/server/ws/ServletRequestTest.java b/server/sonar-webserver-ws/src/test/java/org/sonar/server/ws/ServletRequestTest.java index f491ecf89c7..297a6c7e67e 100644 --- a/server/sonar-webserver-ws/src/test/java/org/sonar/server/ws/ServletRequestTest.java +++ b/server/sonar-webserver-ws/src/test/java/org/sonar/server/ws/ServletRequestTest.java @@ -19,16 +19,17 @@ */ package org.sonar.server.ws; -import com.google.common.collect.ImmutableMap; import com.google.common.net.HttpHeaders; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.StringReader; import java.util.List; +import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.Part; import org.junit.Test; +import org.sonar.server.http.JavaxHttpRequest; import org.sonarqube.ws.MediaTypes; import static org.assertj.core.api.Assertions.assertThat; @@ -39,10 +40,9 @@ import static org.mockito.Mockito.when; public class ServletRequestTest { + private final HttpServletRequest source = mock(HttpServletRequest.class); - private HttpServletRequest source = mock(HttpServletRequest.class); - - private ServletRequest underTest = new ServletRequest(source); + private final ServletRequest underTest = new ServletRequest(new JavaxHttpRequest(source)); @Test public void call_method() { @@ -76,8 +76,8 @@ public class ServletRequestTest { @Test public void has_param_from_source() { - when(source.getParameterMap()).thenReturn(ImmutableMap.of("param", new String[] {"value"})); - ServletRequest request = new ServletRequest(source); + when(source.getParameterMap()).thenReturn(Map.of("param", new String[] {"value"})); + ServletRequest request = new ServletRequest(new JavaxHttpRequest(source)); assertThat(request.hasParam("param")).isTrue(); } diff --git a/server/sonar-webserver-ws/src/test/java/org/sonar/server/ws/ServletResponseTest.java b/server/sonar-webserver-ws/src/test/java/org/sonar/server/ws/ServletResponseTest.java index 8f44be35c65..39f4e209a27 100644 --- a/server/sonar-webserver-ws/src/test/java/org/sonar/server/ws/ServletResponseTest.java +++ b/server/sonar-webserver-ws/src/test/java/org/sonar/server/ws/ServletResponseTest.java @@ -20,11 +20,11 @@ package org.sonar.server.ws; import java.io.IOException; -import java.nio.charset.Charset; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletResponse; import org.junit.Before; import org.junit.Test; +import org.sonar.server.http.JavaxHttpResponse; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; @@ -36,11 +36,10 @@ import static org.sonarqube.ws.MediaTypes.XML; public class ServletResponseTest { + private final ServletOutputStream output = mock(ServletOutputStream.class); + private final HttpServletResponse response = mock(HttpServletResponse.class); - private ServletOutputStream output = mock(ServletOutputStream.class); - private HttpServletResponse response = mock(HttpServletResponse.class); - - private ServletResponse underTest = new ServletResponse(response); + private final ServletResponse underTest = new ServletResponse(new JavaxHttpResponse(response)); @Before public void setUp() throws Exception { diff --git a/server/sonar-webserver/src/it/java/org/sonar/server/platform/web/SonarLintConnectionFilterIT.java b/server/sonar-webserver/src/it/java/org/sonar/server/platform/web/SonarLintConnectionFilterIT.java index 45f4be0b9bd..ff14e8d21f2 100644 --- a/server/sonar-webserver/src/it/java/org/sonar/server/platform/web/SonarLintConnectionFilterIT.java +++ b/server/sonar-webserver/src/it/java/org/sonar/server/platform/web/SonarLintConnectionFilterIT.java @@ -22,15 +22,15 @@ package org.sonar.server.platform.web; import java.io.IOException; import javax.annotation.CheckForNull; import javax.annotation.Nullable; -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import org.junit.Rule; import org.junit.Test; import org.sonar.api.impl.utils.TestSystem2; +import org.sonar.api.server.http.HttpResponse; +import org.sonar.api.web.FilterChain; import org.sonar.db.DbTester; import org.sonar.db.user.UserDto; +import org.sonar.server.http.JavaxHttpRequest; import org.sonar.server.user.ServerUserSession; import org.sonar.server.user.ThreadLocalUserSession; @@ -48,7 +48,7 @@ public class SonarLintConnectionFilterIT { public DbTester dbTester = DbTester.create(system2); @Test - public void update() throws IOException, ServletException { + public void update() throws IOException { system2.setNow(10_000_000L); addUser(LOGIN, 1_000_000L); @@ -57,7 +57,7 @@ public class SonarLintConnectionFilterIT { } @Test - public void update_first_time() throws IOException, ServletException { + public void update_first_time() throws IOException { system2.setNow(10_000_000L); addUser(LOGIN, null); @@ -74,7 +74,7 @@ public class SonarLintConnectionFilterIT { } @Test - public void do_nothing_if_no_sonarlint_agent() throws IOException, ServletException { + public void do_nothing_if_no_sonarlint_agent() throws IOException { system2.setNow(10_000L); addUser(LOGIN, 1_000L); @@ -84,7 +84,7 @@ public class SonarLintConnectionFilterIT { } @Test - public void do_nothing_if_not_logged_in() throws IOException, ServletException { + public void do_nothing_if_not_logged_in() throws IOException { system2.setNow(10_000_000L); addUser("invalid", 1_000_000L); @@ -93,17 +93,17 @@ public class SonarLintConnectionFilterIT { } @Test - public void dont_fail_if_no_user_set() throws IOException, ServletException { + public void dont_fail_if_no_user_set() throws IOException { SonarLintConnectionFilter underTest = new SonarLintConnectionFilter(dbTester.getDbClient(), new ThreadLocalUserSession(), system2); - HttpServletRequest request = mock(HttpServletRequest.class); - when(request.getHeader("User-Agent")).thenReturn("sonarlint"); + HttpServletRequest httpRequest = mock(HttpServletRequest.class); + when(httpRequest.getHeader("User-Agent")).thenReturn("sonarlint"); FilterChain chain = mock(FilterChain.class); - underTest.doFilter(request, mock(ServletResponse.class), chain); + underTest.doFilter(new JavaxHttpRequest(httpRequest), mock(HttpResponse.class), chain); verify(chain).doFilter(any(), any()); } @Test - public void only_update_if_not_updated_within_1h() throws IOException, ServletException { + public void only_update_if_not_updated_within_1h() throws IOException { system2.setNow(2_000_000L); addUser(LOGIN, 1_000_000L); @@ -120,15 +120,15 @@ public class SonarLintConnectionFilterIT { return dbTester.getDbClient().userDao().selectByLogin(dbTester.getSession(), login).getLastSonarlintConnectionDate(); } - private void runFilter(String loggedInUser, @Nullable String agent) throws IOException, ServletException { + private void runFilter(String loggedInUser, @Nullable String agent) throws IOException { UserDto user = dbTester.getDbClient().userDao().selectByLogin(dbTester.getSession(), loggedInUser); ThreadLocalUserSession session = new ThreadLocalUserSession(); session.set(new ServerUserSession(dbTester.getDbClient(), user)); SonarLintConnectionFilter underTest = new SonarLintConnectionFilter(dbTester.getDbClient(), session, system2); - HttpServletRequest request = mock(HttpServletRequest.class); - when(request.getHeader("User-Agent")).thenReturn(agent); + HttpServletRequest httpRequest = mock(HttpServletRequest.class); + when(httpRequest.getHeader("User-Agent")).thenReturn(agent); FilterChain chain = mock(FilterChain.class); - underTest.doFilter(request, mock(ServletResponse.class), chain); + underTest.doFilter(new JavaxHttpRequest(httpRequest), mock(HttpResponse.class), chain); verify(chain).doFilter(any(), any()); } } diff --git a/server/sonar-webserver/src/main/java/org/sonar/server/platform/web/MasterServletFilter.java b/server/sonar-webserver/src/main/java/org/sonar/server/platform/web/MasterServletFilter.java index 994bea92ca1..31e40cf4b2b 100644 --- a/server/sonar-webserver/src/main/java/org/sonar/server/platform/web/MasterServletFilter.java +++ b/server/sonar-webserver/src/main/java/org/sonar/server/platform/web/MasterServletFilter.java @@ -35,8 +35,14 @@ 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.server.http.HttpRequest; +import org.sonar.api.server.http.HttpResponse; import org.sonar.api.utils.log.Loggers; +import org.sonar.api.web.HttpFilter; import org.sonar.api.web.ServletFilter; +import org.sonar.server.http.JavaxHttpRequest; +import org.sonar.server.http.JavaxHttpResponse; import org.sonar.server.platform.PlatformImpl; /** @@ -46,7 +52,10 @@ public class MasterServletFilter implements Filter { private static final String SCIM_FILTER_PATH = "/api/scim/v2/"; private static volatile MasterServletFilter instance; + + @Deprecated(forRemoval = true) private ServletFilter[] filters; + private HttpFilter[] httpFilters; private FilterConfig config; public MasterServletFilter() { @@ -60,7 +69,9 @@ public class MasterServletFilter implements Filter { public void init(FilterConfig config) { // Filters are already available in the container unless a database migration is required. See // org.sonar.server.startup.RegisterServletFilters. - init(config, PlatformImpl.getInstance().getContainer().getComponentsByType(ServletFilter.class)); + List servletFilterList = PlatformImpl.getInstance().getContainer().getComponentsByType(ServletFilter.class); + List httpFilterList = PlatformImpl.getInstance().getContainer().getComponentsByType(HttpFilter.class); + init(config, servletFilterList, httpFilterList); } @CheckForNull @@ -73,17 +84,18 @@ public class MasterServletFilter implements Filter { MasterServletFilter.instance = instance; } - void init(FilterConfig config, List filters) { + void init(FilterConfig config, List filters, List httpFilters) { this.config = config; - initFilters(filters); + initHttpFilters(httpFilters); + initServletFilters(filters); } - public void initFilters(List filterExtensions) { - LinkedList filterList = new LinkedList<>(); - for (ServletFilter extension : filterExtensions) { + public void initHttpFilters(List filterExtensions) { + LinkedList filterList = new LinkedList<>(); + for (HttpFilter extension : filterExtensions) { try { Loggers.get(MasterServletFilter.class).info(String.format("Initializing servlet filter %s [pattern=%s]", extension, extension.doGetPattern().label())); - extension.init(config); + extension.init(); // As for scim we need to intercept traffic to URLs with path parameters // and that use is not properly handled when dealing with inclusions/exclusions of the WebServiceFilter, // we need to make sure the Scim filters are invoked before the WebserviceFilter @@ -96,18 +108,34 @@ public class MasterServletFilter implements Filter { throw new IllegalStateException("Fail to initialize servlet filter: " + extension + ". Message: " + e.getMessage(), e); } } - filters = filterList.toArray(new ServletFilter[0]); + httpFilters = filterList.toArray(new HttpFilter[0]); } - private static boolean isScimFilter(ServletFilter extension) { + private static boolean isScimFilter(HttpFilter extension) { return extension.doGetPattern().getInclusions().stream() .anyMatch(s -> s.startsWith(SCIM_FILTER_PATH)); } + @Deprecated(forRemoval = true) + public void initServletFilters(List filterExtensions) { + LinkedList filterList = new LinkedList<>(); + for (ServletFilter extension : filterExtensions) { + try { + Loggers.get(MasterServletFilter.class).info(String.format("Initializing servlet filter %s [pattern=%s]", extension, extension.doGetPattern().label())); + extension.init(config); + // adding deprecated extensions as last + filterList.addLast(extension); + } catch (Exception e) { + throw new IllegalStateException("Fail to initialize servlet filter: " + extension + ". Message: " + e.getMessage(), e); + } + } + filters = filterList.toArray(new ServletFilter[0]); + } + @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException { HttpServletRequest hsr = (HttpServletRequest) request; - if (filters.length == 0) { + if (filters.length == 0 && httpFilters.length == 0) { chain.doFilter(hsr, response); } else { String path = hsr.getRequestURI().replaceFirst(hsr.getContextPath(), ""); @@ -118,6 +146,10 @@ public class MasterServletFilter implements Filter { } private void buildGodchain(String path, GodFilterChain godChain) { + Arrays.stream(httpFilters) + .filter(filter -> filter.doGetPattern().matches(path)) + .forEachOrdered(godChain::addFilter); + Arrays.stream(filters) .filter(filter -> filter.doGetPattern().matches(path)) .forEachOrdered(godChain::addFilter); @@ -128,6 +160,10 @@ public class MasterServletFilter implements Filter { for (ServletFilter filter : filters) { filter.destroy(); } + + for (HttpFilter filter : httpFilters) { + filter.destroy(); + } } @VisibleForTesting @@ -135,9 +171,14 @@ public class MasterServletFilter implements Filter { return filters; } + @VisibleForTesting + HttpFilter[] getHttpFilters() { + return httpFilters; + } + private static final class GodFilterChain implements FilterChain { - private FilterChain chain; - private List filters = new LinkedList<>(); + private final FilterChain chain; + private final List filters = new LinkedList<>(); private Iterator iterator; public GodFilterChain(FilterChain chain) { @@ -146,20 +187,59 @@ public class MasterServletFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { + if (iterator == null) { iterator = filters.iterator(); } if (iterator.hasNext()) { - iterator.next().doFilter(request, response, this); + Filter next = iterator.next(); + next.doFilter(request, response, this); } else { chain.doFilter(request, response); } } + @Deprecated(forRemoval = true) public void addFilter(Filter filter) { Preconditions.checkState(iterator == null); filters.add(filter); } + + public void addFilter(HttpFilter filter) { + Preconditions.checkState(iterator == null); + filters.add(new JavaxFilterAdapter(filter)); + } + } + + private static class JavaxFilterAdapter implements Filter { + private final HttpFilter httpFilter; + + JavaxFilterAdapter(HttpFilter httpFilter) { + this.httpFilter = httpFilter; + } + + @Override + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException { + HttpRequest javaxHttpRequest = new JavaxHttpRequest((HttpServletRequest) servletRequest); + HttpResponse javaxHttpResponse = new JavaxHttpResponse((HttpServletResponse) servletResponse); + httpFilter.doFilter(javaxHttpRequest, javaxHttpResponse, new HttpFilterChainAdapter(chain)); + } } + private static class HttpFilterChainAdapter implements org.sonar.api.web.FilterChain { + private final FilterChain filterChain; + + HttpFilterChainAdapter(FilterChain filterChain) { + this.filterChain = filterChain; + } + + @Override + public void doFilter(HttpRequest httpRequest, HttpResponse httpResponse) throws IOException { + try { + filterChain.doFilter(((JavaxHttpRequest) httpRequest).getDelegate(), ((JavaxHttpResponse) httpResponse).getDelegate()); + } catch (ServletException e) { + throw new RuntimeException(e); + } + } + } } diff --git a/server/sonar-webserver/src/main/java/org/sonar/server/platform/web/RegisterServletFilters.java b/server/sonar-webserver/src/main/java/org/sonar/server/platform/web/RegisterServletFilters.java index c77b0204d7c..3967a6e3b65 100644 --- a/server/sonar-webserver/src/main/java/org/sonar/server/platform/web/RegisterServletFilters.java +++ b/server/sonar-webserver/src/main/java/org/sonar/server/platform/web/RegisterServletFilters.java @@ -21,6 +21,7 @@ package org.sonar.server.platform.web; import java.util.Arrays; import org.sonar.api.Startable; +import org.sonar.api.web.HttpFilter; import org.sonar.api.web.ServletFilter; import org.springframework.beans.factory.annotation.Autowired; @@ -28,16 +29,24 @@ import org.springframework.beans.factory.annotation.Autowired; * @since 3.5 */ public class RegisterServletFilters implements Startable { - private final ServletFilter[] filters; + private final ServletFilter[] servletFilters; + private final HttpFilter[] httpFilters; @Autowired(required = false) - public RegisterServletFilters(ServletFilter[] filters) { - this.filters = filters; + @Deprecated(since = "10.1", forRemoval = true) + public RegisterServletFilters(ServletFilter[] servletFilters, HttpFilter[] httpFilters) { + this.servletFilters = servletFilters; + this.httpFilters = httpFilters; + } + + @Autowired(required = false) + public RegisterServletFilters(HttpFilter[] httpFilters) { + this(new ServletFilter[0], httpFilters); } @Autowired(required = false) public RegisterServletFilters() { - this(new ServletFilter[0]); + this(new ServletFilter[0], new HttpFilter[0]); } @Override @@ -45,9 +54,10 @@ public class RegisterServletFilters implements Startable { MasterServletFilter masterServletFilter = MasterServletFilter.getInstance(); if (masterServletFilter != null) { // Probably a database upgrade. MasterSlaveFilter was instantiated by the servlet container - // while picocontainer was not completely up. + // while spring was not completely up. // See https://jira.sonarsource.com/browse/SONAR-3612 - masterServletFilter.initFilters(Arrays.asList(filters)); + masterServletFilter.initHttpFilters(Arrays.asList(httpFilters)); + masterServletFilter.initServletFilters(Arrays.asList(servletFilters)); } } diff --git a/server/sonar-webserver/src/main/java/org/sonar/server/platform/web/SonarLintConnectionFilter.java b/server/sonar-webserver/src/main/java/org/sonar/server/platform/web/SonarLintConnectionFilter.java index e5ba6d2aca9..7d01689b916 100644 --- a/server/sonar-webserver/src/main/java/org/sonar/server/platform/web/SonarLintConnectionFilter.java +++ b/server/sonar-webserver/src/main/java/org/sonar/server/platform/web/SonarLintConnectionFilter.java @@ -22,12 +22,12 @@ package org.sonar.server.platform.web; import java.io.IOException; import java.util.Locale; import java.util.Optional; -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; +import org.sonar.api.server.http.HttpRequest; +import org.sonar.api.server.http.HttpResponse; import org.sonar.api.utils.System2; -import org.sonar.api.web.ServletFilter; +import org.sonar.api.web.FilterChain; +import org.sonar.api.web.HttpFilter; +import org.sonar.api.web.UrlPattern; import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.server.user.ThreadLocalUserSession; @@ -35,7 +35,7 @@ import org.sonar.server.ws.ServletRequest; import static java.util.concurrent.TimeUnit.HOURS; -public class SonarLintConnectionFilter extends ServletFilter { +public class SonarLintConnectionFilter extends HttpFilter { private static final UrlPattern URL_PATTERN = UrlPattern.builder() .includes("/api/*") .excludes("/api/v2/*") @@ -56,25 +56,14 @@ public class SonarLintConnectionFilter extends ServletFilter { } @Override - public void doFilter(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException { - HttpServletRequest request = (HttpServletRequest) servletRequest; + public void doFilter(HttpRequest request, HttpResponse response, FilterChain chain) throws IOException { ServletRequest wsRequest = new ServletRequest(request); Optional agent = wsRequest.header("User-Agent"); if (agent.isPresent() && agent.get().toLowerCase(Locale.ENGLISH).contains("sonarlint")) { update(); } - chain.doFilter(servletRequest, servletResponse); - } - - @Override - public void init(FilterConfig filterConfig) { - // Nothing to do - } - - @Override - public void destroy() { - // Nothing to do + chain.doFilter(request, response); } public void update() { diff --git a/server/sonar-webserver/src/main/java/org/sonar/server/platform/web/UserSessionFilter.java b/server/sonar-webserver/src/main/java/org/sonar/server/platform/web/UserSessionFilter.java index 777380a0a03..104bc7d87aa 100644 --- a/server/sonar-webserver/src/main/java/org/sonar/server/platform/web/UserSessionFilter.java +++ b/server/sonar-webserver/src/main/java/org/sonar/server/platform/web/UserSessionFilter.java @@ -34,6 +34,8 @@ import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; import org.sonar.db.DBSessions; import org.sonar.server.authentication.UserSessionInitializer; +import org.sonar.server.http.JavaxHttpRequest; +import org.sonar.server.http.JavaxHttpResponse; import org.sonar.server.platform.Platform; import org.sonar.server.platform.PlatformImpl; import org.sonar.server.setting.ThreadLocalSettings; @@ -77,7 +79,7 @@ public class UserSessionFilter implements Filter { private static void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain, @Nullable UserSessionInitializer userSessionInitializer) throws IOException, ServletException { try { - if (userSessionInitializer == null || userSessionInitializer.initUserSession(request, response)) { + if (userSessionInitializer == null || userSessionInitializer.initUserSession(new JavaxHttpRequest(request), new JavaxHttpResponse(response))) { chain.doFilter(request, response); } } finally { diff --git a/server/sonar-webserver/src/main/java/org/sonar/server/platform/web/WebServiceFilter.java b/server/sonar-webserver/src/main/java/org/sonar/server/platform/web/WebServiceFilter.java index 5d04f9e0381..4ab60d22d29 100644 --- a/server/sonar-webserver/src/main/java/org/sonar/server/platform/web/WebServiceFilter.java +++ b/server/sonar-webserver/src/main/java/org/sonar/server/platform/web/WebServiceFilter.java @@ -23,12 +23,12 @@ import java.util.HashSet; import java.util.Set; import java.util.function.Function; import java.util.stream.Stream; -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import org.sonar.api.server.http.HttpRequest; +import org.sonar.api.server.http.HttpResponse; import org.sonar.api.server.ws.WebService; -import org.sonar.api.web.ServletFilter; +import org.sonar.api.web.FilterChain; +import org.sonar.api.web.HttpFilter; +import org.sonar.api.web.UrlPattern; import org.sonar.core.util.stream.MoreCollectors; import org.sonar.server.ws.ServletFilterHandler; import org.sonar.server.ws.ServletRequest; @@ -47,7 +47,7 @@ import static org.sonar.server.platform.web.WebServiceReroutingFilter.MOVED_WEB_ *
  • web services that directly implemented with servlet filter, see {@link ServletFilterHandler})
  • * */ -public class WebServiceFilter extends ServletFilter { +public class WebServiceFilter extends HttpFilter { private final WebServiceEngine webServiceEngine; private final Set includeUrls; @@ -78,24 +78,12 @@ public class WebServiceFilter extends ServletFilter { } @Override - public void doFilter(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse, FilterChain chain) { - HttpServletRequest request = (HttpServletRequest) servletRequest; - HttpServletResponse response = (HttpServletResponse) servletResponse; + public void doFilter(HttpRequest request, HttpResponse response, FilterChain filterChain) { ServletRequest wsRequest = new ServletRequest(request); ServletResponse wsResponse = new ServletResponse(response); webServiceEngine.execute(wsRequest, wsResponse); } - @Override - public void init(FilterConfig filterConfig) { - // Nothing to do - } - - @Override - public void destroy() { - // Nothing to do - } - private static Function toPath() { return action -> "/" + action.path() + ".*"; } diff --git a/server/sonar-webserver/src/main/java/org/sonar/server/platform/web/WebServiceReroutingFilter.java b/server/sonar-webserver/src/main/java/org/sonar/server/platform/web/WebServiceReroutingFilter.java index dc58c49f108..52547fe7918 100644 --- a/server/sonar-webserver/src/main/java/org/sonar/server/platform/web/WebServiceReroutingFilter.java +++ b/server/sonar-webserver/src/main/java/org/sonar/server/platform/web/WebServiceReroutingFilter.java @@ -19,14 +19,13 @@ */ package org.sonar.server.platform.web; -import com.google.common.collect.ImmutableMap; import java.util.Map; import java.util.Set; -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import org.sonar.api.web.ServletFilter; +import org.sonar.api.server.http.HttpRequest; +import org.sonar.api.server.http.HttpResponse; +import org.sonar.api.web.FilterChain; +import org.sonar.api.web.HttpFilter; +import org.sonar.api.web.UrlPattern; import org.sonar.server.ws.ServletRequest; import org.sonar.server.ws.ServletResponse; import org.sonar.server.ws.WebServiceEngine; @@ -34,12 +33,11 @@ import org.sonar.server.ws.WebServiceEngine; /** * This filter is used to execute renamed/moved web services */ -public class WebServiceReroutingFilter extends ServletFilter { +public class WebServiceReroutingFilter extends HttpFilter { - private static final Map REDIRECTS = ImmutableMap.builder() - .put("/api/components/bulk_update_key", "/api/projects/bulk_update_key") - .put("/api/components/update_key", "/api/projects/update_key") - .build(); + private static final Map REDIRECTS = Map.of( + "/api/components/bulk_update_key", "/api/projects/bulk_update_key", + "/api/components/update_key", "/api/projects/update_key"); static final Set MOVED_WEB_SERVICES = REDIRECTS.keySet(); private final WebServiceEngine webServiceEngine; @@ -56,16 +54,15 @@ public class WebServiceReroutingFilter extends ServletFilter { } @Override - public void doFilter(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse, FilterChain chain) { - HttpServletRequest httpRequest = (HttpServletRequest) servletRequest; - RedirectionRequest wsRequest = new RedirectionRequest(httpRequest); - ServletResponse wsResponse = new ServletResponse((HttpServletResponse) servletResponse); + public void doFilter(HttpRequest request, HttpResponse response, FilterChain filterChain) { + RedirectionRequest wsRequest = new RedirectionRequest(request); + ServletResponse wsResponse = new ServletResponse(response); webServiceEngine.execute(wsRequest, wsResponse); } @Override - public void init(FilterConfig filterConfig) { + public void init() { // Nothing to do } @@ -77,7 +74,7 @@ public class WebServiceReroutingFilter extends ServletFilter { private static class RedirectionRequest extends ServletRequest { private final String redirectedPath; - public RedirectionRequest(HttpServletRequest source) { + public RedirectionRequest(HttpRequest source) { super(source); this.redirectedPath = REDIRECTS.getOrDefault(source.getServletPath(), source.getServletPath()); } diff --git a/server/sonar-webserver/src/test/java/org/sonar/server/platform/web/MasterServletFilterTest.java b/server/sonar-webserver/src/test/java/org/sonar/server/platform/web/MasterServletFilterTest.java index 49077005dbd..fa580be99be 100644 --- a/server/sonar-webserver/src/test/java/org/sonar/server/platform/web/MasterServletFilterTest.java +++ b/server/sonar-webserver/src/test/java/org/sonar/server/platform/web/MasterServletFilterTest.java @@ -21,6 +21,7 @@ package org.sonar.server.platform.web; import java.io.IOException; import java.util.Collections; +import java.util.List; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; @@ -34,11 +35,17 @@ import org.junit.Test; import org.mockito.InOrder; import org.mockito.Mockito; import org.slf4j.event.Level; +import org.sonar.api.server.http.HttpRequest; +import org.sonar.api.server.http.HttpResponse; import org.sonar.api.testfixtures.log.LogTester; +import org.sonar.api.web.HttpFilter; import org.sonar.api.web.ServletFilter; import org.sonar.api.web.ServletFilter.UrlPattern; +import org.sonar.server.http.JavaxHttpRequest; +import org.sonar.server.http.JavaxHttpResponse; import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -60,37 +67,43 @@ public class MasterServletFilterTest { } @Test - public void should_init_and_destroy_filters() throws Exception { - ServletFilter filter = createMockFilter(); + public void should_init_and_destroy_filters() throws ServletException { + ServletFilter servletFilter = createMockServletFilter(); + HttpFilter httpFilter = createMockHttpFilter(); FilterConfig config = mock(FilterConfig.class); MasterServletFilter master = new MasterServletFilter(); - master.init(config, singletonList(filter)); + master.init(config, singletonList(servletFilter), singletonList(httpFilter)); - assertThat(master.getFilters()).containsOnly(filter); - verify(filter).init(config); + assertThat(master.getFilters()).containsOnly(servletFilter); + assertThat(master.getHttpFilters()).containsOnly(httpFilter); + verify(servletFilter).init(config); + verify(httpFilter).init(); master.destroy(); - verify(filter).destroy(); + verify(servletFilter).destroy(); + verify(httpFilter).destroy(); } @Test public void servlet_container_should_instantiate_only_a_single_master_instance() { new MasterServletFilter(); - assertThatThrownBy(() -> new MasterServletFilter()) + assertThatThrownBy(MasterServletFilter::new) .isInstanceOf(IllegalStateException.class) .hasMessageContaining("Servlet filter org.sonar.server.platform.web.MasterServletFilter is already instantiated"); } @Test public void should_propagate_initialization_failure() throws Exception { - ServletFilter filter = createMockFilter(); + ServletFilter filter = createMockServletFilter(); doThrow(new IllegalStateException("foo")).when(filter).init(any(FilterConfig.class)); FilterConfig config = mock(FilterConfig.class); MasterServletFilter filters = new MasterServletFilter(); - assertThatThrownBy(() -> filters.init(config, singletonList(filter))) + List servletFilters = singletonList(filter); + List httpFilters = emptyList(); + assertThatThrownBy(() -> filters.init(config, servletFilters, httpFilters)) .isInstanceOf(IllegalStateException.class) .hasMessageContaining("foo"); } @@ -99,7 +112,7 @@ public class MasterServletFilterTest { public void filters_should_be_optional() throws Exception { FilterConfig config = mock(FilterConfig.class); MasterServletFilter filters = new MasterServletFilter(); - filters.init(config, Collections.emptyList()); + filters.init(config, Collections.emptyList(), Collections.emptyList()); ServletRequest request = mock(HttpServletRequest.class); ServletResponse response = mock(HttpServletResponse.class); @@ -113,23 +126,31 @@ public class MasterServletFilterTest { public void should_add_scim_filter_first_for_scim_request() throws Exception { String scimPath = "/api/scim/v2/Groups"; HttpServletRequest request = mock(HttpServletRequest.class); + HttpServletResponse response = mock(HttpServletResponse.class); when(request.getRequestURI()).thenReturn(scimPath); when(request.getContextPath()).thenReturn(""); - ServletResponse response = mock(HttpServletResponse.class); + + HttpRequest httpRequest = mock(JavaxHttpRequest.class); + HttpResponse httpResponse = mock(JavaxHttpResponse.class); + when(httpRequest.getRequestURI()).thenReturn(scimPath); + when(httpRequest.getContextPath()).thenReturn(""); + FilterChain chain = mock(FilterChain.class); ServletFilter filter1 = mockFilter(ServletFilter.class, request, response); ServletFilter filter2 = mockFilter(ServletFilter.class, request, response); - ServletFilter filter3 = mockFilter(WebServiceFilter.class, request, response); - when(filter3.doGetPattern()).thenReturn(UrlPattern.builder().includes(scimPath).build()); + HttpFilter filter3 = mockHttpFilter(WebServiceFilter.class, httpRequest, httpResponse); + HttpFilter filter4 = mockHttpFilter(HttpFilter.class, httpRequest, httpResponse); + when(filter3.doGetPattern()).thenReturn(org.sonar.api.web.UrlPattern.builder().includes(scimPath).build()); MasterServletFilter filters = new MasterServletFilter(); - filters.init(mock(FilterConfig.class), asList(filter3, filter1, filter2)); + filters.init(mock(FilterConfig.class), asList(filter1, filter2), asList(filter4, filter3)); filters.doFilter(request, response, chain); - InOrder inOrder = Mockito.inOrder(filter1, filter2, filter3); + InOrder inOrder = Mockito.inOrder(filter1, filter2, filter3, filter4); inOrder.verify(filter3).doFilter(any(), any(), any()); + inOrder.verify(filter4).doFilter(any(), any(), any()); inOrder.verify(filter1).doFilter(any(), any(), any()); inOrder.verify(filter2).doFilter(any(), any(), any()); } @@ -145,49 +166,56 @@ public class MasterServletFilterTest { return filter; } + private HttpFilter mockHttpFilter(Class filterClazz, HttpRequest request, HttpResponse response) throws IOException { + HttpFilter filter = mock(filterClazz); + when(filter.doGetPattern()).thenReturn(org.sonar.api.web.UrlPattern.builder().build()); + doAnswer(invocation -> { + org.sonar.api.web.FilterChain argument = invocation.getArgument(2, org.sonar.api.web.FilterChain.class); + argument.doFilter(request, response); + return null; + }).when(filter).doFilter(any(), any(), any()); + return filter; + } + @Test public void display_servlet_filter_patterns_in_INFO_log() { - ServletFilter filter = new PatternFilter(UrlPattern.builder().includes("/api/issues").excludes("/batch/projects").build()); + HttpFilter filter = new PatternFilter(org.sonar.api.web.UrlPattern.builder().includes("/api/issues").excludes("/batch/projects").build()); FilterConfig config = mock(FilterConfig.class); MasterServletFilter master = new MasterServletFilter(); - master.init(config, singletonList(filter)); + master.init(config, emptyList(), singletonList(filter)); assertThat(logTester.logs(Level.INFO)).containsOnly("Initializing servlet filter PatternFilter [pattern=UrlPattern{inclusions=[/api/issues], exclusions=[/batch/projects]}]"); } - private static ServletFilter createMockFilter() { + private static ServletFilter createMockServletFilter() { ServletFilter filter = mock(ServletFilter.class); when(filter.doGetPattern()).thenReturn(UrlPattern.builder().build()); return filter; } - private static class PatternFilter extends ServletFilter { + private static HttpFilter createMockHttpFilter() { + HttpFilter filter = mock(HttpFilter.class); + when(filter.doGetPattern()).thenReturn(org.sonar.api.web.UrlPattern.builder().build()); + return filter; + } - private final UrlPattern urlPattern; + private static class PatternFilter extends HttpFilter { - PatternFilter(UrlPattern urlPattern) { + private final org.sonar.api.web.UrlPattern urlPattern; + + PatternFilter(org.sonar.api.web.UrlPattern urlPattern) { this.urlPattern = urlPattern; } @Override - public UrlPattern doGetPattern() { + public org.sonar.api.web.UrlPattern doGetPattern() { return urlPattern; } @Override - public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) { - // Nothing to do - } + public void doFilter(HttpRequest request, HttpResponse response, org.sonar.api.web.FilterChain chain) throws IOException { - @Override - public void init(FilterConfig filterConfig) { - // Nothing to do - } - - @Override - public void destroy() { - // Nothing to do } @Override diff --git a/server/sonar-webserver/src/test/java/org/sonar/server/platform/web/RegisterServletFiltersTest.java b/server/sonar-webserver/src/test/java/org/sonar/server/platform/web/RegisterServletFiltersTest.java index d1e500bca57..9b9a42b9bc6 100644 --- a/server/sonar-webserver/src/test/java/org/sonar/server/platform/web/RegisterServletFiltersTest.java +++ b/server/sonar-webserver/src/test/java/org/sonar/server/platform/web/RegisterServletFiltersTest.java @@ -20,6 +20,7 @@ package org.sonar.server.platform.web; import org.junit.Test; +import org.sonar.api.web.HttpFilter; import org.sonar.api.web.ServletFilter; import static org.mockito.ArgumentMatchers.anyList; @@ -30,15 +31,15 @@ public class RegisterServletFiltersTest { @Test public void should_not_fail_if_master_filter_is_not_up() { MasterServletFilter.setInstance(null); - new RegisterServletFilters(new ServletFilter[2]).start(); + new RegisterServletFilters(new ServletFilter[2], new HttpFilter[2]).start(); } @Test public void should_register_filters_if_master_filter_is_up() { MasterServletFilter.setInstance(mock(MasterServletFilter.class)); - new RegisterServletFilters(new ServletFilter[2]).start(); + new RegisterServletFilters(new ServletFilter[2], new HttpFilter[2]).start(); - verify(MasterServletFilter.getInstance()).initFilters(anyList()); + verify(MasterServletFilter.getInstance()).initServletFilters(anyList()); } @Test @@ -46,6 +47,7 @@ public class RegisterServletFiltersTest { MasterServletFilter.setInstance(mock(MasterServletFilter.class)); new RegisterServletFilters().start(); // do not fail - verify(MasterServletFilter.getInstance()).initFilters(anyList()); + verify(MasterServletFilter.getInstance()).initHttpFilters(anyList()); + verify(MasterServletFilter.getInstance()).initServletFilters(anyList()); } } diff --git a/server/sonar-webserver/src/test/java/org/sonar/server/platform/web/UserSessionFilterTest.java b/server/sonar-webserver/src/test/java/org/sonar/server/platform/web/UserSessionFilterTest.java index 8d0618a440f..2b8b886eac6 100644 --- a/server/sonar-webserver/src/test/java/org/sonar/server/platform/web/UserSessionFilterTest.java +++ b/server/sonar-webserver/src/test/java/org/sonar/server/platform/web/UserSessionFilterTest.java @@ -26,17 +26,21 @@ import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.assertj.core.api.Assertions; import org.junit.Before; import org.junit.Test; import org.mockito.InOrder; import org.sonar.core.platform.ExtensionContainer; import org.sonar.db.DBSessions; import org.sonar.server.authentication.UserSessionInitializer; +import org.sonar.server.http.JavaxHttpRequest; +import org.sonar.server.http.JavaxHttpResponse; import org.sonar.server.platform.Platform; import org.sonar.server.setting.ThreadLocalSettings; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; @@ -72,7 +76,7 @@ public class UserSessionFilterTest { underTest.doFilter(request, response, chain); verify(chain).doFilter(request, response); - verify(userSessionInitializer).initUserSession(request, response); + verify(userSessionInitializer).initUserSession(any(JavaxHttpRequest.class), any(JavaxHttpResponse.class)); } @Test @@ -82,7 +86,7 @@ public class UserSessionFilterTest { underTest.doFilter(request, response, chain); verify(chain, never()).doFilter(request, response); - verify(userSessionInitializer).initUserSession(request, response); + verify(userSessionInitializer).initUserSession(any(JavaxHttpRequest.class), any(JavaxHttpResponse.class)); } @Test @@ -155,14 +159,15 @@ public class UserSessionFilterTest { @Test public void just_for_fun_and_coverage() { UserSessionFilter filter = new UserSessionFilter(); - filter.init(mock(FilterConfig.class)); - filter.destroy(); - // do not fail + + FilterConfig filterConfig = mock(FilterConfig.class); + Assertions.assertThatNoException().isThrownBy(() -> filter.init(filterConfig)); + Assertions.assertThatNoException().isThrownBy(filter::destroy); } private void mockUserSessionInitializer(boolean value) { when(container.getOptionalComponentByType(UserSessionInitializer.class)).thenReturn(Optional.of(userSessionInitializer)); - when(userSessionInitializer.initUserSession(request, response)).thenReturn(value); + when(userSessionInitializer.initUserSession(any(JavaxHttpRequest.class), any(JavaxHttpResponse.class))).thenReturn(value); } private RuntimeException mockUserSessionInitializerRemoveUserSessionFailing() { diff --git a/server/sonar-webserver/src/test/java/org/sonar/server/platform/web/WebServiceFilterTest.java b/server/sonar-webserver/src/test/java/org/sonar/server/platform/web/WebServiceFilterTest.java index 2972b59efd8..faf45054ddb 100644 --- a/server/sonar-webserver/src/test/java/org/sonar/server/platform/web/WebServiceFilterTest.java +++ b/server/sonar-webserver/src/test/java/org/sonar/server/platform/web/WebServiceFilterTest.java @@ -21,7 +21,6 @@ package org.sonar.server.platform.web; import java.util.ArrayList; import java.util.List; -import javax.servlet.FilterChain; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -31,6 +30,9 @@ import org.sonar.api.server.ws.Request; import org.sonar.api.server.ws.RequestHandler; import org.sonar.api.server.ws.Response; import org.sonar.api.server.ws.WebService; +import org.sonar.api.web.FilterChain; +import org.sonar.server.http.JavaxHttpRequest; +import org.sonar.server.http.JavaxHttpResponse; import org.sonar.server.ws.ServletFilterHandler; import org.sonar.server.ws.WebServiceEngine; @@ -43,21 +45,19 @@ import static org.sonar.server.platform.web.WebServiceFilterTest.WsUrl.newWsUrl; public class WebServiceFilterTest { - private static final String RUNTIME_VERSION = "7.1.0.1234"; + private final WebServiceEngine webServiceEngine = mock(WebServiceEngine.class); - - private WebServiceEngine webServiceEngine = mock(WebServiceEngine.class); - - private HttpServletRequest request = mock(HttpServletRequest.class); - private HttpServletResponse response = mock(HttpServletResponse.class); - private FilterChain chain = mock(FilterChain.class); - private ServletOutputStream responseOutput = mock(ServletOutputStream.class); + private final HttpServletRequest request = mock(HttpServletRequest.class); + private final HttpServletResponse response = mock(HttpServletResponse.class); + private final FilterChain chain = mock(FilterChain.class); + private final ServletOutputStream responseOutput = mock(ServletOutputStream.class); private WebServiceFilter underTest; @Before public void setUp() throws Exception { when(request.getContextPath()).thenReturn(""); - when(response.getOutputStream()).thenReturn(responseOutput); + HttpServletResponse mockResponse = mock(HttpServletResponse.class); + when(mockResponse.getOutputStream()).thenReturn(responseOutput); } @Test @@ -103,7 +103,7 @@ public class WebServiceFilterTest { public void execute_ws() { underTest = new WebServiceFilter(webServiceEngine); - underTest.doFilter(request, response, chain); + underTest.doFilter(new JavaxHttpRequest(request), new JavaxHttpResponse(response), chain); verify(webServiceEngine).execute(any(), any()); } @@ -132,8 +132,8 @@ public class WebServiceFilterTest { } static final class WsUrl { - private String controller; - private String[] actions; + private final String controller; + private final String[] actions; private RequestHandler requestHandler = EmptyRequestHandler.INSTANCE; WsUrl(String controller, String... actions) { diff --git a/server/sonar-webserver/src/test/java/org/sonar/server/platform/web/WebServiceReroutingFilterTest.java b/server/sonar-webserver/src/test/java/org/sonar/server/platform/web/WebServiceReroutingFilterTest.java index 92ddd47b0f0..682c4052e51 100644 --- a/server/sonar-webserver/src/test/java/org/sonar/server/platform/web/WebServiceReroutingFilterTest.java +++ b/server/sonar-webserver/src/test/java/org/sonar/server/platform/web/WebServiceReroutingFilterTest.java @@ -19,12 +19,14 @@ */ package org.sonar.server.platform.web; -import javax.servlet.FilterChain; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; +import org.sonar.api.web.FilterChain; +import org.sonar.server.http.JavaxHttpRequest; +import org.sonar.server.http.JavaxHttpResponse; import org.sonar.server.ws.ServletRequest; import org.sonar.server.ws.ServletResponse; import org.sonar.server.ws.WebServiceEngine; @@ -37,14 +39,14 @@ import static org.mockito.Mockito.when; public class WebServiceReroutingFilterTest { - private WebServiceEngine webServiceEngine = mock(WebServiceEngine.class); + private final WebServiceEngine webServiceEngine = mock(WebServiceEngine.class); - private HttpServletRequest request = mock(HttpServletRequest.class); - private HttpServletResponse response = mock(HttpServletResponse.class); - private FilterChain chain = mock(FilterChain.class); - private ArgumentCaptor servletRequestCaptor = ArgumentCaptor.forClass(ServletRequest.class); + private final HttpServletRequest request = mock(HttpServletRequest.class); + private final HttpServletResponse response = mock(HttpServletResponse.class); + private final FilterChain chain = mock(FilterChain.class); + private final ArgumentCaptor servletRequestCaptor = ArgumentCaptor.forClass(ServletRequest.class); - private WebServiceReroutingFilter underTest = new WebServiceReroutingFilter(webServiceEngine); + private final WebServiceReroutingFilter underTest = new WebServiceReroutingFilter(webServiceEngine); @Before public void setUp() { @@ -63,7 +65,7 @@ public class WebServiceReroutingFilterTest { when(request.getServletPath()).thenReturn("/api/components/update_key"); when(request.getMethod()).thenReturn("POST"); - underTest.doFilter(request, response, chain); + underTest.doFilter(new JavaxHttpRequest(request), new JavaxHttpResponse(response), chain); assertRedirection("/api/projects/update_key", "POST"); } diff --git a/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssue.java b/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssue.java index 08b92350a10..324097ee15d 100644 --- a/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssue.java +++ b/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssue.java @@ -40,6 +40,7 @@ import javax.annotation.Nullable; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.builder.ToStringBuilder; import org.apache.commons.lang.builder.ToStringStyle; +import org.sonar.api.code.CodeCharacteristic; import org.sonar.api.issue.Issue; import org.sonar.api.issue.IssueComment; import org.sonar.api.rule.RuleKey; @@ -144,6 +145,12 @@ public class DefaultIssue implements Issue, Trackable, org.sonar.api.ce.measure. return type; } + @CheckForNull + @Override + public CodeCharacteristic characteristic() { + throw new IllegalStateException("Not implemented yet"); + } + public DefaultIssue setType(RuleType type) { this.type = type; return this; diff --git a/sonar-plugin-api-impl/src/main/java/org/sonar/api/batch/sensor/issue/internal/DefaultExternalIssue.java b/sonar-plugin-api-impl/src/main/java/org/sonar/api/batch/sensor/issue/internal/DefaultExternalIssue.java index 2567c27bc88..42418950a25 100644 --- a/sonar-plugin-api-impl/src/main/java/org/sonar/api/batch/sensor/issue/internal/DefaultExternalIssue.java +++ b/sonar-plugin-api-impl/src/main/java/org/sonar/api/batch/sensor/issue/internal/DefaultExternalIssue.java @@ -19,12 +19,14 @@ */ package org.sonar.api.batch.sensor.issue.internal; +import javax.annotation.CheckForNull; import javax.annotation.Nullable; import org.sonar.api.batch.fs.internal.DefaultInputProject; import org.sonar.api.batch.rule.Severity; import org.sonar.api.batch.sensor.internal.SensorStorage; import org.sonar.api.batch.sensor.issue.ExternalIssue; import org.sonar.api.batch.sensor.issue.NewExternalIssue; +import org.sonar.api.code.CodeCharacteristic; import org.sonar.api.rule.RuleKey; import org.sonar.api.rules.RuleType; @@ -97,6 +99,12 @@ public class DefaultExternalIssue extends AbstractDefaultIssue