]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-17296 move validation callback under /saml controller alongside init action
authorSteve Marion <steve.marion@sonarsource.com>
Tue, 20 Sep 2022 14:31:42 +0000 (16:31 +0200)
committersonartech <sonartech@sonarsource.com>
Thu, 22 Sep 2022 20:03:32 +0000 (20:03 +0000)
13 files changed:
server/sonar-web/src/main/js/apps/settings/components/authentication/SamlAuthentication.tsx
server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/SamlValidationRedirectionFilter.java
server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/SamlValidationRedirectionFilterTest.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/saml/ws/SamlValidationCallbackFilter.java [deleted file]
server/sonar-webserver-webapi/src/main/java/org/sonar/server/saml/ws/SamlValidationInitAction.java [deleted file]
server/sonar-webserver-webapi/src/main/java/org/sonar/server/saml/ws/SamlValidationModule.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/saml/ws/SamlValidationWs.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/saml/ws/ValidationAction.java [new file with mode: 0644]
server/sonar-webserver-webapi/src/main/java/org/sonar/server/saml/ws/ValidationInitAction.java [new file with mode: 0644]
server/sonar-webserver-webapi/src/test/java/org/sonar/server/saml/ws/SamlValidationCallbackFilterTest.java [deleted file]
server/sonar-webserver-webapi/src/test/java/org/sonar/server/saml/ws/SamlValidationInitActionTest.java [deleted file]
server/sonar-webserver-webapi/src/test/java/org/sonar/server/saml/ws/ValidationActionTest.java [new file with mode: 0644]
server/sonar-webserver-webapi/src/test/java/org/sonar/server/saml/ws/ValidationInitActionTest.java [new file with mode: 0644]

index e0ccf340bfa68bffbce02e0c388a5e4f8da4b4b2..f6070383c109b9db30bc86b8407afb36baf4148e 100644 (file)
@@ -49,7 +49,7 @@ interface SamlAuthenticationState {
   success?: boolean;
 }
 
-const CONFIG_TEST_PATH = '/api/saml/validation_init';
+const CONFIG_TEST_PATH = '/saml/validation_init';
 
 const SAML_ENABLED_FIELD = 'sonar.auth.saml.enabled';
 
index 9a76357e5f6d2e6ac4c0c768adb891f23e0b4c03..7d66c9047f0607fc8a1689ddf64a20aaf476d0e5 100644 (file)
@@ -22,6 +22,7 @@ package org.sonar.server.authentication;
 
 import com.google.common.io.Resources;
 import java.io.IOException;
+import java.net.URI;
 import java.net.URL;
 import java.nio.charset.StandardCharsets;
 import javax.servlet.FilterChain;
@@ -41,7 +42,8 @@ import static org.sonar.server.authentication.AuthenticationFilter.CALLBACK_PATH
 public class SamlValidationRedirectionFilter extends ServletFilter {
 
   public static final String VALIDATION_RELAY_STATE = "validation-query";
-  public static final String SAML_VALIDATION_URL = "/saml/validation_callback";
+  public static final String SAML_VALIDATION_CONTROLLER_CONTEXT = "saml";
+  public static final String SAML_VALIDATION_KEY = "validation";
   private String redirectionPageTemplate;
   private final Server server;
 
@@ -76,10 +78,13 @@ public class SamlValidationRedirectionFilter extends ServletFilter {
       HttpServletResponse httpResponse = (HttpServletResponse) response;
 
       String samlResponse = StringEscapeUtils.escapeHtml(request.getParameter("SAMLResponse"));
+      URI redirectionEndpointUrl = URI.create(server.getContextPath() + "/")
+        .resolve(SAML_VALIDATION_CONTROLLER_CONTEXT + "/")
+        .resolve(SAML_VALIDATION_KEY);
 
       String template = StringUtils.replaceEachRepeatedly(redirectionPageTemplate,
         new String[]{"%VALIDATION_URL%", "%SAML_RESPONSE%"},
-        new String[]{server.getContextPath() + SAML_VALIDATION_URL, samlResponse});
+        new String[]{redirectionEndpointUrl.toString(), samlResponse});
 
       httpResponse.setContentType("text/html");
       httpResponse.getWriter().print(template);
index 5dc41eea3cb24be3163a3452e377c63a670887b4..b27c68a5f1f06c6e2adcd4cbdb12d0e014fba66b 100644 (file)
@@ -100,6 +100,7 @@ public class SamlValidationRedirectionFilterTest {
     ArgumentCaptor<String> htmlProduced = ArgumentCaptor.forClass(String.class);
     verify(pw).print(htmlProduced.capture());
     assertThat(htmlProduced.getValue()).doesNotContain("<script>/*hack website*/</script>");
+    assertThat(htmlProduced.getValue()).contains("action=\"/saml/validation\"");
   }
 
   @Test
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/saml/ws/SamlValidationCallbackFilter.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/saml/ws/SamlValidationCallbackFilter.java
deleted file mode 100644 (file)
index 439a1b3..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2022 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.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.HttpServletRequestWrapper;
-import javax.servlet.http.HttpServletResponse;
-import org.sonar.api.web.ServletFilter;
-import org.sonar.auth.saml.SamlAuthenticator;
-import org.sonar.auth.saml.SamlIdentityProvider;
-import org.sonar.server.authentication.AuthenticationError;
-import org.sonar.server.authentication.OAuth2ContextFactory;
-import org.sonar.server.user.ThreadLocalUserSession;
-
-import static org.sonar.server.authentication.SamlValidationRedirectionFilter.SAML_VALIDATION_URL;
-
-public class SamlValidationCallbackFilter extends ServletFilter {
-
-  private final ThreadLocalUserSession userSession;
-  private final SamlAuthenticator samlAuthenticator;
-  private final OAuth2ContextFactory oAuth2ContextFactory;
-
-  public SamlValidationCallbackFilter(ThreadLocalUserSession userSession, SamlAuthenticator samlAuthenticator, OAuth2ContextFactory oAuth2ContextFactory) {
-    this.samlAuthenticator = samlAuthenticator;
-    this.userSession = userSession;
-    this.oAuth2ContextFactory = oAuth2ContextFactory;
-  }
-
-  @Override
-  public UrlPattern doGetPattern() {
-    return UrlPattern.create(SAML_VALIDATION_URL);
-  }
-
-  @Override
-  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
-    HttpServletResponse httpResponse = (HttpServletResponse) response;
-    HttpServletRequest httpRequest = (HttpServletRequest) request;
-    if (!userSession.hasSession() || !userSession.isSystemAdministrator()) {
-      AuthenticationError.handleError(httpRequest, httpResponse, "User needs to be logged in as system administrator to access this page.");
-      return;
-    }
-
-    httpRequest = new HttpServletRequestWrapper(httpRequest) {
-      @Override
-      public StringBuffer getRequestURL() {
-        return new StringBuffer(oAuth2ContextFactory.generateCallbackUrl(SamlIdentityProvider.KEY));
-      }
-    };
-
-    httpResponse.setContentType("text/html");
-    httpResponse.getWriter().print(samlAuthenticator.getAuthenticationStatusPage(httpRequest, httpResponse));
-  }
-}
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/saml/ws/SamlValidationInitAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/saml/ws/SamlValidationInitAction.java
deleted file mode 100644 (file)
index 6837be7..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2022 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.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.ws.WebService;
-import org.sonar.api.web.ServletFilter;
-import org.sonar.auth.saml.SamlAuthenticator;
-import org.sonar.auth.saml.SamlIdentityProvider;
-import org.sonar.server.authentication.AuthenticationError;
-import org.sonar.server.authentication.OAuth2ContextFactory;
-import org.sonar.server.exceptions.ForbiddenException;
-import org.sonar.server.user.UserSession;
-import org.sonar.server.ws.ServletFilterHandler;
-
-public class SamlValidationInitAction extends ServletFilter implements SamlAction {
-
-  public static final String VALIDATION_RELAY_STATE = "validation-query";
-  private final SamlAuthenticator samlAuthenticator;
-
-  private final OAuth2ContextFactory oAuth2ContextFactory;
-  private final UserSession userSession;
-
-  public SamlValidationInitAction(SamlAuthenticator samlAuthenticator, OAuth2ContextFactory oAuth2ContextFactory, UserSession userSession) {
-    this.samlAuthenticator = samlAuthenticator;
-    this.oAuth2ContextFactory = oAuth2ContextFactory;
-    this.userSession = userSession;
-  }
-
-  @Override
-  public UrlPattern doGetPattern() {
-    return UrlPattern.create("/api/saml/validation_init");
-  }
-
-  @Override
-  public void define(WebService.NewController controller) {
-    controller
-      .createAction("validation_init")
-      .setInternal(true)
-      .setPost(false)
-      .setHandler(ServletFilterHandler.INSTANCE)
-      .setDescription("Initiate a SAML request to the identity Provider for configuration validation purpose.")
-      .setSince("9.7");
-  }
-
-  @Override
-  public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
-    HttpServletRequest request = (HttpServletRequest) servletRequest;
-    HttpServletResponse response = (HttpServletResponse) servletResponse;
-
-    try {
-      userSession.checkIsSystemAdministrator();
-    } catch (ForbiddenException e) {
-      AuthenticationError.handleError(request, response, "User needs to be logged in as system administrator to access this page.");
-      return;
-    }
-
-    samlAuthenticator.initLogin(oAuth2ContextFactory.generateCallbackUrl(SamlIdentityProvider.KEY),
-      VALIDATION_RELAY_STATE, request, response);
-  }
-}
index 602ed55959a8567961a276a891e31a1a382af1a0..ce51a4a06fcdd5dd1ee4fe01a1984f0759878d67 100644 (file)
@@ -26,8 +26,8 @@ public class SamlValidationModule extends Module {
   protected void configureModule() {
     add(
       SamlValidationWs.class,
-      SamlValidationInitAction.class,
-      SamlValidationCallbackFilter.class
+      ValidationInitAction.class,
+      ValidationAction.class
     );
   }
 }
index 52529579be979afe0491f73da74de29ae56ffc89..198e24d614bba792f6f88e8668dbe4862fe84331 100644 (file)
@@ -21,10 +21,11 @@ package org.sonar.server.saml.ws;
 
 import java.util.List;
 import org.sonar.api.server.ws.WebService;
+import org.sonar.server.authentication.SamlValidationRedirectionFilter;
 
 public class SamlValidationWs implements WebService {
 
-  public static final String SAML_VALIDATION_CONTROLLER = "api/saml";
+  public static final String SAML_VALIDATION_CONTROLLER = SamlValidationRedirectionFilter.SAML_VALIDATION_CONTROLLER_CONTEXT;
   private final List<SamlAction> actions;
 
   public SamlValidationWs(List<SamlAction> actions) {
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
new file mode 100644 (file)
index 0000000..4b58458
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 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.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.HttpServletRequestWrapper;
+import javax.servlet.http.HttpServletResponse;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.api.web.ServletFilter;
+import org.sonar.auth.saml.SamlAuthenticator;
+import org.sonar.auth.saml.SamlIdentityProvider;
+import org.sonar.server.authentication.AuthenticationError;
+import org.sonar.server.authentication.OAuth2ContextFactory;
+import org.sonar.server.authentication.SamlValidationRedirectionFilter;
+import org.sonar.server.user.ThreadLocalUserSession;
+import org.sonar.server.ws.ServletFilterHandler;
+
+public class ValidationAction extends ServletFilter implements SamlAction {
+
+  static final String VALIDATION_CALLBACK_KEY = SamlValidationRedirectionFilter.SAML_VALIDATION_KEY;
+  private final ThreadLocalUserSession userSession;
+  private final SamlAuthenticator samlAuthenticator;
+  private final OAuth2ContextFactory oAuth2ContextFactory;
+
+  public ValidationAction(ThreadLocalUserSession userSession, SamlAuthenticator samlAuthenticator, OAuth2ContextFactory oAuth2ContextFactory) {
+    this.samlAuthenticator = samlAuthenticator;
+    this.userSession = userSession;
+    this.oAuth2ContextFactory = oAuth2ContextFactory;
+  }
+
+  @Override
+  public UrlPattern doGetPattern() {
+    return UrlPattern.create("/" + SamlValidationWs.SAML_VALIDATION_CONTROLLER + "/" + VALIDATION_CALLBACK_KEY);
+  }
+
+  @Override
+  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
+    HttpServletResponse httpResponse = (HttpServletResponse) response;
+    HttpServletRequest httpRequest = (HttpServletRequest) request;
+    if (!userSession.hasSession() || !userSession.isSystemAdministrator()) {
+      AuthenticationError.handleError(httpRequest, httpResponse, "User needs to be logged in as system administrator to access this page.");
+      return;
+    }
+
+    httpRequest = new HttpServletRequestWrapper(httpRequest) {
+      @Override
+      public StringBuffer getRequestURL() {
+        return new StringBuffer(oAuth2ContextFactory.generateCallbackUrl(SamlIdentityProvider.KEY));
+      }
+    };
+
+    httpResponse.setContentType("text/html");
+    httpResponse.getWriter().print(samlAuthenticator.getAuthenticationStatusPage(httpRequest, httpResponse));
+  }
+
+  @Override
+  public void define(WebService.NewController controller) {
+    WebService.NewAction action = controller
+      .createAction(VALIDATION_CALLBACK_KEY)
+      .setInternal(true)
+      .setPost(true)
+      .setHandler(ServletFilterHandler.INSTANCE)
+      .setDescription("Handle the callback of a SAML assertion from the identity Provider and produces " +
+        "a HTML page with all information available in the assertion.")
+      .setSince("9.7");
+    action.createParam("SAMLResponse")
+      .setDescription("SAML assertion value")
+      .setRequired(true);
+  }
+}
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
new file mode 100644 (file)
index 0000000..f882827
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 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.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.ws.WebService;
+import org.sonar.api.web.ServletFilter;
+import org.sonar.auth.saml.SamlAuthenticator;
+import org.sonar.auth.saml.SamlIdentityProvider;
+import org.sonar.server.authentication.AuthenticationError;
+import org.sonar.server.authentication.OAuth2ContextFactory;
+import org.sonar.server.exceptions.ForbiddenException;
+import org.sonar.server.user.UserSession;
+import org.sonar.server.ws.ServletFilterHandler;
+
+public class ValidationInitAction extends ServletFilter implements SamlAction {
+
+  public static final String VALIDATION_RELAY_STATE = "validation-query";
+  public static final String VALIDATION_INIT_KEY = "validation_init";
+  private final SamlAuthenticator samlAuthenticator;
+
+  private final OAuth2ContextFactory oAuth2ContextFactory;
+  private final UserSession userSession;
+
+  public ValidationInitAction(SamlAuthenticator samlAuthenticator, OAuth2ContextFactory oAuth2ContextFactory, UserSession userSession) {
+    this.samlAuthenticator = samlAuthenticator;
+    this.oAuth2ContextFactory = oAuth2ContextFactory;
+    this.userSession = userSession;
+  }
+
+  @Override
+  public UrlPattern doGetPattern() {
+    return UrlPattern.create("/" + SamlValidationWs.SAML_VALIDATION_CONTROLLER + "/" + VALIDATION_INIT_KEY);
+  }
+
+  @Override
+  public void define(WebService.NewController controller) {
+    controller
+      .createAction(VALIDATION_INIT_KEY)
+      .setInternal(true)
+      .setPost(false)
+      .setHandler(ServletFilterHandler.INSTANCE)
+      .setDescription("Initiate a SAML request to the identity Provider for configuration validation purpose.")
+      .setSince("9.7");
+  }
+
+  @Override
+  public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
+    HttpServletRequest request = (HttpServletRequest) servletRequest;
+    HttpServletResponse response = (HttpServletResponse) servletResponse;
+
+    try {
+      userSession.checkIsSystemAdministrator();
+    } catch (ForbiddenException e) {
+      AuthenticationError.handleError(request, response, "User needs to be logged in as system administrator to access this page.");
+      return;
+    }
+
+    samlAuthenticator.initLogin(oAuth2ContextFactory.generateCallbackUrl(SamlIdentityProvider.KEY),
+      VALIDATION_RELAY_STATE, request, response);
+  }
+}
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/saml/ws/SamlValidationCallbackFilterTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/saml/ws/SamlValidationCallbackFilterTest.java
deleted file mode 100644 (file)
index 27fd976..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2022 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.saml.ws;
-
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-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.auth.saml.SamlAuthenticator;
-import org.sonar.server.authentication.OAuth2ContextFactory;
-import org.sonar.server.user.ThreadLocalUserSession;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoInteractions;
-
-public class SamlValidationCallbackFilterTest {
-
-  private SamlValidationCallbackFilter underTest;
-  private SamlAuthenticator samlAuthenticator;
-  private ThreadLocalUserSession userSession;
-
-  @Before
-  public void setup() {
-    samlAuthenticator = mock(SamlAuthenticator.class);
-    userSession = mock(ThreadLocalUserSession.class);
-    var oAuth2ContextFactory = mock(OAuth2ContextFactory.class);
-    underTest = new SamlValidationCallbackFilter(userSession, samlAuthenticator, oAuth2ContextFactory);
-  }
-
-  @Test
-  public void do_get_pattern() {
-    assertThat(underTest.doGetPattern().matches("/saml/validation_callback")).isTrue();
-    assertThat(underTest.doGetPattern().matches("/saml/validation_callback2")).isFalse();
-    assertThat(underTest.doGetPattern().matches("/saml/")).isFalse();
-  }
-
-  @Test
-  public void do_filter_admin() throws ServletException, IOException {
-    HttpServletRequest servletRequest = mock(HttpServletRequest.class);
-    HttpServletResponse servletResponse = mock(HttpServletResponse.class);
-    StringWriter stringWriter = new StringWriter();
-    doReturn(new PrintWriter(stringWriter)).when(servletResponse).getWriter();
-    FilterChain filterChain = mock(FilterChain.class);
-
-    doReturn(true).when(userSession).hasSession();
-    doReturn(true).when(userSession).isSystemAdministrator();
-
-    underTest.doFilter(servletRequest, servletResponse, filterChain);
-
-    verify(samlAuthenticator).getAuthenticationStatusPage(any(), any());
-    verify(servletResponse).getWriter();
-  }
-
-  @Test
-  public void do_filter_not_authorized() throws ServletException, IOException {
-    HttpServletRequest servletRequest = spy(HttpServletRequest.class);
-    HttpServletResponse servletResponse = mock(HttpServletResponse.class);
-    StringWriter stringWriter = new StringWriter();
-    doReturn(new PrintWriter(stringWriter)).when(servletResponse).getWriter();
-    FilterChain filterChain = mock(FilterChain.class);
-
-    doReturn(true).when(userSession).hasSession();
-    doReturn(false).when(userSession).isSystemAdministrator();
-
-    underTest.doFilter(servletRequest, servletResponse, filterChain);
-
-    verifyNoInteractions(samlAuthenticator);
-  }
-}
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/saml/ws/SamlValidationInitActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/saml/ws/SamlValidationInitActionTest.java
deleted file mode 100644 (file)
index fde2872..0000000
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2022 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.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.ws.WebService;
-import org.sonar.auth.saml.SamlAuthenticator;
-import org.sonar.server.authentication.OAuth2ContextFactory;
-import org.sonar.server.tester.UserSessionRule;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.matches;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoInteractions;
-import static org.mockito.Mockito.when;
-
-public class SamlValidationInitActionTest {
-  @Rule
-  public UserSessionRule userSession = UserSessionRule.standalone();
-  private SamlValidationInitAction underTest;
-  private SamlAuthenticator samlAuthenticator;
-  private OAuth2ContextFactory oAuth2ContextFactory;
-
-  @Before
-  public void setUp() throws Exception {
-    samlAuthenticator = mock(SamlAuthenticator.class);
-    oAuth2ContextFactory = mock(OAuth2ContextFactory.class);
-    underTest = new SamlValidationInitAction(samlAuthenticator, oAuth2ContextFactory, userSession);
-  }
-
-  @Test
-  public void do_get_pattern() {
-    assertThat(underTest.doGetPattern().matches("/api/saml/validation_init")).isTrue();
-    assertThat(underTest.doGetPattern().matches("/api/saml")).isFalse();
-    assertThat(underTest.doGetPattern().matches("/api/saml/validation_init2")).isFalse();
-  }
-
-  @Test
-  public void do_filter_as_admin() throws IOException, ServletException {
-    userSession.logIn().setSystemAdministrator();
-    HttpServletRequest servletRequest = mock(HttpServletRequest.class);
-    HttpServletResponse servletResponse = mock(HttpServletResponse.class);
-    FilterChain filterChain = mock(FilterChain.class);
-    String callbackUrl = "http://localhost:9000/api/validation_test";
-    when(oAuth2ContextFactory.generateCallbackUrl(anyString()))
-      .thenReturn(callbackUrl);
-
-    underTest.doFilter(servletRequest, servletResponse, filterChain);
-
-    verify(samlAuthenticator).initLogin(matches(callbackUrl),
-      matches(SamlValidationInitAction.VALIDATION_RELAY_STATE),
-      any(), any());
-  }
-
-  @Test
-  public void do_filter_as_not_admin() throws IOException, ServletException {
-    userSession.logIn();
-    HttpServletRequest servletRequest = mock(HttpServletRequest.class);
-    HttpServletResponse servletResponse = mock(HttpServletResponse.class);
-    FilterChain filterChain = mock(FilterChain.class);
-    String callbackUrl = "http://localhost:9000/api/validation_test";
-    when(oAuth2ContextFactory.generateCallbackUrl(anyString()))
-      .thenReturn(callbackUrl);
-
-    underTest.doFilter(servletRequest, servletResponse, filterChain);
-
-    verifyNoInteractions(samlAuthenticator);
-    verify(servletResponse).sendRedirect(anyString());
-  }
-
-  @Test
-  public void do_filter_as_anonymous() throws IOException, ServletException {
-    userSession.anonymous();
-    HttpServletRequest servletRequest = mock(HttpServletRequest.class);
-    HttpServletResponse servletResponse = mock(HttpServletResponse.class);
-    FilterChain filterChain = mock(FilterChain.class);
-    String callbackUrl = "http://localhost:9000/api/validation_test";
-    when(oAuth2ContextFactory.generateCallbackUrl(anyString()))
-      .thenReturn(callbackUrl);
-
-    underTest.doFilter(servletRequest, servletResponse, filterChain);
-
-    verifyNoInteractions(samlAuthenticator);
-    verify(servletResponse).sendRedirect(anyString());
-  }
-
-  @Test
-  public void verify_definition() {
-    String controllerKey = "foo";
-    WebService.Context context = new WebService.Context();
-    WebService.NewController newController = context.createController(controllerKey);
-    underTest.define(newController);
-    newController.done();
-
-    WebService.Action validationInitAction = context.controller(controllerKey).action("validation_init");
-    assertThat(validationInitAction).isNotNull();
-    assertThat(validationInitAction.description()).isNotEmpty();
-    assertThat(validationInitAction.handler()).isNotNull();
-  }
-}
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
new file mode 100644 (file)
index 0000000..a00371d
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 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.saml.ws;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+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.ws.WebService;
+import org.sonar.auth.saml.SamlAuthenticator;
+import org.sonar.server.authentication.OAuth2ContextFactory;
+import org.sonar.server.user.ThreadLocalUserSession;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
+
+public class ValidationActionTest {
+
+  private ValidationAction underTest;
+  private SamlAuthenticator samlAuthenticator;
+  private ThreadLocalUserSession userSession;
+
+  @Before
+  public void setup() {
+    samlAuthenticator = mock(SamlAuthenticator.class);
+    userSession = mock(ThreadLocalUserSession.class);
+    var oAuth2ContextFactory = mock(OAuth2ContextFactory.class);
+    underTest = new ValidationAction(userSession, samlAuthenticator, oAuth2ContextFactory);
+  }
+
+  @Test
+  public void do_get_pattern() {
+    assertThat(underTest.doGetPattern().matches("/saml/validation")).isTrue();
+    assertThat(underTest.doGetPattern().matches("/saml/validation2")).isFalse();
+    assertThat(underTest.doGetPattern().matches("/api/saml/validation")).isFalse();
+    assertThat(underTest.doGetPattern().matches("/saml/validation_callback2")).isFalse();
+    assertThat(underTest.doGetPattern().matches("/saml/")).isFalse();
+  }
+
+  @Test
+  public void do_filter_admin() throws ServletException, IOException {
+    HttpServletRequest servletRequest = mock(HttpServletRequest.class);
+    HttpServletResponse servletResponse = mock(HttpServletResponse.class);
+    StringWriter stringWriter = new StringWriter();
+    doReturn(new PrintWriter(stringWriter)).when(servletResponse).getWriter();
+    FilterChain filterChain = mock(FilterChain.class);
+
+    doReturn(true).when(userSession).hasSession();
+    doReturn(true).when(userSession).isSystemAdministrator();
+
+    underTest.doFilter(servletRequest, servletResponse, filterChain);
+
+    verify(samlAuthenticator).getAuthenticationStatusPage(any(), any());
+    verify(servletResponse).getWriter();
+  }
+
+  @Test
+  public void do_filter_not_authorized() throws ServletException, IOException {
+    HttpServletRequest servletRequest = spy(HttpServletRequest.class);
+    HttpServletResponse servletResponse = mock(HttpServletResponse.class);
+    StringWriter stringWriter = new StringWriter();
+    doReturn(new PrintWriter(stringWriter)).when(servletResponse).getWriter();
+    FilterChain filterChain = mock(FilterChain.class);
+
+    doReturn(true).when(userSession).hasSession();
+    doReturn(false).when(userSession).isSystemAdministrator();
+
+    underTest.doFilter(servletRequest, servletResponse, filterChain);
+
+    verifyNoInteractions(samlAuthenticator);
+  }
+
+  @Test
+  public void verify_definition() {
+    String controllerKey = "foo";
+    WebService.Context context = new WebService.Context();
+    WebService.NewController newController = context.createController(controllerKey);
+    underTest.define(newController);
+    newController.done();
+
+    WebService.Action validationInitAction = context.controller(controllerKey)
+      .action(ValidationAction.VALIDATION_CALLBACK_KEY);
+    assertThat(validationInitAction).isNotNull();
+    assertThat(validationInitAction.description()).isNotEmpty();
+    assertThat(validationInitAction.handler()).isNotNull();
+  }
+}
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
new file mode 100644 (file)
index 0000000..618dce6
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 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.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.ws.WebService;
+import org.sonar.auth.saml.SamlAuthenticator;
+import org.sonar.server.authentication.OAuth2ContextFactory;
+import org.sonar.server.tester.UserSessionRule;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.matches;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
+import static org.mockito.Mockito.when;
+
+public class ValidationInitActionTest {
+  @Rule
+  public UserSessionRule userSession = UserSessionRule.standalone();
+  private ValidationInitAction underTest;
+  private SamlAuthenticator samlAuthenticator;
+  private OAuth2ContextFactory oAuth2ContextFactory;
+
+  @Before
+  public void setUp() throws Exception {
+    samlAuthenticator = mock(SamlAuthenticator.class);
+    oAuth2ContextFactory = mock(OAuth2ContextFactory.class);
+    underTest = new ValidationInitAction(samlAuthenticator, oAuth2ContextFactory, userSession);
+  }
+
+  @Test
+  public void do_get_pattern() {
+    assertThat(underTest.doGetPattern().matches("/saml/validation_init")).isTrue();
+    assertThat(underTest.doGetPattern().matches("/api/saml")).isFalse();
+    assertThat(underTest.doGetPattern().matches("/api/saml/validation_init")).isFalse();
+    assertThat(underTest.doGetPattern().matches("/saml/validation_init2")).isFalse();
+  }
+
+  @Test
+  public void do_filter_as_admin() throws IOException, ServletException {
+    userSession.logIn().setSystemAdministrator();
+    HttpServletRequest servletRequest = mock(HttpServletRequest.class);
+    HttpServletResponse servletResponse = mock(HttpServletResponse.class);
+    FilterChain filterChain = mock(FilterChain.class);
+    String callbackUrl = "http://localhost:9000/api/validation_test";
+    when(oAuth2ContextFactory.generateCallbackUrl(anyString()))
+      .thenReturn(callbackUrl);
+
+    underTest.doFilter(servletRequest, servletResponse, filterChain);
+
+    verify(samlAuthenticator).initLogin(matches(callbackUrl),
+      matches(ValidationInitAction.VALIDATION_RELAY_STATE),
+      any(), any());
+  }
+
+  @Test
+  public void do_filter_as_not_admin() throws IOException, ServletException {
+    userSession.logIn();
+    HttpServletRequest servletRequest = mock(HttpServletRequest.class);
+    HttpServletResponse servletResponse = mock(HttpServletResponse.class);
+    FilterChain filterChain = mock(FilterChain.class);
+    String callbackUrl = "http://localhost:9000/api/validation_test";
+    when(oAuth2ContextFactory.generateCallbackUrl(anyString()))
+      .thenReturn(callbackUrl);
+
+    underTest.doFilter(servletRequest, servletResponse, filterChain);
+
+    verifyNoInteractions(samlAuthenticator);
+    verify(servletResponse).sendRedirect(anyString());
+  }
+
+  @Test
+  public void do_filter_as_anonymous() throws IOException, ServletException {
+    userSession.anonymous();
+    HttpServletRequest servletRequest = mock(HttpServletRequest.class);
+    HttpServletResponse servletResponse = mock(HttpServletResponse.class);
+    FilterChain filterChain = mock(FilterChain.class);
+    String callbackUrl = "http://localhost:9000/api/validation_test";
+    when(oAuth2ContextFactory.generateCallbackUrl(anyString()))
+      .thenReturn(callbackUrl);
+
+    underTest.doFilter(servletRequest, servletResponse, filterChain);
+
+    verifyNoInteractions(samlAuthenticator);
+    verify(servletResponse).sendRedirect(anyString());
+  }
+
+  @Test
+  public void verify_definition() {
+    String controllerKey = "foo";
+    WebService.Context context = new WebService.Context();
+    WebService.NewController newController = context.createController(controllerKey);
+    underTest.define(newController);
+    newController.done();
+
+    WebService.Action validationInitAction = context.controller(controllerKey).action("validation_init");
+    assertThat(validationInitAction).isNotNull();
+    assertThat(validationInitAction.description()).isNotEmpty();
+    assertThat(validationInitAction.handler()).isNotNull();
+  }
+}