/*
* SonarQube
* Copyright (C) 2009-2017 SonarSource SA
* mailto:info 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.Arrays;
import java.util.Random;
import javax.annotation.Nullable;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.sonar.api.utils.System2;
import org.sonar.api.web.UserRole;
import org.sonar.db.DbClient;
import org.sonar.db.DbTester;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.ComponentTesting;
import org.sonar.db.organization.OrganizationDto;
import org.sonar.db.permission.OrganizationPermission;
import org.sonar.db.user.GroupDto;
import org.sonar.db.user.UserDto;
import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.organization.TestDefaultOrganizationProvider;
import org.sonar.server.organization.TestOrganizationFlags;
import static org.assertj.core.api.Assertions.assertThat;
import static org.sonar.core.permission.GlobalPermissions.PROVISIONING;
import static org.sonar.core.permission.GlobalPermissions.SYSTEM_ADMIN;
import static org.sonar.db.permission.OrganizationPermission.ADMINISTER;
import static org.sonar.db.permission.OrganizationPermission.PROVISION_PROJECTS;
import static org.sonar.db.permission.OrganizationPermission.SCAN;
public class ServerUserSessionTest {
private static final String LOGIN = "marius";
private static final String PUBLIC_PROJECT_UUID = "public project";
private static final String PRIVATE_PROJECT_UUID = "private project";
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 db = DbTester.create(System2.INSTANCE);
@Rule
public ExpectedException expectedException = ExpectedException.none();
private DbClient dbClient = db.getDbClient();
private UserDto user;
private GroupDto groupOfUser;
private TestOrganizationFlags organizationFlags = TestOrganizationFlags.standalone();
private TestDefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(db);
private OrganizationDto organization;
private ComponentDto publicProject;
private ComponentDto privateProject;
@Before
public void setUp() throws Exception {
organization = db.organizations().insert();
publicProject = db.components().insertPublicProject(organization, PUBLIC_PROJECT_UUID);
privateProject = db.components().insertPrivateProject(organization, dto -> dto.setUuid(PRIVATE_PROJECT_UUID).setProjectUuid(PRIVATE_PROJECT_UUID).setPrivate(true));
db.components().insertComponent(ComponentTesting.newFileDto(publicProject, null, FILE_UUID).setKey(FILE_KEY));
user = db.users().insertUser(LOGIN);
groupOfUser = db.users().insertGroup(organization);
}
@Test
public void anonymous_is_not_logged_in_and_does_not_have_login() throws Exception {
UserSession session = newAnonymousSession();
assertThat(session.getLogin()).isNull();
assertThat(session.isLoggedIn()).isFalse();
}
@Test
public void getGroups_is_empty_on_anonymous() {
assertThat(newAnonymousSession().getGroups()).isEmpty();
}
@Test
public void getGroups_is_empty_if_user_is_not_member_of_any_group() {
assertThat(newUserSession(user).getGroups()).isEmpty();
}
@Test
public void getGroups_returns_the_groups_of_logged_in_user() {
GroupDto group1 = db.users().insertGroup();
GroupDto group2 = db.users().insertGroup();
db.users().insertMember(group1, user);
db.users().insertMember(group2, user);
assertThat(newUserSession(user).getGroups()).extracting(GroupDto::getId).containsOnly(group1.getId(), group2.getId());
}
@Test
public void getGroups_keeps_groups_in_cache() {
GroupDto group1 = db.users().insertGroup();
GroupDto group2 = db.users().insertGroup();
db.users().insertMember(group1, user);
ServerUserSession session = newUserSession(user);
assertThat(session.getGroups()).extracting(GroupDto::getId).containsOnly(group1.getId());
// membership updated but not cache
db.users().insertMember(group2, user);
assertThat(session.getGroups()).extracting(GroupDto::getId).containsOnly(group1.getId());
}
@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 hasComponentUuidPermission_returns_true_when_flag_root_is_true_on_UserDto_no_matter_if_user_has_project_permission_for_given_uuid() {
UserSession underTest = newUserSession(ROOT_USER_DTO);
assertThat(underTest.hasComponentUuidPermission(UserRole.USER, FILE_UUID)).isTrue();
assertThat(underTest.hasComponentUuidPermission(UserRole.CODEVIEWER, FILE_UUID)).isTrue();
assertThat(underTest.hasComponentUuidPermission(UserRole.ADMIN, FILE_UUID)).isTrue();
assertThat(underTest.hasComponentUuidPermission("whatever", "who cares?")).isTrue();
}
@Test
public void checkComponentUuidPermission_succeeds_if_user_has_permission_for_specified_uuid_in_db() {
UserSession underTest = newUserSession(ROOT_USER_DTO);
assertThat(underTest.checkComponentUuidPermission(UserRole.USER, FILE_UUID)).isSameAs(underTest);
assertThat(underTest.checkComponentUuidPermission("whatever", "who cares?")).isSameAs(underTest);
}
@Test
public void checkComponentUuidPermission_fails_with_FE_when_user_has_not_permission_for_specified_uuid_in_db() {
addProjectPermissions(privateProject, UserRole.USER);
UserSession session = newUserSession(user);
expectInsufficientPrivilegesForbiddenException();
session.checkComponentUuidPermission(UserRole.USER, "another-uuid");
}
@Test
public void checkPermission_throws_ForbiddenException_when_user_doesnt_have_the_specified_permission_on_organization() {
OrganizationDto org = db.organizations().insert();
db.users().insertUser(NON_ROOT_USER_DTO);
expectInsufficientPrivilegesForbiddenException();
newUserSession(NON_ROOT_USER_DTO).checkPermission(PROVISION_PROJECTS, org);
}
@Test
public void checkPermission_succeeds_when_user_has_the_specified_permission_on_organization() {
OrganizationDto org = db.organizations().insert();
db.users().insertUser(NON_ROOT_USER_DTO);
db.users().insertPermissionOnUser(org, NON_ROOT_USER_DTO, PROVISIONING);
newUserSession(NON_ROOT_USER_DTO).checkPermission(PROVISION_PROJECTS, org);
}
@Test
public void checkPermission_succeeds_when_user_is_root() {
OrganizationDto org = db.organizations().insert();
newUserSession(ROOT_USER_DTO).checkPermission(PROVISION_PROJECTS, org);
}
@Test
public void test_hasPermission_on_organization_for_logged_in_user() {
OrganizationDto org = db.organizations().insert();
ComponentDto project = db.components().insertPrivateProject(org);
db.users().insertPermissionOnUser(org, user, PROVISION_PROJECTS);
db.users().insertProjectPermissionOnUser(user, UserRole.ADMIN, project);
UserSession session = newUserSession(user);
assertThat(session.hasPermission(PROVISION_PROJECTS, org.getUuid())).isTrue();
assertThat(session.hasPermission(ADMINISTER, org.getUuid())).isFalse();
assertThat(session.hasPermission(PROVISION_PROJECTS, "another-org")).isFalse();
}
@Test
public void test_hasPermission_on_organization_for_anonymous_user() {
OrganizationDto org = db.organizations().insert();
db.users().insertPermissionOnAnyone(org, PROVISION_PROJECTS);
UserSession session = newAnonymousSession();
assertThat(session.hasPermission(PROVISION_PROJECTS, org.getUuid())).isTrue();
assertThat(session.hasPermission(ADMINISTER, org.getUuid())).isFalse();
assertThat(session.hasPermission(PROVISION_PROJECTS, "another-org")).isFalse();
}
@Test
public void hasPermission_on_organization_keeps_cache_of_permissions_of_logged_in_user() {
OrganizationDto org = db.organizations().insert();
db.users().insertPermissionOnUser(org, user, PROVISIONING);
UserSession session = newUserSession(user);
// feed the cache
assertThat(session.hasPermission(PROVISION_PROJECTS, org.getUuid())).isTrue();
// change permissions without updating the cache
db.users().deletePermissionFromUser(org, user, PROVISION_PROJECTS);
db.users().insertPermissionOnUser(org, user, SCAN);
assertThat(session.hasPermission(PROVISION_PROJECTS, org.getUuid())).isTrue();
assertThat(session.hasPermission(ADMINISTER, org.getUuid())).isFalse();
assertThat(session.hasPermission(SCAN, org.getUuid())).isFalse();
}
@Test
public void hasPermission_on_organization_keeps_cache_of_permissions_of_anonymous_user() {
OrganizationDto org = db.organizations().insert();
db.users().insertPermissionOnAnyone(org, PROVISION_PROJECTS);
UserSession session = newAnonymousSession();
// feed the cache
assertThat(session.hasPermission(PROVISION_PROJECTS, org.getUuid())).isTrue();
// change permissions without updating the cache
db.users().insertPermissionOnAnyone(org, SCAN);
assertThat(session.hasPermission(PROVISION_PROJECTS, org.getUuid())).isTrue();
assertThat(session.hasPermission(SCAN, org.getUuid())).isFalse();
}
@Test
public void hasComponentPermissionByDtoOrUuid_returns_true_for_anonymous_user_for_permissions_USER_and_CODEVIEWER_on_public_projects_without_permissions() {
ServerUserSession underTest = newAnonymousSession();
assertThat(hasComponentPermissionByDtoOrUuid(underTest, UserRole.USER, publicProject)).isTrue();
assertThat(hasComponentPermissionByDtoOrUuid(underTest, UserRole.CODEVIEWER, publicProject)).isTrue();
}
@Test
public void hasComponentPermissionByDtoOrUuid_returns_true_for_anonymous_user_for_permissions_USER_and_CODEVIEWER_on_public_projects_with_global_permissions() {
ServerUserSession underTest = newAnonymousSession();
db.users().insertProjectPermissionOnAnyone("p1", publicProject);
assertThat(hasComponentPermissionByDtoOrUuid(underTest, UserRole.USER, publicProject)).isTrue();
assertThat(hasComponentPermissionByDtoOrUuid(underTest, UserRole.CODEVIEWER, publicProject)).isTrue();
}
@Test
public void hasComponentPermissionByDtoOrUuid_returns_true_for_anonymous_user_for_permissions_USER_and_CODEVIEWER_on_public_projects_with_group_permissions() {
ServerUserSession underTest = newAnonymousSession();
db.users().insertProjectPermissionOnGroup(db.users().insertGroup(organization), "p1", publicProject);
assertThat(hasComponentPermissionByDtoOrUuid(underTest, UserRole.USER, publicProject)).isTrue();
assertThat(hasComponentPermissionByDtoOrUuid(underTest, UserRole.CODEVIEWER, publicProject)).isTrue();
}
@Test
public void hasComponentPermissionByDtoOrUuid_returns_true_for_anonymous_user_for_permissions_USER_and_CODEVIEWER_on_public_projects_with_user_permissions() {
ServerUserSession underTest = newAnonymousSession();
db.users().insertProjectPermissionOnUser(db.users().insertUser(), "p1", publicProject);
assertThat(hasComponentPermissionByDtoOrUuid(underTest, UserRole.USER, publicProject)).isTrue();
assertThat(hasComponentPermissionByDtoOrUuid(underTest, UserRole.CODEVIEWER, publicProject)).isTrue();
}
@Test
public void hasComponentPermissionByDtoOrUuid_returns_false_for_authenticated_user_for_permissions_USER_and_CODEVIEWER_on_private_projects_without_permissions() {
ServerUserSession underTest = newUserSession(user);
assertThat(hasComponentPermissionByDtoOrUuid(underTest, UserRole.USER, privateProject)).isFalse();
assertThat(hasComponentPermissionByDtoOrUuid(underTest, UserRole.CODEVIEWER, privateProject)).isFalse();
}
@Test
public void hasComponentPermissionByDtoOrUuid_returns_false_for_authenticated_user_for_permissions_USER_and_CODEVIEWER_on_private_projects_with_group_permissions() {
ServerUserSession underTest = newUserSession(user);
db.users().insertProjectPermissionOnGroup(db.users().insertGroup(organization), "p1", privateProject);
assertThat(hasComponentPermissionByDtoOrUuid(underTest, UserRole.USER, privateProject)).isFalse();
assertThat(hasComponentPermissionByDtoOrUuid(underTest, UserRole.CODEVIEWER, privateProject)).isFalse();
}
@Test
public void hasComponentPermissionByDtoOrUuid_returns_false_for_authenticated_user_for_permissions_USER_and_CODEVIEWER_on_private_projects_with_user_permissions() {
ServerUserSession underTest = newUserSession(user);
db.users().insertProjectPermissionOnUser(db.users().insertUser(), "p1", privateProject);
assertThat(hasComponentPermissionByDtoOrUuid(underTest, UserRole.USER, privateProject)).isFalse();
assertThat(hasComponentPermissionByDtoOrUuid(underTest, UserRole.CODEVIEWER, privateProject)).isFalse();
}
@Test
public void hasComponentPermissionByDtoOrUuid_returns_true_for_anonymous_user_for_inserted_permissions_on_group_AnyOne_on_public_projects() {
ServerUserSession underTest = newAnonymousSession();
db.users().insertProjectPermissionOnAnyone("p1", publicProject);
assertThat(hasComponentPermissionByDtoOrUuid(underTest, "p1", publicProject)).isTrue();
}
@Test
public void hasComponentPermissionByDtoOrUuid_returns_false_for_anonymous_user_for_inserted_permissions_on_group_on_public_projects() {
ServerUserSession underTest = newAnonymousSession();
db.users().insertProjectPermissionOnGroup(groupOfUser, "p1", publicProject);
assertThat(hasComponentPermissionByDtoOrUuid(underTest, "p1", publicProject)).isFalse();
}
@Test
public void hasComponentPermissionByDtoOrUuid_returns_false_for_anonymous_user_for_inserted_permissions_on_group_on_private_projects() {
ServerUserSession underTest = newAnonymousSession();
db.users().insertProjectPermissionOnGroup(groupOfUser, "p1", privateProject);
assertThat(hasComponentPermissionByDtoOrUuid(underTest, "p1", privateProject)).isFalse();
}
@Test
public void hasComponentPermissionByDtoOrUuid_returns_false_for_anonymous_user_for_inserted_permissions_on_user_on_public_projects() {
ServerUserSession underTest = newAnonymousSession();
db.users().insertProjectPermissionOnUser(user, "p1", publicProject);
assertThat(hasComponentPermissionByDtoOrUuid(underTest, "p1", publicProject)).isFalse();
}
@Test
public void hasComponentPermissionByDtoOrUuid_returns_false_for_anonymous_user_for_inserted_permissions_on_user_on_private_projects() {
ServerUserSession underTest = newAnonymousSession();
db.users().insertProjectPermissionOnUser(user, "p1", privateProject);
assertThat(hasComponentPermissionByDtoOrUuid(underTest, "p1", privateProject)).isFalse();
}
@Test
public void hasComponentPermissionByDtoOrUuid_returns_true_for_any_project_or_permission_for_root_user() {
ServerUserSession underTest = newUserSession(ROOT_USER_DTO);
assertThat(hasComponentPermissionByDtoOrUuid(underTest, "does not matter", publicProject)).isTrue();
}
@Test
public void hasComponentPermissionByDtoOrUuid_keeps_cache_of_permissions_of_logged_in_user() {
db.users().insertProjectPermissionOnUser(user, UserRole.ADMIN, publicProject);
UserSession underTest = newUserSession(user);
// feed the cache
assertThat(hasComponentPermissionByDtoOrUuid(underTest, UserRole.ADMIN, publicProject)).isTrue();
// change permissions without updating the cache
db.users().deletePermissionFromUser(publicProject, user, UserRole.ADMIN);
db.users().insertProjectPermissionOnUser(user, UserRole.ISSUE_ADMIN, publicProject);
assertThat(hasComponentPermissionByDtoOrUuid(underTest, UserRole.ADMIN, publicProject)).isTrue();
assertThat(hasComponentPermissionByDtoOrUuid(underTest, UserRole.ISSUE_ADMIN, publicProject)).isFalse();
}
@Test
public void hasComponentPermissionByDtoOrUuid_keeps_cache_of_permissions_of_anonymous_user() {
db.users().insertProjectPermissionOnAnyone(UserRole.ADMIN, publicProject);
UserSession underTest = newAnonymousSession();
// feed the cache
assertThat(hasComponentPermissionByDtoOrUuid(underTest, UserRole.ADMIN, publicProject)).isTrue();
// change permissions without updating the cache
db.users().deleteProjectPermissionFromAnyone(publicProject, UserRole.ADMIN);
db.users().insertProjectPermissionOnAnyone(UserRole.ISSUE_ADMIN, publicProject);
assertThat(hasComponentPermissionByDtoOrUuid(underTest, UserRole.ADMIN, publicProject)).isTrue();
assertThat(hasComponentPermissionByDtoOrUuid(underTest, UserRole.ISSUE_ADMIN, publicProject)).isFalse();
}
private boolean hasComponentPermissionByDtoOrUuid(UserSession underTest, String permission, ComponentDto component) {
return new Random().nextBoolean() ? underTest.hasComponentPermission(permission, component) : underTest.hasComponentUuidPermission(permission, component.uuid());
}
@Test
public void keepAuthorizedComponents_returns_empty_list_if_no_permissions_are_granted() {
UserSession underTest = newAnonymousSession();
assertThat(underTest.keepAuthorizedComponents(UserRole.ADMIN, Arrays.asList(privateProject, publicProject))).isEmpty();
}
@Test
public void keepAuthorizedComponents_filters_components_with_granted_permissions_for_logged_in_user() {
UserSession underTest = newUserSession(user);
db.users().insertProjectPermissionOnUser(user, UserRole.ADMIN, privateProject);
assertThat(underTest.keepAuthorizedComponents(UserRole.ISSUE_ADMIN, Arrays.asList(privateProject, publicProject))).isEmpty();
assertThat(underTest.keepAuthorizedComponents(UserRole.ADMIN, Arrays.asList(privateProject, publicProject))).containsExactly(privateProject);
}
@Test
public void keepAuthorizedComponents_filters_components_with_granted_permissions_for_anonymous() {
UserSession underTest = newAnonymousSession();
db.users().insertProjectPermissionOnAnyone(UserRole.ISSUE_ADMIN, publicProject);
assertThat(underTest.keepAuthorizedComponents(UserRole.ADMIN, Arrays.asList(privateProject, publicProject))).isEmpty();
assertThat(underTest.keepAuthorizedComponents(UserRole.ISSUE_ADMIN, Arrays.asList(privateProject, publicProject))).containsExactly(publicProject);
}
@Test
public void keepAuthorizedComponents_returns_all_specified_components_if_root() {
user = db.users().makeRoot(user);
UserSession underTest = newUserSession(user);
assertThat(underTest.keepAuthorizedComponents(UserRole.ADMIN, Arrays.asList(privateProject, publicProject)))
.containsExactly(privateProject, publicProject);
}
@Test
public void isSystemAdministrator_returns_true_if_org_feature_is_enabled_and_user_is_root() {
organizationFlags.setEnabled(true);
user = db.users().makeRoot(user);
UserSession session = newUserSession(user);
assertThat(session.isSystemAdministrator()).isTrue();
}
@Test
public void isSystemAdministrator_returns_false_if_org_feature_is_enabled_and_user_is_not_root() {
organizationFlags.setEnabled(true);
user = db.users().makeNotRoot(user);
UserSession session = newUserSession(user);
assertThat(session.isSystemAdministrator()).isFalse();
}
@Test
public void isSystemAdministrator_returns_false_if_org_feature_is_enabled_and_user_is_administrator_of_default_organization() {
organizationFlags.setEnabled(true);
user = db.users().makeNotRoot(user);
db.users().insertPermissionOnUser(db.getDefaultOrganization(), user, SYSTEM_ADMIN);
UserSession session = newUserSession(user);
assertThat(session.isSystemAdministrator()).isFalse();
}
@Test
public void isSystemAdministrator_returns_true_if_org_feature_is_disabled_and_user_is_administrator_of_default_organization() {
organizationFlags.setEnabled(false);
user = db.users().makeNotRoot(user);
db.users().insertPermissionOnUser(db.getDefaultOrganization(), user, SYSTEM_ADMIN);
UserSession session = newUserSession(user);
assertThat(session.isSystemAdministrator()).isTrue();
}
@Test
public void isSystemAdministrator_returns_false_if_org_feature_is_disabled_and_user_is_not_administrator_of_default_organization() {
organizationFlags.setEnabled(true);
user = db.users().makeNotRoot(user);
db.users().insertPermissionOnUser(db.getDefaultOrganization(), user, PROVISIONING);
UserSession session = newUserSession(user);
assertThat(session.isSystemAdministrator()).isFalse();
}
@Test
public void keep_isSystemAdministrator_flag_in_cache() {
organizationFlags.setEnabled(false);
user = db.users().makeNotRoot(user);
db.users().insertPermissionOnUser(db.getDefaultOrganization(), user, SYSTEM_ADMIN);
UserSession session = newUserSession(user);
session.checkIsSystemAdministrator();
db.getDbClient().userDao().deactivateUserById(db.getSession(), user.getId());
db.commit();
// should fail but succeeds because flag is kept in cache
session.checkIsSystemAdministrator();
}
@Test
public void checkIsSystemAdministrator_succeeds_if_system_administrator() {
organizationFlags.setEnabled(true);
user = db.users().makeRoot(user);
UserSession session = newUserSession(user);
session.checkIsSystemAdministrator();
}
@Test
public void checkIsSystemAdministrator_throws_ForbiddenException_if_not_system_administrator() {
organizationFlags.setEnabled(true);
user = db.users().makeNotRoot(user);
UserSession session = newUserSession(user);
expectedException.expect(ForbiddenException.class);
expectedException.expectMessage("Insufficient privileges");
session.checkIsSystemAdministrator();
}
private ServerUserSession newUserSession(@Nullable UserDto userDto) {
return new ServerUserSession(dbClient, organizationFlags, defaultOrganizationProvider, userDto);
}
private ServerUserSession newAnonymousSession() {
return newUserSession(null);
}
private void addProjectPermissions(ComponentDto component, String... permissions) {
addPermissions(component, permissions);
}
private void addPermissions(@Nullable ComponentDto component, String... permissions) {
for (String permission : permissions) {
if (component == null) {
db.users().insertPermissionOnUser(user, OrganizationPermission.fromKey(permission));
} else {
db.users().insertProjectPermissionOnUser(user, permission, component);
}
}
}
private void expectInsufficientPrivilegesForbiddenException() {
expectedException.expect(ForbiddenException.class);
expectedException.expectMessage("Insufficient privileges");
}
}