]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-7494 Fix IdentityProvider 876/head
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Thu, 31 Mar 2016 09:43:37 +0000 (11:43 +0200)
committerTeryk Bellahsene <teryk.bellahsene@sonarsource.com>
Thu, 31 Mar 2016 11:09:07 +0000 (13:09 +0200)
server/sonar-server/src/main/java/org/sonar/server/authentication/CsrfVerifier.java
server/sonar-server/src/main/java/org/sonar/server/authentication/InitFilter.java
server/sonar-server/src/main/java/org/sonar/server/authentication/OAuth2CallbackFilter.java
server/sonar-server/src/main/java/org/sonar/server/authentication/OAuth2ContextFactory.java
server/sonar-server/src/test/java/org/sonar/server/authentication/CsrfVerifierTest.java
server/sonar-server/src/test/java/org/sonar/server/authentication/InitFilterTest.java
server/sonar-server/src/test/java/org/sonar/server/authentication/OAuth2CallbackFilterTest.java
server/sonar-server/src/test/java/org/sonar/server/authentication/OAuth2ContextFactoryTest.java
server/sonar-web/src/main/webapp/WEB-INF/app/views/sessions/_form.html.erb

index 9a0371506fbdba4ffbc7b702abeeca52ab5f3ba5..20ea1417e2328596ca9c2b2ea44c471e82633c4c 100644 (file)
@@ -45,7 +45,7 @@ public class CsrfVerifier {
     // Store it in the session for later validation.
     String state = new BigInteger(130, new SecureRandom()).toString(32);
     Cookie cookie = new Cookie(CSRF_STATE_COOKIE, sha256Hex(state));
-    cookie.setPath("/");
+    cookie.setPath(server.getContextPath() + "/");
     cookie.setHttpOnly(true);
     cookie.setMaxAge(-1);
     cookie.setSecure(server.isSecured());
@@ -69,7 +69,7 @@ public class CsrfVerifier {
     // remove cookie
     stateCookie.setValue(null);
     stateCookie.setMaxAge(0);
-    stateCookie.setPath("/");
+    stateCookie.setPath(server.getContextPath() + "/");
     response.addCookie(stateCookie);
 
     String stateInRequest = request.getParameter("state");
index d7b9e2a960632e0cc66f43b48782ddc14d3a69f3..00e4a5b6e466210af19b09665e8d193e79a2cff6 100644 (file)
@@ -27,6 +27,7 @@ import javax.servlet.ServletRequest;
 import javax.servlet.ServletResponse;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
+import org.sonar.api.platform.Server;
 import org.sonar.api.server.authentication.BaseIdentityProvider;
 import org.sonar.api.server.authentication.IdentityProvider;
 import org.sonar.api.server.authentication.OAuth2IdentityProvider;
@@ -45,11 +46,13 @@ public class InitFilter extends ServletFilter {
   private final IdentityProviderRepository identityProviderRepository;
   private final BaseContextFactory baseContextFactory;
   private final OAuth2ContextFactory oAuth2ContextFactory;
+  private final Server server;
 
-  public InitFilter(IdentityProviderRepository identityProviderRepository, BaseContextFactory baseContextFactory, OAuth2ContextFactory oAuth2ContextFactory) {
+  public InitFilter(IdentityProviderRepository identityProviderRepository, BaseContextFactory baseContextFactory, OAuth2ContextFactory oAuth2ContextFactory, Server server) {
     this.identityProviderRepository = identityProviderRepository;
     this.baseContextFactory = baseContextFactory;
     this.oAuth2ContextFactory = oAuth2ContextFactory;
+    this.server = server;
   }
 
   @Override
@@ -63,7 +66,7 @@ public class InitFilter extends ServletFilter {
     String requestURI = httpRequest.getRequestURI();
     String keyProvider = "";
     try {
-      keyProvider = extractKeyProvider(requestURI, INIT_CONTEXT);
+      keyProvider = extractKeyProvider(requestURI, server.getContextPath() + INIT_CONTEXT);
       IdentityProvider provider = identityProviderRepository.getEnabledByKey(keyProvider);
       if (provider instanceof BaseIdentityProvider) {
         BaseIdentityProvider baseIdentityProvider = (BaseIdentityProvider) provider;
index 3c708eb1a4d1eb8e32c8fa617533f9e8ec4e6d35..f1ae0c5dcb9e2597c1988f56f1f5d8ed69313272 100644 (file)
@@ -28,6 +28,7 @@ import javax.servlet.ServletResponse;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import org.sonar.api.CoreProperties;
+import org.sonar.api.platform.Server;
 import org.sonar.api.server.authentication.IdentityProvider;
 import org.sonar.api.server.authentication.OAuth2IdentityProvider;
 import org.sonar.api.server.authentication.UnauthorizedException;
@@ -44,10 +45,12 @@ public class OAuth2CallbackFilter extends ServletFilter {
 
   private final IdentityProviderRepository identityProviderRepository;
   private final OAuth2ContextFactory oAuth2ContextFactory;
+  private final Server server;
 
-  public OAuth2CallbackFilter(IdentityProviderRepository identityProviderRepository, OAuth2ContextFactory oAuth2ContextFactory) {
+  public OAuth2CallbackFilter(IdentityProviderRepository identityProviderRepository, OAuth2ContextFactory oAuth2ContextFactory, Server server) {
     this.identityProviderRepository = identityProviderRepository;
     this.oAuth2ContextFactory = oAuth2ContextFactory;
+    this.server = server;
   }
 
   @Override
@@ -61,7 +64,7 @@ public class OAuth2CallbackFilter extends ServletFilter {
     String requestUri = httpRequest.getRequestURI();
     String keyProvider = "";
     try {
-      keyProvider = extractKeyProvider(requestUri, CALLBACK_PATH);
+      keyProvider = extractKeyProvider(requestUri, server.getContextPath() + CALLBACK_PATH);
       IdentityProvider provider = identityProviderRepository.getEnabledByKey(keyProvider);
       if (provider instanceof OAuth2IdentityProvider) {
         OAuth2IdentityProvider oauthProvider = (OAuth2IdentityProvider) provider;
index 60cb6ff6fa93527c9c9c59fb0d13c825fb2b8a6f..4c52ef80910f951c8aaf019948600bffb690e8ba 100644 (file)
@@ -104,7 +104,7 @@ public class OAuth2ContextFactory {
     @Override
     public void redirectToRequestedPage() {
       try {
-        getResponse().sendRedirect("/");
+        getResponse().sendRedirect(server.getContextPath() + "/");
       } catch (IOException e) {
         throw new IllegalStateException("Fail to redirect to home", e);
       }
index 918e0e9f27bb987b70b070e4b83b049cd5dbb175..5b77cd434e82e71c7e53a5f76a6601838a91f398 100644 (file)
@@ -22,6 +22,7 @@ package org.sonar.server.authentication;
 import javax.servlet.http.Cookie;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
@@ -49,6 +50,11 @@ public class CsrfVerifierTest {
 
   CsrfVerifier underTest = new CsrfVerifier(server);
 
+  @Before
+  public void setUp() throws Exception {
+    when(server.getContextPath()).thenReturn("");
+  }
+
   @Test
   public void generate_state_on_secured_server() throws Exception {
     when(server.isSecured()).thenReturn(true);
@@ -89,6 +95,20 @@ public class CsrfVerifierTest {
     assertThat(updatedCookie.getMaxAge()).isEqualTo(0);
   }
 
+  @Test
+  public void verify_state_when_context() throws Exception {
+    String state = "state";
+    when(request.getCookies()).thenReturn(new Cookie[] {new Cookie("OAUTHSTATE", sha256Hex(state))});
+    when(request.getParameter("state")).thenReturn(state);
+    when(server.getContextPath()).thenReturn("/sonarqube");
+
+    underTest.verifyState(request, response);
+
+    verify(response).addCookie(cookieArgumentCaptor.capture());
+    Cookie updatedCookie = cookieArgumentCaptor.getValue();
+    assertThat(updatedCookie.getPath()).isEqualTo("/sonarqube/");
+  }
+
   @Test
   public void fail_with_unauthorized_when_state_cookie_is_not_the_same_as_state_parameter() throws Exception {
     when(request.getCookies()).thenReturn(new Cookie[] {new Cookie("OAUTHSTATE", sha1Hex("state"))});
index ef82a2def3e7a0eef2499a2e407d16aa0a2d9459..5fb48bb9ec29e12830f55a29bc4ac1ef6365884a 100644 (file)
@@ -26,6 +26,7 @@ import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
+import org.sonar.api.platform.Server;
 import org.sonar.api.server.authentication.BaseIdentityProvider;
 import org.sonar.api.server.authentication.Display;
 import org.sonar.api.server.authentication.IdentityProvider;
@@ -55,6 +56,7 @@ public class InitFilterTest {
 
   BaseContextFactory baseContextFactory = mock(BaseContextFactory.class);
   OAuth2ContextFactory oAuth2ContextFactory = mock(OAuth2ContextFactory.class);
+  Server server = mock(Server.class);
 
   HttpServletRequest request = mock(HttpServletRequest.class);
   HttpServletResponse response = mock(HttpServletResponse.class);
@@ -66,12 +68,13 @@ public class InitFilterTest {
   FakeBasicIdentityProvider baseIdentityProvider = new FakeBasicIdentityProvider(BASIC_PROVIDER_KEY, true);
   BaseIdentityProvider.Context baseContext = mock(BaseIdentityProvider.Context.class);
 
-  InitFilter underTest = new InitFilter(identityProviderRepository, baseContextFactory, oAuth2ContextFactory);
+  InitFilter underTest = new InitFilter(identityProviderRepository, baseContextFactory, oAuth2ContextFactory, server);
 
   @Before
   public void setUp() throws Exception {
     when(oAuth2ContextFactory.newContext(request, response, oAuth2IdentityProvider)).thenReturn(oauth2Context);
     when(baseContextFactory.newContext(request, response, baseIdentityProvider)).thenReturn(baseContext);
+    when(server.getContextPath()).thenReturn("");
   }
 
   @Test
@@ -79,6 +82,17 @@ public class InitFilterTest {
     assertThat(underTest.doGetPattern()).isNotNull();
   }
 
+  @Test
+  public void do_filter_with_context() throws Exception {
+    when(server.getContextPath()).thenReturn("/sonarqube");
+    when(request.getRequestURI()).thenReturn("/sonarqube/sessions/init/" + OAUTH2_PROVIDER_KEY);
+    identityProviderRepository.addIdentityProvider(oAuth2IdentityProvider);
+
+    underTest.doFilter(request, response, chain);
+
+    assertOAuth2InitCalled();
+  }
+
   @Test
   public void do_filter_on_auth2_identity_provider() throws Exception {
     when(request.getRequestURI()).thenReturn("/sessions/init/" + OAUTH2_PROVIDER_KEY);
index ef19c6a298b1b68e122def94fa3c003493bd460c..e07443f4c29e22426a576f01a651f72da9078ce1 100644 (file)
@@ -26,6 +26,7 @@ import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
+import org.sonar.api.platform.Server;
 import org.sonar.api.server.authentication.OAuth2IdentityProvider;
 import org.sonar.api.server.authentication.UnauthorizedException;
 import org.sonar.api.utils.log.LogTester;
@@ -53,16 +54,18 @@ public class OAuth2CallbackFilterTest {
 
   HttpServletRequest request = mock(HttpServletRequest.class);
   HttpServletResponse response = mock(HttpServletResponse.class);
+  Server server = mock(Server.class);
   FilterChain chain = mock(FilterChain.class);
 
   FakeOAuth2IdentityProvider oAuth2IdentityProvider = new FakeOAuth2IdentityProvider(OAUTH2_PROVIDER_KEY, true);
   OAuth2IdentityProvider.InitContext oauth2Context = mock(OAuth2IdentityProvider.InitContext.class);
 
-  OAuth2CallbackFilter underTest = new OAuth2CallbackFilter(identityProviderRepository, oAuth2ContextFactory);
+  OAuth2CallbackFilter underTest = new OAuth2CallbackFilter(identityProviderRepository, oAuth2ContextFactory, server);
 
   @Before
   public void setUp() throws Exception {
     when(oAuth2ContextFactory.newContext(request, response, oAuth2IdentityProvider)).thenReturn(oauth2Context);
+    when(server.getContextPath()).thenReturn("");
   }
 
   @Test
@@ -70,6 +73,17 @@ public class OAuth2CallbackFilterTest {
     assertThat(underTest.doGetPattern()).isNotNull();
   }
 
+  @Test
+  public void do_filter_with_context() throws Exception {
+    when(server.getContextPath()).thenReturn("/sonarqube");
+    when(request.getRequestURI()).thenReturn("/sonarqube/oauth2/callback/" + OAUTH2_PROVIDER_KEY);
+    identityProviderRepository.addIdentityProvider(oAuth2IdentityProvider);
+
+    underTest.doFilter(request, response, chain);
+
+    assertCallbackCalled();
+  }
+
   @Test
   public void do_filter_on_auth2_identity_provider() throws Exception {
     when(request.getRequestURI()).thenReturn("/oauth2/callback/" + OAUTH2_PROVIDER_KEY);
index f44a9e7722f6a1ef3e295fa4cfa96cd21b888ec7..b9344fb2615b8b0fa8e6263e1b1fd7b39df6fd44 100644 (file)
@@ -132,6 +132,7 @@ public class OAuth2ContextFactoryTest {
 
   @Test
   public void redirect_to_requested_page() throws Exception {
+    when(server.getContextPath()).thenReturn("");
     OAuth2IdentityProvider.CallbackContext callback = underTest.newCallback(request, response, identityProvider);
 
     callback.redirectToRequestedPage();
@@ -139,6 +140,16 @@ public class OAuth2ContextFactoryTest {
     verify(response).sendRedirect("/");
   }
 
+  @Test
+  public void redirect_to_requested_page_with_context() throws Exception {
+    when(server.getContextPath()).thenReturn("/sonarqube");
+    OAuth2IdentityProvider.CallbackContext callback = underTest.newCallback(request, response, identityProvider);
+
+    callback.redirectToRequestedPage();
+
+    verify(response).sendRedirect("/sonarqube/");
+  }
+
   @Test
   public void verify_csrf_state() throws Exception {
     OAuth2IdentityProvider.CallbackContext callback = underTest.newCallback(request, response, identityProvider);
index 9711bbadc42a7471695b10ec98c8a31459c03f73..2c5381d9690e52ee7487c758c939f8ce16307cd1 100644 (file)
@@ -10,7 +10,7 @@
              style="background-color: <%= provider.getDisplay().getBackgroundColor().to_s %>"
              title="Log in with <%= provider.getName().to_s -%>">
             <img alt="<%= provider.getName().to_s -%>" width="20" height="20"
-                 src="<%= provider.getDisplay().getIconPath().to_s -%>">
+                 src="<%= ApplicationController.root_context + provider.getDisplay().getIconPath().to_s -%>">
             <span>Log in with <%= provider.getName().to_s -%></span>
           </a>
         </li>