]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-21116 Fix api/authentication/validate doesnt validate user tokens
authorantoine.vinot <antoine.vinot@sonarsource.com>
Mon, 29 Jan 2024 15:58:48 +0000 (16:58 +0100)
committersonartech <sonartech@sonarsource.com>
Tue, 30 Jan 2024 20:03:01 +0000 (20:03 +0000)
server/sonar-webserver-webapi/src/main/java/org/sonar/server/authentication/ws/ValidateAction.java
server/sonar-webserver-webapi/src/test/java/org/sonar/server/authentication/ws/ValidateActionTest.java

index 412ff665ff4d04e12f9a3f511dc0080936ca8908..51d9c0b7ea5ceff7712ae9d422af3dd37451592c 100644 (file)
@@ -32,8 +32,11 @@ 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.HttpHeadersAuthentication;
 import org.sonar.server.authentication.JwtHttpHandler;
+import org.sonar.server.authentication.UserAuthResult;
 import org.sonar.server.authentication.event.AuthenticationException;
+import org.sonar.server.usertoken.UserTokenAuthentication;
 import org.sonar.server.ws.ServletFilterHandler;
 import org.sonarqube.ws.MediaTypes;
 
@@ -49,11 +52,17 @@ public class ValidateAction extends HttpFilter implements AuthenticationWsAction
   private final Configuration config;
   private final JwtHttpHandler jwtHttpHandler;
   private final BasicAuthentication basicAuthentication;
+  private final HttpHeadersAuthentication httpHeadersAuthentication;
+  private final UserTokenAuthentication userTokenAuthentication;
+
+  public ValidateAction(Configuration config, BasicAuthentication basicAuthentication, JwtHttpHandler jwtHttpHandler, HttpHeadersAuthentication httpHeadersAuthentication,
+    UserTokenAuthentication userTokenAuthentication) {
 
-  public ValidateAction(Configuration config, BasicAuthentication basicAuthentication, JwtHttpHandler jwtHttpHandler) {
     this.config = config;
     this.basicAuthentication = basicAuthentication;
     this.jwtHttpHandler = jwtHttpHandler;
+    this.httpHeadersAuthentication = httpHeadersAuthentication;
+    this.userTokenAuthentication = userTokenAuthentication;
   }
 
   @Override
@@ -84,15 +93,11 @@ public class ValidateAction extends HttpFilter implements AuthenticationWsAction
 
   private boolean authenticate(HttpRequest request, HttpResponse response) {
     try {
-      Optional<UserDto> user = jwtHttpHandler.validateToken(request, response);
-      if (user.isPresent()) {
-        return true;
-      }
-      user = basicAuthentication.authenticate(request);
-      if (user.isPresent()) {
-        return true;
-      }
-      return !config.getBoolean(CORE_FORCE_AUTHENTICATION_PROPERTY).orElse(CORE_FORCE_AUTHENTICATION_DEFAULT_VALUE);
+      Optional<UserDto> user = httpHeadersAuthentication.authenticate(request, response)
+        .or(() -> jwtHttpHandler.validateToken(request, response))
+        .or(() -> basicAuthentication.authenticate(request))
+        .or(() -> userTokenAuthentication.authenticate(request).map(UserAuthResult::getUserDto));
+      return user.isPresent() || !config.getBoolean(CORE_FORCE_AUTHENTICATION_PROPERTY).orElse(CORE_FORCE_AUTHENTICATION_DEFAULT_VALUE);
     } catch (AuthenticationException e) {
       return false;
     }
index 861fefd3055b5c4d100688968ff0d7ac755af3dc..f3e59d8e9f95c4f2c198daf01a899d77822b4f93 100644 (file)
@@ -19,6 +19,7 @@
  */
 package org.sonar.server.authentication.ws;
 
+import java.io.IOException;
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.util.Optional;
@@ -30,8 +31,11 @@ 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.HttpHeadersAuthentication;
 import org.sonar.server.authentication.JwtHttpHandler;
+import org.sonar.server.authentication.UserAuthResult;
 import org.sonar.server.authentication.event.AuthenticationException;
+import org.sonar.server.usertoken.UserTokenAuthentication;
 import org.sonar.server.ws.ServletFilterHandler;
 import org.sonar.test.JsonAssert;
 import org.sonarqube.ws.MediaTypes;
@@ -42,6 +46,7 @@ import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 import static org.sonar.db.user.UserTesting.newUserDto;
+import static org.sonar.server.authentication.UserAuthResult.AuthType.TOKEN;
 
 public class ValidateActionTest {
 
@@ -53,19 +58,25 @@ public class ValidateActionTest {
 
   private final BasicAuthentication basicAuthentication = mock(BasicAuthentication.class);
   private final JwtHttpHandler jwtHttpHandler = mock(JwtHttpHandler.class);
+  private final HttpHeadersAuthentication httpHeadersAuthentication = mock(HttpHeadersAuthentication.class);
+  private final UserTokenAuthentication userTokenAuthentication = mock(UserTokenAuthentication.class);
 
   private final MapSettings settings = new MapSettings();
 
-  private final ValidateAction underTest = new ValidateAction(settings.asConfig(), basicAuthentication, jwtHttpHandler);
+  private final ValidateAction underTest = new ValidateAction(settings.asConfig(), basicAuthentication, jwtHttpHandler, httpHeadersAuthentication, userTokenAuthentication);
 
   @Before
   public void setUp() throws Exception {
     PrintWriter writer = new PrintWriter(stringWriter);
     when(response.getWriter()).thenReturn(writer);
+    when(basicAuthentication.authenticate(request)).thenReturn(Optional.empty());
+    when(jwtHttpHandler.validateToken(request, response)).thenReturn(Optional.empty());
+    when(httpHeadersAuthentication.authenticate(request, response)).thenReturn(Optional.empty());
+    when(userTokenAuthentication.authenticate(request)).thenReturn(Optional.empty());
   }
 
   @Test
-  public void verify_definition() {
+  public void define_shouldDefineWS() {
     String controllerKey = "foo";
     WebService.Context context = new WebService.Context();
     WebService.NewController newController = context.createController(controllerKey);
@@ -80,81 +91,91 @@ public class ValidateActionTest {
   }
 
   @Test
-  public void return_true_when_jwt_token_is_set() throws Exception {
+  public void doFilter_whenJwtToken_shouldReturnTrue() throws Exception {
     when(jwtHttpHandler.validateToken(request, response)).thenReturn(Optional.of(newUserDto()));
-    when(basicAuthentication.authenticate(request)).thenReturn(Optional.empty());
-
     underTest.doFilter(request, response, chain);
-
-    verify(response).setContentType(MediaTypes.JSON);
-    JsonAssert.assertJson(stringWriter.toString()).isSimilarTo("{\"valid\":true}");
+    verifyResponseIsTrue();
   }
 
   @Test
-  public void return_true_when_basic_auth() throws Exception {
-    when(jwtHttpHandler.validateToken(request, response)).thenReturn(Optional.empty());
+  public void doFilter_whenBasicAuth_shouldReturnTrue() throws Exception {
     when(basicAuthentication.authenticate(request)).thenReturn(Optional.of(newUserDto()));
-
     underTest.doFilter(request, response, chain);
-
-    verify(response).setContentType(MediaTypes.JSON);
-    JsonAssert.assertJson(stringWriter.toString()).isSimilarTo("{\"valid\":true}");
+    verifyResponseIsTrue();
   }
 
   @Test
-  public void return_true_when_no_jwt_nor_basic_auth_and_no_force_authentication() throws Exception {
+  public void doFilter_whenNoForceAuthentication_shoudlReturnTrue() throws Exception {
     settings.setProperty("sonar.forceAuthentication", "false");
-    when(jwtHttpHandler.validateToken(request, response)).thenReturn(Optional.empty());
-    when(basicAuthentication.authenticate(request)).thenReturn(Optional.empty());
-
     underTest.doFilter(request, response, chain);
-
-    verify(response).setContentType(MediaTypes.JSON);
-    JsonAssert.assertJson(stringWriter.toString()).isSimilarTo("{\"valid\":true}");
+    verifyResponseIsTrue();
   }
 
   @Test
-  public void return_false_when_no_jwt_nor_basic_auth_and_force_authentication_is_true() throws Exception {
+  public void doFilter_whenForceAuthentication_shouldReturnFalse() throws Exception {
     settings.setProperty("sonar.forceAuthentication", "true");
-    when(jwtHttpHandler.validateToken(request, response)).thenReturn(Optional.empty());
-    when(basicAuthentication.authenticate(request)).thenReturn(Optional.empty());
-
     underTest.doFilter(request, response, chain);
-
-    verify(response).setContentType(MediaTypes.JSON);
-    JsonAssert.assertJson(stringWriter.toString()).isSimilarTo("{\"valid\":false}");
+    verifyResponseIsFalse();
   }
 
   @Test
-  public void return_false_when_no_jwt_nor_basic_auth_and_force_authentication_fallback_to_default() throws Exception {
-    when(jwtHttpHandler.validateToken(request, response)).thenReturn(Optional.empty());
-    when(basicAuthentication.authenticate(request)).thenReturn(Optional.empty());
-
+  public void doFilter_whenDefaultForceAuthentication_shouldReturnFalse() throws Exception {
     underTest.doFilter(request, response, chain);
-
-    verify(response).setContentType(MediaTypes.JSON);
-    JsonAssert.assertJson(stringWriter.toString()).isSimilarTo("{\"valid\":false}");
+    verifyResponseIsFalse();
   }
 
   @Test
-  public void return_false_when_jwt_throws_unauthorized_exception() throws Exception {
+  public void doFilter_whenJwtThrowsUnauthorizedException_shouldReturnFalse() throws Exception {
     doThrow(AuthenticationException.class).when(jwtHttpHandler).validateToken(request, response);
-    when(basicAuthentication.authenticate(request)).thenReturn(Optional.empty());
+    underTest.doFilter(request, response, chain);
+    verifyResponseIsFalse();
+  }
 
+  @Test
+  public void doFilter_whenBasicAuthenticatorThrowsUnauthorizedException_shouldReturnFalse() throws Exception {
+    doThrow(AuthenticationException.class).when(basicAuthentication).authenticate(request);
     underTest.doFilter(request, response, chain);
+    verifyResponseIsFalse();
+  }
 
-    verify(response).setContentType(MediaTypes.JSON);
-    JsonAssert.assertJson(stringWriter.toString()).isSimilarTo("{\"valid\":false}");
+  @Test
+  public void doFiler_whenHttpHeaderAuthentication_shouldReturnTrue() throws IOException {
+    when(httpHeadersAuthentication.authenticate(request, response)).thenReturn(Optional.of(newUserDto()));
+    underTest.doFilter(request, response, chain);
+    verifyResponseIsTrue();
   }
 
   @Test
-  public void return_false_when_basic_authenticator_throws_unauthorized_exception() throws Exception {
-    when(jwtHttpHandler.validateToken(request, response)).thenReturn(Optional.empty());
-    doThrow(AuthenticationException.class).when(basicAuthentication).authenticate(request);
+  public void doFiler_whenHttpHeaderAuthenticationThrowsUnauthorizedException_shouldReturnFalse() throws IOException {
+    doThrow(AuthenticationException.class).when(httpHeadersAuthentication).authenticate(request, response);
+    underTest.doFilter(request, response, chain);
+    verifyResponseIsFalse();
+  }
+
+  @Test
+  public void doFilter_whenUserTokenAuthentication_shouldReturnTrue() throws IOException {
+    when(userTokenAuthentication.authenticate(request)).thenReturn(Optional.of(new UserAuthResult(newUserDto(), TOKEN)));
+    underTest.doFilter(request, response, chain);
+    verifyResponseIsTrue();
+  }
 
+  @Test
+  public void doFiler_whenUserTokenAuthenticationThrowsUnauthorizedException_shouldReturnFalse() throws IOException {
+    doThrow(AuthenticationException.class).when(httpHeadersAuthentication).authenticate(request, response);
     underTest.doFilter(request, response, chain);
+    verifyResponseIsFalse();
+  }
+
+  private void verifyResponseIsFalse() {
+    verifyResponse("{\"valid\":false}");
+  }
+
+  private void verifyResponseIsTrue() {
+    verifyResponse("{\"valid\":true}");
+  }
 
+  private void verifyResponse(String expectedJson) {
     verify(response).setContentType(MediaTypes.JSON);
-    JsonAssert.assertJson(stringWriter.toString()).isSimilarTo("{\"valid\":false}");
+    JsonAssert.assertJson(stringWriter.toString()).isSimilarTo(expectedJson);
   }
 }