From 377b2087815b3b6da05d9f4a7718b9fae7abd71e Mon Sep 17 00:00:00 2001
From: Sébastien Lesaint <sebastien.lesaint@sonarsource.com>
Date: Tue, 4 Oct 2016 11:10:31 +0200
Subject: SONAR-8155 add UserSession#isRoot and checkIsRoot

---
 .../main/java/org/sonar/ce/user/CeUserSession.java | 10 ++++
 .../org/sonar/server/user/AbstractUserSession.java | 12 ++++-
 .../java/org/sonar/server/user/DoPrivileged.java   |  5 ++
 .../org/sonar/server/user/ServerUserSession.java   |  5 ++
 .../sonar/server/user/ThreadLocalUserSession.java  | 11 +++++
 .../java/org/sonar/server/user/UserSession.java    |  7 +++
 .../server/tester/AnonymousMockUserSession.java    |  5 ++
 .../org/sonar/server/tester/MockUserSession.java   | 10 ++++
 .../org/sonar/server/tester/UserSessionRule.java   | 23 ++++++++-
 .../sonar/server/user/ServerUserSessionTest.java   | 54 ++++++++++++++++------
 .../src/main/java/org/sonar/db/user/UserDto.java   |  5 +-
 11 files changed, 128 insertions(+), 19 deletions(-)

diff --git a/server/sonar-ce/src/main/java/org/sonar/ce/user/CeUserSession.java b/server/sonar-ce/src/main/java/org/sonar/ce/user/CeUserSession.java
index f1bb8212bb1..b805e1749be 100644
--- a/server/sonar-ce/src/main/java/org/sonar/ce/user/CeUserSession.java
+++ b/server/sonar-ce/src/main/java/org/sonar/ce/user/CeUserSession.java
@@ -65,6 +65,16 @@ public class CeUserSession implements UserSession {
     return notImplemented();
   }
 
+  @Override
+  public boolean isRoot() {
+    return notImplementedBooleanMethod();
+  }
+
+  @Override
+  public UserSession checkIsRoot() {
+    return notImplemented();
+  }
+
   @Override
   public UserSession checkLoggedIn() {
     return notImplemented();
diff --git a/server/sonar-server/src/main/java/org/sonar/server/user/AbstractUserSession.java b/server/sonar-server/src/main/java/org/sonar/server/user/AbstractUserSession.java
index e5441c5faf8..d43a1db5350 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/user/AbstractUserSession.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/user/AbstractUserSession.java
@@ -27,11 +27,21 @@ import org.sonar.server.exceptions.UnauthorizedException;
 public abstract class AbstractUserSession implements UserSession {
   private static final String INSUFFICIENT_PRIVILEGES_MESSAGE = "Insufficient privileges";
   private static final ForbiddenException INSUFFICIENT_PRIVILEGES_EXCEPTION = new ForbiddenException(INSUFFICIENT_PRIVILEGES_MESSAGE);
+  private static final String AUTHENTICATION_IS_REQUIRED_MESSAGE = "Authentication is required";
 
   @Override
   public UserSession checkLoggedIn() {
     if (!isLoggedIn()) {
-      throw new UnauthorizedException("Authentication is required");
+      throw new UnauthorizedException(AUTHENTICATION_IS_REQUIRED_MESSAGE);
+    }
+    return this;
+  }
+
+
+  @Override
+  public UserSession checkIsRoot() {
+    if (!isRoot()) {
+      throw new UnauthorizedException(AUTHENTICATION_IS_REQUIRED_MESSAGE);
     }
     return this;
   }
diff --git a/server/sonar-server/src/main/java/org/sonar/server/user/DoPrivileged.java b/server/sonar-server/src/main/java/org/sonar/server/user/DoPrivileged.java
index 120911e9f61..cd00aaddf34 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/user/DoPrivileged.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/user/DoPrivileged.java
@@ -92,6 +92,11 @@ public final class DoPrivileged {
         return false;
       }
 
+      @Override
+      public boolean isRoot() {
+        return false;
+      }
+
       @Override
       public Locale locale() {
         return Locale.getDefault();
diff --git a/server/sonar-server/src/main/java/org/sonar/server/user/ServerUserSession.java b/server/sonar-server/src/main/java/org/sonar/server/user/ServerUserSession.java
index 9aafd5f4e1d..3ccf912885e 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/user/ServerUserSession.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/user/ServerUserSession.java
@@ -125,6 +125,11 @@ public class ServerUserSession extends AbstractUserSession {
     return Locale.ENGLISH;
   }
 
+  @Override
+  public boolean isRoot() {
+    return userDto != null && userDto.isRoot();
+  }
+
   @Override
   public List<String> globalPermissions() {
     if (globalPermissions == null) {
diff --git a/server/sonar-server/src/main/java/org/sonar/server/user/ThreadLocalUserSession.java b/server/sonar-server/src/main/java/org/sonar/server/user/ThreadLocalUserSession.java
index 7e46f810a98..12594ace597 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/user/ThreadLocalUserSession.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/user/ThreadLocalUserSession.java
@@ -86,6 +86,17 @@ public class ThreadLocalUserSession implements UserSession {
     return get().locale();
   }
 
+  @Override
+  public boolean isRoot() {
+    return get().isRoot();
+  }
+
+  @Override
+  public UserSession checkIsRoot() {
+    get().checkIsRoot();
+    return this;
+  }
+
   @Override
   public UserSession checkLoggedIn() {
     get().checkLoggedIn();
diff --git a/server/sonar-server/src/main/java/org/sonar/server/user/UserSession.java b/server/sonar-server/src/main/java/org/sonar/server/user/UserSession.java
index 1e5e1c57557..2373cf0c9fe 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/user/UserSession.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/user/UserSession.java
@@ -45,6 +45,13 @@ public interface UserSession {
   @Deprecated
   Locale locale();
 
+  boolean isRoot();
+
+  /**
+   * Ensures that user is root in otherwise throws {@link org.sonar.server.exceptions.UnauthorizedException}.
+   */
+  UserSession checkIsRoot();
+
   /**
    * Ensures that user is logged in otherwise throws {@link org.sonar.server.exceptions.UnauthorizedException}.
    */
diff --git a/server/sonar-server/src/test/java/org/sonar/server/tester/AnonymousMockUserSession.java b/server/sonar-server/src/test/java/org/sonar/server/tester/AnonymousMockUserSession.java
index f1502fd75dd..6da49c33c0e 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/tester/AnonymousMockUserSession.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/tester/AnonymousMockUserSession.java
@@ -25,6 +25,11 @@ public class AnonymousMockUserSession extends AbstractMockUserSession<AnonymousM
     super(AnonymousMockUserSession.class);
   }
 
+  @Override
+  public boolean isRoot() {
+    return false;
+  }
+
   @Override
   public String getLogin() {
     return null;
diff --git a/server/sonar-server/src/test/java/org/sonar/server/tester/MockUserSession.java b/server/sonar-server/src/test/java/org/sonar/server/tester/MockUserSession.java
index 8df3ab8fa78..df409a00c58 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/tester/MockUserSession.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/tester/MockUserSession.java
@@ -25,6 +25,7 @@ import static com.google.common.base.Preconditions.checkArgument;
 
 public class MockUserSession extends AbstractMockUserSession<MockUserSession> {
   private final String login;
+  private boolean root = false;
   private Integer userId;
   private String name;
 
@@ -41,6 +42,15 @@ public class MockUserSession extends AbstractMockUserSession<MockUserSession> {
     return true;
   }
 
+  @Override
+  public boolean isRoot() {
+    return root;
+  }
+
+  public void setRoot(boolean root) {
+    this.root = root;
+  }
+
   @Override
   public String getLogin() {
     return this.login;
diff --git a/server/sonar-server/src/test/java/org/sonar/server/tester/UserSessionRule.java b/server/sonar-server/src/test/java/org/sonar/server/tester/UserSessionRule.java
index 243ba5daa8b..2da0a3a0794 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/tester/UserSessionRule.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/tester/UserSessionRule.java
@@ -78,7 +78,7 @@ import static com.google.common.base.Preconditions.checkState;
  * </p>
  */
 public class UserSessionRule implements TestRule, UserSession {
-  public static final String DEFAULT_LOGIN = "default_login";
+  private static final String DEFAULT_LOGIN = "default_login";
 
   @CheckForNull
   private final ServerTester serverTester;
@@ -120,6 +120,16 @@ public class UserSessionRule implements TestRule, UserSession {
     return this;
   }
 
+  public UserSessionRule setRoot() {
+    ensureMockUserSession().setRoot(true);
+    return this;
+  }
+
+  public UserSessionRule setNonRoot() {
+    ensureMockUserSession().setRoot(false);
+    return this;
+  }
+
   @Override
   public Statement apply(Statement statement, Description description) {
     return this.statement(statement);
@@ -269,6 +279,17 @@ public class UserSessionRule implements TestRule, UserSession {
     return currentUserSession.locale();
   }
 
+  @Override
+  public boolean isRoot() {
+    return currentUserSession.isRoot();
+  }
+
+  @Override
+  public UserSession checkIsRoot() {
+    currentUserSession.checkIsRoot();
+    return this;
+  }
+
   @Override
   public UserSession checkLoggedIn() {
     currentUserSession.checkLoggedIn();
diff --git a/server/sonar-server/src/test/java/org/sonar/server/user/ServerUserSessionTest.java b/server/sonar-server/src/test/java/org/sonar/server/user/ServerUserSessionTest.java
index 3c43cb13048..07d3956ab6c 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/user/ServerUserSessionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/user/ServerUserSessionTest.java
@@ -37,6 +37,7 @@ import org.sonar.db.permission.GroupPermissionDto;
 import org.sonar.db.permission.UserPermissionDto;
 import org.sonar.db.user.UserDto;
 import org.sonar.server.exceptions.ForbiddenException;
+import org.sonar.server.exceptions.UnauthorizedException;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.sonar.core.permission.GlobalPermissions.QUALITY_GATE_ADMIN;
@@ -47,35 +48,58 @@ import static org.sonar.server.user.ServerUserSession.createForAnonymous;
 import static org.sonar.server.user.ServerUserSession.createForUser;
 
 public class ServerUserSessionTest {
-  static final String LOGIN = "marius";
-
-  static final String PROJECT_UUID = "ABCD";
-  static final String FILE_KEY = "com.foo:Bar:BarFile.xoo";
-  static final String FILE_UUID = "BCDE";
+  private static final String LOGIN = "marius";
+
+  private static final String PROJECT_UUID = "ABCD";
+  private static final String FILE_KEY = "com.foo:Bar:BarFile.xoo";
+  private static final String FILE_UUID = "BCDE";
+  private static final UserDto ROOT_USER_DTO = new UserDto() {{
+    setRoot(true);
+  }}.setLogin("root_user");
+  private static final UserDto NON_ROOT_USER_DTO = new UserDto() {{
+    setRoot(false);
+  }}.setLogin("regular_user");
 
   @Rule
   public DbTester dbTester = DbTester.create(System2.INSTANCE);
-
   @Rule
   public ExpectedException expectedException = ExpectedException.none();
 
-  ComponentDbTester componentDbTester = new ComponentDbTester(dbTester);
-
-  DbClient dbClient = dbTester.getDbClient();
-
-  DbSession dbSession = dbTester.getSession();
-
-  UserDto userDto = newUserDto().setLogin(LOGIN);
-  ComponentDto project, file;
+  private ComponentDbTester componentDbTester = new ComponentDbTester(dbTester);
+  private DbClient dbClient = dbTester.getDbClient();
+  private DbSession dbSession = dbTester.getSession();
+  private UserDto userDto = newUserDto().setLogin(LOGIN);
+  private ComponentDto project;
 
   @Before
   public void setUp() throws Exception {
     project = componentDbTester.insertComponent(ComponentTesting.newProjectDto(PROJECT_UUID));
-    file = componentDbTester.insertComponent(ComponentTesting.newFileDto(project, null, FILE_UUID).setKey(FILE_KEY));
+    componentDbTester.insertComponent(ComponentTesting.newFileDto(project, null, FILE_UUID).setKey(FILE_KEY));
     dbClient.userDao().insert(dbSession, userDto);
     dbSession.commit();
   }
 
+  @Test
+  public void isRoot_is_false_is_flag_root_is_false_on_UserDto() {
+    assertThat(newUserSession(ROOT_USER_DTO).isRoot()).isTrue();
+    assertThat(newUserSession(NON_ROOT_USER_DTO).isRoot()).isFalse();
+  }
+
+  @Test
+  public void checkIsRoot_fails_with_UnauthorizedException_when_flag_is_false_on_UserDto() {
+    expectedException.expect(UnauthorizedException.class);
+    expectedException.expectMessage("Authentication is required");
+
+    newUserSession(NON_ROOT_USER_DTO).checkIsRoot();
+  }
+
+  @Test
+  public void checkIsRoot_does_not_fails_when_flag_is_true_on_UserDto() {
+    ServerUserSession underTest = newUserSession(ROOT_USER_DTO);
+
+    assertThat(underTest.checkIsRoot()).isSameAs(underTest);
+  }
+
   @Test
   public void has_global_permission() {
     addGlobalPermissions("admin", "profileadmin");
diff --git a/sonar-db/src/main/java/org/sonar/db/user/UserDto.java b/sonar-db/src/main/java/org/sonar/db/user/UserDto.java
index df3d9e02726..385968943fd 100644
--- a/sonar-db/src/main/java/org/sonar/db/user/UserDto.java
+++ b/sonar-db/src/main/java/org/sonar/db/user/UserDto.java
@@ -207,13 +207,14 @@ public class UserDto {
   }
 
   /**
-   * Setters is not accessible as dedicated requests must be used to update the root flag of a user:
+   * Setters is not accessible as MyBatis doesn't need setter to set the field and dedicated SQL requests must be used
+   * to update the root flag of a user:
    * <ul>
    *   <li>a user can not be created root</li>
    *   <li>the generic update method of a user can not change its root flag</li>
    * </ul>
    */
-  private void setRoot(boolean root) {
+  protected void setRoot(boolean root) {
     this.root = root;
   }
 
-- 
cgit v1.2.3