]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-7732 UserSession throws UnauthorizedException if null
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Thu, 23 Jun 2016 07:54:55 +0000 (09:54 +0200)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Wed, 29 Jun 2016 06:41:53 +0000 (08:41 +0200)
If user is not authorized (bad credentials, not authenticated when force authentication is true, etc.) the UserSession will throw an UnauthorizedException in order for rails to be able to deal with this use case (redirect to login page, render 401 in WS,etc.)

12 files changed:
server/sonar-server/src/main/java/org/sonar/server/user/AnonymousUserSession.java [deleted file]
server/sonar-server/src/main/java/org/sonar/server/user/DoPrivileged.java
server/sonar-server/src/main/java/org/sonar/server/user/ThreadLocalUserSession.java
server/sonar-server/src/test/java/org/sonar/server/authentication/ws/LoginActionTest.java
server/sonar-server/src/test/java/org/sonar/server/qualitygate/QualityGatesTest.java
server/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileComparisonMediumTest.java
server/sonar-server/src/test/java/org/sonar/server/user/ThreadLocalUserSessionTest.java
server/sonar-web/src/main/webapp/WEB-INF/app/controllers/api/java_ws_controller.rb
server/sonar-web/src/main/webapp/WEB-INF/app/controllers/application_controller.rb
server/sonar-web/src/main/webapp/WEB-INF/app/views/layouts/_head.html.erb
server/sonar-web/src/main/webapp/WEB-INF/app/views/layouts/_navbar.html.erb
server/sonar-web/src/main/webapp/WEB-INF/lib/authenticated_system.rb

diff --git a/server/sonar-server/src/main/java/org/sonar/server/user/AnonymousUserSession.java b/server/sonar-server/src/main/java/org/sonar/server/user/AnonymousUserSession.java
deleted file mode 100644 (file)
index 985a397..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact 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.user;
-
-import java.util.Collections;
-import java.util.List;
-
-public final class AnonymousUserSession extends AbstractUserSession<AnonymousUserSession> {
-  public static final UserSession INSTANCE = new AnonymousUserSession();
-
-  private AnonymousUserSession() {
-    super(AnonymousUserSession.class);
-  }
-
-  @Override
-  public List<String> globalPermissions() {
-    return Collections.emptyList();
-  }
-
-  @Override
-  public boolean hasComponentPermission(String permission, String componentKey) {
-    return false;
-  }
-
-  @Override
-  public boolean hasComponentUuidPermission(String permission, String componentUuid) {
-    return false;
-  }
-}
index 3742b1977e4ce60bb1fb9ccb7666570b93102e14..846878961783abcdb63dd2a7635b0d2214391a14 100644 (file)
@@ -92,13 +92,15 @@ public final class DoPrivileged {
     }
 
     private void start() {
-      oldUserSession = threadLocalUserSession.get();
+      oldUserSession = threadLocalUserSession.hasSession() ? threadLocalUserSession.get() : null;
       threadLocalUserSession.set(new PrivilegedUserSession().setLocale(Locale.getDefault()));
     }
   
     private void stop() {
       threadLocalUserSession.remove();
-      threadLocalUserSession.set(oldUserSession);
+      if (oldUserSession != null) {
+        threadLocalUserSession.set(oldUserSession);
+      }
     }
   }
 }
index b8f0fc4a8f48e6058693ce0e9a43ec77469fd609..9ec8f3c34ba47cc7bcf3d93873e038a9e573828e 100644 (file)
  */
 package org.sonar.server.user;
 
-import com.google.common.base.MoreObjects;
 import java.util.Collection;
 import java.util.List;
 import java.util.Locale;
 import java.util.Set;
 import javax.annotation.CheckForNull;
+import org.sonar.server.exceptions.UnauthorizedException;
 
 /**
  * Part of the current HTTP session
@@ -34,7 +34,11 @@ public class ThreadLocalUserSession implements UserSession {
   private static final ThreadLocal<UserSession> THREAD_LOCAL = new ThreadLocal<>();
 
   public UserSession get() {
-    return MoreObjects.firstNonNull(THREAD_LOCAL.get(), AnonymousUserSession.INSTANCE);
+    UserSession currentUserSession = THREAD_LOCAL.get();
+    if (currentUserSession != null) {
+      return currentUserSession;
+    }
+    throw new UnauthorizedException();
   }
 
   public void set(UserSession session) {
index 3f6fd7c8fee75f9bbc77e8471ce0d3b0e8dc1e9e..8e5c664f5b8d6a5d01e16beac40cb184f42d477e 100644 (file)
@@ -113,7 +113,7 @@ public class LoginActionTest {
     executeRequest(LOGIN, PASSWORD);
 
     verify(response).setStatus(401);
-    assertThat(threadLocalUserSession.isLoggedIn()).isFalse();
+    assertThat(threadLocalUserSession.hasSession()).isFalse();
   }
 
   @Test
index 6e32b15d4b3fc83b96cb2d052da0480c5423efe0..56f401ab687e6fb5226a090240313ef95ae811ff 100644 (file)
  */
 package org.sonar.server.qualitygate;
 
+import static java.util.Arrays.asList;
+import static java.util.Collections.singletonList;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyLong;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+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 com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 import java.util.Collection;
@@ -57,23 +69,11 @@ import org.sonar.db.qualitygate.QualityGateDto;
 import org.sonar.server.exceptions.BadRequestException;
 import org.sonar.server.exceptions.ForbiddenException;
 import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.tester.AnonymousMockUserSession;
 import org.sonar.server.tester.MockUserSession;
 import org.sonar.server.tester.UserSessionRule;
-import org.sonar.server.user.AnonymousUserSession;
 import org.sonar.server.user.UserSession;
 
-import static java.util.Arrays.asList;
-import static java.util.Collections.singletonList;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyLong;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
 @RunWith(MockitoJUnitRunner.class)
 public class QualityGatesTest {
 
@@ -116,7 +116,7 @@ public class QualityGatesTest {
   UserSession authorizedProfileAdminUserSession = new MockUserSession("gaudol").setName("Olivier").setGlobalPermissions(GlobalPermissions.QUALITY_GATE_ADMIN);
   UserSession authorizedProjectAdminUserSession = new MockUserSession("gaudol").setName("Olivier").addProjectPermissions(UserRole.ADMIN, PROJECT_KEY);
   UserSession unauthorizedUserSession = new MockUserSession("polop").setName("Polop");
-  UserSession unauthenticatedUserSession = AnonymousUserSession.INSTANCE;
+  UserSession unauthenticatedUserSession = new AnonymousMockUserSession();
 
   @Before
   public void initialize() {
index 34fff148da26730a32d3b9829337b95fe0efd827..cb3be9900b8bf7c3acf0d936e7fa5934dc6886ae 100644 (file)
  */
 package org.sonar.server.qualityprofile;
 
+import static org.assertj.core.api.Assertions.assertThat;
+
 import com.google.common.collect.MapDifference.ValueDifference;
 import org.assertj.core.data.MapEntry;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.ClassRule;
+import org.junit.Rule;
 import org.junit.Test;
 import org.sonar.api.rule.Severity;
 import org.sonar.api.server.rule.RuleParamType;
@@ -36,14 +39,16 @@ import org.sonar.db.rule.RuleTesting;
 import org.sonar.server.qualityprofile.QProfileComparison.ActiveRuleDiff;
 import org.sonar.server.qualityprofile.QProfileComparison.QProfileComparisonResult;
 import org.sonar.server.tester.ServerTester;
-
-import static org.assertj.core.api.Assertions.assertThat;
+import org.sonar.server.tester.UserSessionRule;
 
 public class QProfileComparisonMediumTest {
 
   @ClassRule
   public static ServerTester tester = new ServerTester().withEsIndexes().addXoo();
 
+  @Rule
+  public UserSessionRule userSession = UserSessionRule.forServerTester(tester).anonymous();
+
   DbClient db;
   DbSession dbSession;
   RuleActivator ruleActivator;
index 773c2dc23d0f77e41a7c6efe7b00a2dfccee0191..8968b31b604f4bb96f42afaee2ef0cb7a2e53990 100644 (file)
  */
 package org.sonar.server.user;
 
+import static org.assertj.core.api.Assertions.assertThat;
+
 import java.util.Locale;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
+import org.sonar.server.exceptions.UnauthorizedException;
+import org.sonar.server.tester.AnonymousMockUserSession;
 import org.sonar.server.tester.MockUserSession;
 
-import static org.assertj.core.api.Assertions.assertThat;
-
 public class ThreadLocalUserSessionTest {
 
   ThreadLocalUserSession threadLocalUserSession = new ThreadLocalUserSession();
+
   @Rule
   public ExpectedException thrown = ExpectedException.none();
 
@@ -48,9 +51,22 @@ public class ThreadLocalUserSessionTest {
   }
 
   @Test
-  public void getSession_get_anonymous_by_default() {
+  public void get_session_for_user() {
+    threadLocalUserSession.set(new MockUserSession("karadoc").setUserId(123).setLocale(Locale.FRENCH));
+
     UserSession session = threadLocalUserSession.get();
+    assertThat(session).isNotNull();
+    assertThat(session.getUserId()).isEqualTo(123);
+    assertThat(session.getLogin()).isEqualTo("karadoc");
+    assertThat(session.isLoggedIn()).isTrue();
+    assertThat(session.locale()).isEqualTo(Locale.FRENCH);
+  }
+
+  @Test
+  public void get_session_for_anonymous() {
+    threadLocalUserSession.set(new AnonymousMockUserSession());
 
+    UserSession session = threadLocalUserSession.get();
     assertThat(session).isNotNull();
     assertThat(session.getLogin()).isNull();
     assertThat(session.getUserId()).isNull();
@@ -60,15 +76,9 @@ public class ThreadLocalUserSessionTest {
   }
 
   @Test
-  public void get_session() {
-    threadLocalUserSession.set(new MockUserSession("karadoc").setUserId(123).setLocale(Locale.FRENCH));
-
-    UserSession session = threadLocalUserSession.get();
-    assertThat(session).isNotNull();
-    assertThat(session.getUserId()).isEqualTo(123);
-    assertThat(session.getLogin()).isEqualTo("karadoc");
-    assertThat(session.isLoggedIn()).isTrue();
-    assertThat(session.locale()).isEqualTo(Locale.FRENCH);
+  public void throw_UnauthorizedException_when_no_session() {
+    thrown.expect(UnauthorizedException.class);
+    threadLocalUserSession.get();
   }
 
 }
index d9a159be64be11a0f355f8572eab95b5474d0c2c..cb74a0700b32b2d5279de97fd51b942ded074ddd 100644 (file)
@@ -52,6 +52,7 @@ class Api::JavaWsController < Api::ApiController
     (params[:wspath]=='batch' && params[:wsaction]=='index') ||
       (params[:wspath]=='batch' && params[:wsaction]=='file') ||
       (params[:wspath]=='api/system' && params[:wsaction]=='db_migration_status') ||
+      (params[:wspath]=='api/system' && params[:wsaction]=='migrate_db') ||
       (params[:wspath]=='api/system' && params[:wsaction]=='status')
   end
 
index 83b7a6ebff8746051f9a5592c179385d7131b264..b2e07cd7b866bab147a71e73278b005d2d2c837f 100644 (file)
@@ -102,7 +102,11 @@ class ApplicationController < ActionController::Base
   end
 
   def check_authentication
-    access_denied if !current_user && java_facade.getConfigurationValue('sonar.forceAuthentication')=='true'
+    begin
+      current_user
+    rescue Java::OrgSonarServerExceptions::UnauthorizedException => ex
+      access_denied
+    end
   end
 
   # i18n
index 66c5645c688a3a0fd01aed685048bc09264e7de2..30673cc211b95bd20df38892260227ceacba9a55 100644 (file)
@@ -26,9 +26,9 @@
     <%# The two lines below mean that before full removal of Rails, we have to find a way to handle config properties %>
     window.SS = {
       hoursInDay: <%= configuration('sonar.technicalDebt.hoursInDay', 8) %>,
-      user: '<%= escape_javascript current_user.login if current_user -%>',
-      userName: '<%= escape_javascript current_user.name if current_user -%>',
-      userEmail: '<%= escape_javascript current_user.email if current_user -%>',
+      user: '<%= escape_javascript current_user.login if logged_in? -%>',
+      userName: '<%= escape_javascript current_user.name if logged_in? -%>',
+      userEmail: '<%= escape_javascript current_user.email if logged_in? -%>',
       lf: {
         enableGravatar: <%= configuration('sonar.lf.enableGravatar', true) %>,
         gravatarServerUrl: '<%= configuration('sonar.lf.gravatarServerUrl') %>'
index 88a863b31d4dba25a242ff4e71141e210a326620..a99e21339f79e3249c1bd1f2655fe7d2cd1d49fe 100644 (file)
@@ -18,6 +18,6 @@
     window.sonarqube.space = 'settings';
     <% end %>
 
-    window.SS.isUserAdmin = <%= current_user && is_admin? ? 'true' : 'false' -%>;
+    window.SS.isUserAdmin = <%= logged_in? && is_admin? ? 'true' : 'false' -%>;
   })();
 </script>
index c9be7f12c8ad1e0aae0e2b039b3655610ff08e81..83ac2b912c44ff5a6ebe546a2b2cd69060e00e89 100644 (file)
@@ -2,11 +2,19 @@ module AuthenticatedSystem
   # Returns true or false if the user is logged in.
   # Preloads @current_user with the user model if they're logged in.
   def logged_in?
-    !!current_user
+    if Java::OrgSonarServerPlatform::Platform.component(Java::OrgSonarServerUser::ThreadLocalUserSession.java_class).hasSession()
+      !!current_user
+    else
+      false
+    end
   end
 
   # Accesses the current user from the session.
   # Future calls avoid the database because nil is not equal to false.
+  #
+  # This method will generate a Java::OrgSonarServerExceptions::UnauthorizedException if user is unauthorized
+  # (bad credentials, not authenticated by force authentication is set to true, etc...)
+  #
   def current_user
     @current_user ||= login_from_java_user_session unless @current_user == false
   end
@@ -118,6 +126,10 @@ module AuthenticatedSystem
   #
 
   # Called from #current_user.  First attempt to login by the user id stored in the session.
+  #
+  # This method will generate a Java::OrgSonarServerExceptions::UnauthorizedException if user is unauthorized
+  # (bad credentials, not authenticated by force authentication is set to true, etc...)
+  #
   def login_from_java_user_session
     userSession = Java::OrgSonarServerPlatform::Platform.component(Java::OrgSonarServerUser::UserSession.java_class)
     user_id = userSession.getUserId() if userSession && userSession.isLoggedIn()