aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-webserver-auth
diff options
context:
space:
mode:
authorSteve Marion <steve.marion@sonarsource.com>2022-09-20 17:05:22 +0200
committersonartech <sonartech@sonarsource.com>2022-09-22 20:03:32 +0000
commit02fed618e278701358310cac4018ab81f87d5c91 (patch)
tree309a738234578667ebfef272d1b7df04c2bc932d /server/sonar-webserver-auth
parentc8b5142dfe1f772f212e740cf54dc487cabac83d (diff)
downloadsonarqube-02fed618e278701358310cac4018ab81f87d5c91.tar.gz
sonarqube-02fed618e278701358310cac4018ab81f87d5c91.zip
SONAR-17296 fix SAML validation page not being accessible with a user session.
Diffstat (limited to 'server/sonar-webserver-auth')
-rw-r--r--server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/SamlValidationRedirectionFilter.java33
-rw-r--r--server/sonar-webserver-auth/src/main/resources/validation-redirection.html19
-rw-r--r--server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/SamlValidationRedirectionFilterTest.java48
3 files changed, 94 insertions, 6 deletions
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 d28daf1b72a..9a76357e5f6 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
@@ -20,13 +20,19 @@
package org.sonar.server.authentication;
+import com.google.common.io.Resources;
import java.io.IOException;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
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.apache.commons.lang.StringUtils;
+import org.sonar.api.internal.apachecommons.lang.StringEscapeUtils;
import org.sonar.api.platform.Server;
import org.sonar.api.web.ServletFilter;
@@ -36,6 +42,7 @@ public class SamlValidationRedirectionFilter extends ServletFilter {
public static final String VALIDATION_RELAY_STATE = "validation-query";
public static final String SAML_VALIDATION_URL = "/saml/validation_callback";
+ private String redirectionPageTemplate;
private final Server server;
public SamlValidationRedirectionFilter(Server server) {
@@ -48,12 +55,34 @@ public class SamlValidationRedirectionFilter extends ServletFilter {
}
@Override
+ public void init(FilterConfig filterConfig) throws ServletException {
+ super.init(filterConfig);
+ this.redirectionPageTemplate = extractTemplate("validation-redirection.html");
+ }
+
+ String extractTemplate(String templateLocation) {
+ try {
+ URL url = Resources.getResource(templateLocation);
+ return Resources.toString(url, StandardCharsets.UTF_8);
+ } catch (IOException | IllegalArgumentException e) {
+ throw new IllegalStateException("Cannot read the template " + templateLocation, e);
+ }
+ }
+
+ @Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
if (isSamlValidation(httpRequest)) {
HttpServletResponse httpResponse = (HttpServletResponse) response;
- httpResponse.setStatus(HttpServletResponse.SC_TEMPORARY_REDIRECT);
- httpResponse.setHeader("Location", server.getContextPath() + SAML_VALIDATION_URL);
+
+ String samlResponse = StringEscapeUtils.escapeHtml(request.getParameter("SAMLResponse"));
+
+ String template = StringUtils.replaceEachRepeatedly(redirectionPageTemplate,
+ new String[]{"%VALIDATION_URL%", "%SAML_RESPONSE%"},
+ new String[]{server.getContextPath() + SAML_VALIDATION_URL, samlResponse});
+
+ httpResponse.setContentType("text/html");
+ httpResponse.getWriter().print(template);
return;
}
chain.doFilter(request, response);
diff --git a/server/sonar-webserver-auth/src/main/resources/validation-redirection.html b/server/sonar-webserver-auth/src/main/resources/validation-redirection.html
new file mode 100644
index 00000000000..df9a48a29d6
--- /dev/null
+++ b/server/sonar-webserver-auth/src/main/resources/validation-redirection.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta name="application-name" content="SonarQube"/>
+ <meta http-equiv="content-type" content="text/html; charset=UTF-8" charset="UTF-8"/>
+ <title>SAML Authentication Test</title>
+</head>
+
+<body>
+ <h1>SAML Authentication Validation</h1>
+ <form id="saml_validate" action="%VALIDATION_URL%" method="POST">
+ <input name="SAMLResponse" value="%SAML_RESPONSE%" type="hidden"/>
+ <button>Click Here to See Result</button>
+ </form>
+</body>
+<script>
+ document.getElementById("saml_validate").submit()
+</script>
+</html>
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 ba54dddc33a..5dc41eea3cb 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
@@ -21,29 +21,36 @@
package org.sonar.server.authentication;
import java.io.IOException;
+import java.io.PrintWriter;
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.mockito.ArgumentCaptor;
import org.sonar.api.platform.Server;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.assertThrows;
+import static org.mockito.ArgumentMatchers.matches;
import static org.mockito.Mockito.doReturn;
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 SamlValidationRedirectionFilterTest {
SamlValidationRedirectionFilter underTest;
@Before
- public void setup() {
+ public void setup() throws ServletException {
Server server = mock(Server.class);
doReturn("").when(server).getContextPath();
underTest = new SamlValidationRedirectionFilter(server);
+ underTest.init(mock(FilterConfig.class));
}
@Test
@@ -60,11 +67,39 @@ public class SamlValidationRedirectionFilterTest {
HttpServletResponse servletResponse = mock(HttpServletResponse.class);
FilterChain filterChain = mock(FilterChain.class);
- doReturn("validation-query").when(servletRequest).getParameter("RelayState");
+ String validSample = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
+ when(servletRequest.getParameter(matches("SAMLResponse"))).thenReturn(validSample);
+ when(servletRequest.getParameter(matches("RelayState"))).thenReturn("validation-query");
+ PrintWriter pw = mock(PrintWriter.class);
+ when(servletResponse.getWriter()).thenReturn(pw);
+
underTest.doFilter(servletRequest, servletResponse, filterChain);
- verify(servletResponse).setStatus(HttpServletResponse.SC_TEMPORARY_REDIRECT);
- verify(servletResponse).setHeader("Location", "/saml/validation_callback");
+ verify(servletResponse).setContentType("text/html");
+ ArgumentCaptor<String> htmlProduced = ArgumentCaptor.forClass(String.class);
+ verify(pw).print(htmlProduced.capture());
+ assertThat(htmlProduced.getValue()).contains(validSample);
+ }
+
+ @Test
+ public void do_filter_validation_wrong_SAML_response() throws ServletException, IOException {
+ HttpServletRequest servletRequest = mock(HttpServletRequest.class);
+ HttpServletResponse servletResponse = mock(HttpServletResponse.class);
+ FilterChain filterChain = mock(FilterChain.class);
+
+ String maliciousSaml = "test\"</input><script>/*hack website*/</script><input value=\"";
+
+ when(servletRequest.getParameter(matches("SAMLResponse"))).thenReturn(maliciousSaml);
+ when(servletRequest.getParameter(matches("RelayState"))).thenReturn("validation-query");
+ PrintWriter pw = mock(PrintWriter.class);
+ when(servletResponse.getWriter()).thenReturn(pw);
+
+ underTest.doFilter(servletRequest, servletResponse, filterChain);
+
+ verify(servletResponse).setContentType("text/html");
+ ArgumentCaptor<String> htmlProduced = ArgumentCaptor.forClass(String.class);
+ verify(pw).print(htmlProduced.capture());
+ assertThat(htmlProduced.getValue()).doesNotContain("<script>/*hack website*/</script>");
}
@Test
@@ -78,4 +113,9 @@ public class SamlValidationRedirectionFilterTest {
verifyNoInteractions(servletResponse);
}
+
+ @Test
+ public void extract_nonexistant_template() {
+ assertThrows(IllegalStateException.class, () -> underTest.extractTemplate("not-there"));
+ }
}