]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-14662 Redirecting admin to the risk consent page
authorLukasz Jarocki <lukasz.jarocki@sonarsource.com>
Fri, 9 Apr 2021 13:00:09 +0000 (15:00 +0200)
committersonartech <sonartech@sonarsource.com>
Thu, 15 Apr 2021 20:03:44 +0000 (20:03 +0000)
server/sonar-webserver-core/src/main/java/org/sonar/server/plugins/PluginsRiskConsentFilter.java [new file with mode: 0644]
server/sonar-webserver-core/src/test/java/org/sonar/server/plugins/PluginsRiskConsentFilterTest.java [new file with mode: 0644]
server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java

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 (file)
index 0000000..998c932
--- /dev/null
@@ -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 (file)
index 0000000..523f65c
--- /dev/null
@@ -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();
+
+  }
+}
index 502f560c36610f1232d442ec3b3b8e380debc957..0b2cdeb039389b917f7da446c8c00a4533676e93 100644 (file)
@@ -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
 
     );