aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--server/sonar-webserver-core/src/main/java/org/sonar/server/plugins/PluginsRiskConsentFilter.java93
-rw-r--r--server/sonar-webserver-core/src/test/java/org/sonar/server/plugins/PluginsRiskConsentFilterTest.java176
-rw-r--r--server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java7
3 files changed, 274 insertions, 2 deletions
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
new file mode 100644
index 00000000000..998c932f07a
--- /dev/null
+++ b/server/sonar-webserver-core/src/main/java/org/sonar/server/plugins/PluginsRiskConsentFilter.java
@@ -0,0 +1,93 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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.plugins;
+
+import com.google.common.collect.ImmutableSet;
+import org.sonar.api.config.Configuration;
+import org.sonar.api.web.ServletFilter;
+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.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;
+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 java.io.IOException;
+import java.util.Set;
+
+public class PluginsRiskConsentFilter extends ServletFilter {
+
+ private static final String PLUGINS_RISK_CONSENT_PATH = "/admin/plugin_risk_consent"; //NOSONAR this path will be the same in every environment
+
+ private static final Set<String> SKIPPED_URLS = ImmutableSet.of(
+ PLUGINS_RISK_CONSENT_PATH,
+ "/account/reset_password",
+ "/admin/change_admin_password",
+ "/batch/*", "/api/*");
+ private final ThreadLocalUserSession userSession;
+ private final Configuration config;
+
+ public PluginsRiskConsentFilter(Configuration config, ThreadLocalUserSession userSession) {
+ this.userSession = userSession;
+ this.config = config;
+ }
+
+ @Override
+ public void init(FilterConfig filterConfig) throws ServletException {
+ //nothing to do
+ }
+
+ @Override
+ public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
+ HttpServletRequest request = (HttpServletRequest) servletRequest;
+ HttpServletResponse response = (HttpServletResponse) servletResponse;
+ PluginRiskConsent riskConsent = PluginRiskConsent.valueOf(config.get(PLUGINS_RISK_CONSENT).orElse(NOT_ACCEPTED.name()));
+
+ if (userSession.hasSession() && userSession.isLoggedIn()
+ && userSession.isSystemAdministrator() && riskConsent == REQUIRED) {
+ redirectTo(response, request.getContextPath() + PLUGINS_RISK_CONSENT_PATH);
+ }
+
+ chain.doFilter(request, response);
+ }
+
+ @Override
+ public UrlPattern doGetPattern() {
+ return UrlPattern.builder()
+ .includes("/*")
+ .excludes(staticResourcePatterns())
+ .excludes(SKIPPED_URLS)
+ .build();
+ }
+
+ @Override
+ public void destroy() {
+ //nothing to do
+ }
+}
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
new file mode 100644
index 00000000000..523f65ce9fb
--- /dev/null
+++ b/server/sonar-webserver-core/src/test/java/org/sonar/server/plugins/PluginsRiskConsentFilterTest.java
@@ -0,0 +1,176 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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.plugins;
+
+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.core.extension.PluginRiskConsent;
+import org.sonar.server.plugins.PluginsRiskConsentFilter;
+import org.sonar.server.user.ThreadLocalUserSession;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.sonar.core.config.CorePropertyDefinitions.PLUGINS_RISK_CONSENT;
+import javax.servlet.FilterChain;
+import javax.servlet.ServletRequest;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.Optional;
+
+public class PluginsRiskConsentFilterTest {
+
+ private Configuration configuration;
+ private ThreadLocalUserSession userSession;
+
+ private ServletRequest servletRequest;
+ private HttpServletResponse servletResponse;
+ private FilterChain chain;
+
+ @Before
+ public void before() {
+ configuration = mock(Configuration.class);
+ when(configuration.get(PLUGINS_RISK_CONSENT)).thenReturn(Optional.of(PluginRiskConsent.REQUIRED.name()));
+ userSession = mock(ThreadLocalUserSession.class);
+
+ servletRequest = mock(HttpServletRequest.class);
+ servletResponse = mock(HttpServletResponse.class);
+ chain = mock(FilterChain.class);
+ }
+
+ @Test
+ public void doFilter_givenNoUserSession_dontRedirect() throws Exception {
+ PluginsRiskConsentFilter consentFilter = new PluginsRiskConsentFilter(configuration, userSession);
+
+ when(userSession.hasSession()).thenReturn(true);
+
+ consentFilter.doFilter(servletRequest, servletResponse, chain);
+
+ verify(servletResponse, times(0)).sendRedirect(Mockito.anyString());
+ }
+
+ @Test
+ public void doFilter_givenNotLoggedIn_dontRedirect() throws Exception {
+ PluginsRiskConsentFilter consentFilter = new PluginsRiskConsentFilter(configuration, userSession);
+
+ when(userSession.hasSession()).thenReturn(true);
+ when(userSession.isLoggedIn()).thenReturn(false);
+
+ consentFilter.doFilter(servletRequest, servletResponse, chain);
+
+ verify(servletResponse, times(0)).sendRedirect(Mockito.anyString());
+ }
+
+ @Test
+ public void doFilter_givenNotLoggedInAndRequired_dontRedirect() throws Exception {
+ PluginsRiskConsentFilter consentFilter = new PluginsRiskConsentFilter(configuration, userSession);
+
+ when(userSession.hasSession()).thenReturn(true);
+ when(userSession.isLoggedIn()).thenReturn(false);
+ when(configuration.get(PLUGINS_RISK_CONSENT)).thenReturn(Optional.of(PluginRiskConsent.REQUIRED.name()));
+
+ consentFilter.doFilter(servletRequest, servletResponse, chain);
+
+ verify(servletResponse, times(0)).sendRedirect(Mockito.anyString());
+ }
+
+ @Test
+ public void doFilter_givenNotLoggedInAndConsentAccepted_dontRedirect() throws Exception {
+ PluginsRiskConsentFilter consentFilter = new PluginsRiskConsentFilter(configuration, userSession);
+
+ when(userSession.hasSession()).thenReturn(true);
+ when(userSession.isLoggedIn()).thenReturn(false);
+ when(configuration.get(PLUGINS_RISK_CONSENT)).thenReturn(Optional.of(PluginRiskConsent.ACCEPTED.name()));
+
+ consentFilter.doFilter(servletRequest, servletResponse, chain);
+
+ verify(servletResponse, times(0)).sendRedirect(Mockito.anyString());
+ }
+
+ @Test
+ public void doFilter_givenLoggedInNotAdmin_dontRedirect() throws Exception {
+ PluginsRiskConsentFilter consentFilter = new PluginsRiskConsentFilter(configuration, userSession);
+
+ when(userSession.hasSession()).thenReturn(true);
+ when(userSession.isLoggedIn()).thenReturn(true);
+ when(userSession.isSystemAdministrator()).thenReturn(false);
+
+ consentFilter.doFilter(servletRequest, servletResponse, chain);
+
+ verify(servletResponse, times(0)).sendRedirect(Mockito.anyString());
+ }
+
+ @Test
+ public void doFilter_givenLoggedInNotAdminAndRequiredConsent_dontRedirect() throws Exception {
+ PluginsRiskConsentFilter consentFilter = new PluginsRiskConsentFilter(configuration, userSession);
+
+ when(userSession.hasSession()).thenReturn(true);
+ when(userSession.isLoggedIn()).thenReturn(true);
+ when(userSession.isSystemAdministrator()).thenReturn(false);
+ when(configuration.get(PLUGINS_RISK_CONSENT)).thenReturn(Optional.of(PluginRiskConsent.REQUIRED.name()));
+
+ consentFilter.doFilter(servletRequest, servletResponse, chain);
+
+ verify(servletResponse, times(0)).sendRedirect(Mockito.anyString());
+ }
+
+ @Test
+ public void doFilter_givenLoggedInAdminAndConsentRequired_redirect() throws Exception {
+ PluginsRiskConsentFilter consentFilter = new PluginsRiskConsentFilter(configuration, userSession);
+
+ when(userSession.hasSession()).thenReturn(true);
+ when(userSession.isLoggedIn()).thenReturn(true);
+ when(userSession.isSystemAdministrator()).thenReturn(true);
+
+ consentFilter.doFilter(servletRequest, servletResponse, chain);
+
+ verify(servletResponse, times(1)).sendRedirect(Mockito.anyString());
+ }
+
+ @Test
+ public void doFilter_givenLoggedInAdminAndConsentNotRequired_dontRedirect() throws Exception {
+ PluginsRiskConsentFilter consentFilter = new PluginsRiskConsentFilter(configuration, userSession);
+
+ when(userSession.hasSession()).thenReturn(true);
+ when(userSession.isLoggedIn()).thenReturn(true);
+ when(userSession.isSystemAdministrator()).thenReturn(true);
+ when(configuration.get(PLUGINS_RISK_CONSENT)).thenReturn(Optional.of(PluginRiskConsent.ACCEPTED.name()));
+
+ consentFilter.doFilter(servletRequest, servletResponse, chain);
+
+ verify(servletResponse, times(0)).sendRedirect(Mockito.anyString());
+ }
+
+ @Test
+ public void doGetPattern_excludesNotEmpty() {
+ PluginsRiskConsentFilter consentFilter = new PluginsRiskConsentFilter(configuration, userSession);
+
+ ServletFilter.UrlPattern urlPattern = consentFilter.doGetPattern();
+
+ assertThat(urlPattern.getExclusions()).isNotEmpty();
+
+ }
+}
diff --git a/server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java b/server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java
index 502f560c366..0b2cdeb0393 100644
--- a/server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java
+++ b/server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java
@@ -19,7 +19,6 @@
*/
package org.sonar.server.platform.platformlevel;
-import java.util.List;
import org.sonar.alm.client.TimeoutConfigurationImpl;
import org.sonar.alm.client.azure.AzureDevOpsHttpClient;
import org.sonar.alm.client.bitbucket.bitbucketcloud.BitbucketCloudRestClient;
@@ -181,6 +180,7 @@ import org.sonar.server.qualityprofile.QProfileTreeImpl;
import org.sonar.server.qualityprofile.RuleActivator;
import org.sonar.server.qualityprofile.index.ActiveRuleIndexer;
import org.sonar.server.qualityprofile.ws.QProfilesWsModule;
+import org.sonar.server.plugins.PluginsRiskConsentFilter;
import org.sonar.server.root.ws.RootWsModule;
import org.sonar.server.rule.CommonRuleDefinitionsImpl;
import org.sonar.server.rule.RuleCreator;
@@ -234,6 +234,7 @@ import org.sonar.server.ws.ws.WebServicesWsModule;
import static org.sonar.core.extension.CoreExtensionsInstaller.noAdditionalSideFilter;
import static org.sonar.core.extension.PlatformLevelPredicates.hasPlatformLevel4OrNone;
+import java.util.List;
public class PlatformLevel4 extends PlatformLevel {
@@ -557,7 +558,9 @@ public class PlatformLevel4 extends PlatformLevel {
TelemetryDataLoaderImpl.class,
TelemetryDataJsonWriter.class,
TelemetryDaemon.class,
- TelemetryClient.class
+ TelemetryClient.class,
+
+ PluginsRiskConsentFilter.class
);