*/
package org.sonar.server.component;
+import java.util.Collection;
import java.util.List;
import org.sonar.api.ce.ComputeEngineSide;
import org.sonar.api.resources.ResourceType;
private final DbClient dbClient;
private final ResourceTypes resourceTypes;
- private final List<ProjectIndexer> projectIndexers;
+ private final Collection<ProjectIndexer> projectIndexers;
- public ComponentCleanerService(DbClient dbClient, ResourceTypes resourceTypes, ProjectIndexer[] projectIndexers) {
+ public ComponentCleanerService(DbClient dbClient, ResourceTypes resourceTypes, ProjectIndexer... projectIndexers) {
this.dbClient = dbClient;
this.resourceTypes = resourceTypes;
this.projectIndexers = asList(projectIndexers);
private final UserSession userSession;
private final ProjectIndexer[] projectIndexers;
- public ComponentService(DbClient dbClient, UserSession userSession, ProjectIndexer[] projectIndexers) {
+ public ComponentService(DbClient dbClient, UserSession userSession, ProjectIndexer... projectIndexers) {
this.dbClient = dbClient;
this.userSession = userSession;
this.projectIndexers = projectIndexers;
package org.sonar.server.component;
+import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import org.sonar.db.DbSession;
import org.sonar.db.component.ComponentDto;
import org.sonar.server.es.ProjectIndexer;
+import org.sonar.server.es.ProjectIndexer.Cause;
import org.sonar.server.exceptions.BadRequestException;
import org.sonar.server.favorite.FavoriteUpdater;
import org.sonar.server.permission.PermissionTemplateService;
+import static java.util.Arrays.asList;
import static org.sonar.api.resources.Qualifiers.PROJECT;
import static org.sonar.core.component.ComponentKeys.isValidModuleKey;
import static org.sonar.server.ws.WsUtils.checkRequest;
private final System2 system2;
private final PermissionTemplateService permissionTemplateService;
private final FavoriteUpdater favoriteUpdater;
- private final ProjectIndexer[] projectIndexers;
+ private final Collection<ProjectIndexer> projectIndexers;
public ComponentUpdater(DbClient dbClient, I18n i18n, System2 system2,
PermissionTemplateService permissionTemplateService, FavoriteUpdater favoriteUpdater,
- ProjectIndexer[] projectIndexers) {
+ ProjectIndexer... projectIndexers) {
this.dbClient = dbClient;
this.i18n = i18n;
this.system2 = system2;
this.permissionTemplateService = permissionTemplateService;
this.favoriteUpdater = favoriteUpdater;
- this.projectIndexers = projectIndexers;
+ this.projectIndexers = asList(projectIndexers);
}
/**
}
private void index(ComponentDto project) {
- for (ProjectIndexer projectIndexer : projectIndexers) {
- projectIndexer.indexProject(project.uuid(), ProjectIndexer.Cause.PROJECT_CREATION);
- }
+ projectIndexers.forEach(i -> i.indexProject(project.uuid(), Cause.PROJECT_CREATION));
}
}
import org.sonar.server.user.UserSession;
import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
-import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
import static org.elasticsearch.index.query.QueryBuilders.termQuery;
@ServerSide
public static final String FIELD_USER_LOGINS = "users";
public static final String FIELD_UPDATED_AT = "updatedAt";
+ /**
+ * When true, then anybody can access to the project. In that case
+ * it's useless to store granted groups and users. The related
+ * fields are empty.
+ */
+ public static final String FIELD_ALLOW_ANYONE = "allowAnyone";
+
private final UserSession userSession;
public AuthorizationTypeSupport(UserSession userSession) {
authType.createDateTimeField(FIELD_UPDATED_AT);
authType.stringFieldBuilder(FIELD_GROUP_NAMES).disableNorms().build();
authType.stringFieldBuilder(FIELD_USER_LOGINS).disableNorms().build();
+ authType.createBooleanField(FIELD_ALLOW_ANYONE);
authType.setEnableSource(false);
return type;
}
public QueryBuilder createQueryFilter() {
Integer userLogin = userSession.getUserId();
Set<String> userGroupNames = userSession.getUserGroups();
- BoolQueryBuilder groupsAndUser = boolQuery();
+ BoolQueryBuilder filter = boolQuery();
+
+ // anyone
+ filter.should(QueryBuilders.termQuery(FIELD_ALLOW_ANYONE, true));
+ // users
Optional.ofNullable(userLogin)
.map(Integer::longValue)
- .ifPresent(userId -> groupsAndUser.should(termQuery(FIELD_USER_LOGINS, userId)));
+ .ifPresent(userId -> filter.should(termQuery(FIELD_USER_LOGINS, userId)));
- userGroupNames
- .forEach(group -> groupsAndUser.should(termQuery(FIELD_GROUP_NAMES, group)));
+ // groups
+ userGroupNames.forEach(
+ group -> filter.should(termQuery(FIELD_GROUP_NAMES, group)));
return QueryBuilders.hasParentQuery(TYPE_AUTHORIZATION,
- QueryBuilders.boolQuery().must(matchAllQuery()).filter(groupsAndUser));
+ QueryBuilders.boolQuery().filter(filter));
}
}
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Throwables;
-import com.google.common.collect.ImmutableMap;
import com.google.common.util.concurrent.Uninterruptibles;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
private final ThreadPoolExecutor executor;
private final DbClient dbClient;
private final EsClient esClient;
- private final List<AuthorizationScope> authorizationScopes;
+ private final Collection<AuthorizationScope> authorizationScopes;
public PermissionIndexer(DbClient dbClient, EsClient esClient, NeedAuthorizationIndexer[] needAuthorizationIndexers) {
this(dbClient, esClient, Arrays.stream(needAuthorizationIndexers)
}
@VisibleForTesting
- public PermissionIndexer(DbClient dbClient, EsClient esClient, List<AuthorizationScope> authorizationScopes) {
+ public PermissionIndexer(DbClient dbClient, EsClient esClient, Collection<AuthorizationScope> authorizationScopes) {
this.executor = new ThreadPoolExecutor(0, 1, 0L, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
this.dbClient = dbClient;
this.esClient = esClient;
if (isEmpty) {
Future submit = executor.submit(() -> {
-
- authorizationScopes.forEach(scope -> truncateAuthorizationType(scope.getIndexName()));
+ authorizationScopes.stream()
+ .map(AuthorizationScope::getIndexName)
+ .forEach(this::truncateAuthorizationType);
try (DbSession dbSession = dbClient.openSession(false)) {
index(new PermissionIndexerDao().selectAll(dbClient, dbSession));
}
private static IndexRequest newIndexRequest(PermissionIndexerDao.Dto dto, String indexName) {
- Map<String, Object> doc = ImmutableMap.of(
- AuthorizationTypeSupport.FIELD_GROUP_NAMES, dto.getGroups(),
- AuthorizationTypeSupport.FIELD_USER_LOGINS, dto.getUsers(),
- AuthorizationTypeSupport.FIELD_UPDATED_AT, new Date(dto.getUpdatedAt()));
+ Map<String, Object> doc = new HashMap<>();
+ doc.put(AuthorizationTypeSupport.FIELD_UPDATED_AT, new Date(dto.getUpdatedAt()));
+ if (dto.isAllowAnyone()) {
+ doc.put(AuthorizationTypeSupport.FIELD_ALLOW_ANYONE, true);
+ // no need to feed users and groups
+ } else {
+ doc.put(AuthorizationTypeSupport.FIELD_ALLOW_ANYONE, false);
+ doc.put(AuthorizationTypeSupport.FIELD_GROUP_NAMES, dto.getGroups());
+ doc.put(AuthorizationTypeSupport.FIELD_USER_LOGINS, dto.getUsers());
+ }
return new IndexRequest(indexName, TYPE_AUTHORIZATION, dto.getProjectUuid())
.routing(dto.getProjectUuid())
.source(doc);
private final String qualifier;
private final List<Long> users = Lists.newArrayList();
private final List<String> groups = Lists.newArrayList();
+ private boolean allowAnyone = false;
public Dto(String projectUuid, long updatedAt, String qualifier) {
this.projectUuid = projectUuid;
public List<String> getGroups() {
return groups;
}
+
+ public void allowAnyone() {
+ this.allowAnyone = true;
+ }
+
+ public boolean isAllowAnyone() {
+ return allowAnyone;
+ }
}
/**
*/
private static final int NB_OF_CONDITION_PLACEHOLDERS = 3;
+ private enum RowKind {
+ USER, GROUP, ANYONE
+ }
+
private static final String SQL_TEMPLATE = "SELECT " +
+ " project_authorization.kind as kind, " +
" project_authorization.project as project, " +
" project_authorization.user_id as user_id, " +
" project_authorization.permission_group as permission_group, " +
// users
- " SELECT " +
+ " SELECT '" + RowKind.USER + "' as kind," +
" projects.uuid AS project, " +
" projects.authorization_updated_at AS updated_at, " +
" projects.qualifier AS qualifier, " +
// groups
- " SELECT " +
+ " SELECT '" + RowKind.GROUP + "' as kind," +
" projects.uuid AS project, " +
" projects.authorization_updated_at AS updated_at, " +
" projects.qualifier AS qualifier, " +
// Anyone virtual group
- " SELECT " +
+ " SELECT '" + RowKind.ANYONE + "' as kind," +
" projects.uuid AS project, " +
" projects.authorization_updated_at AS updated_at, " +
" projects.qualifier AS qualifier, " +
" NULL AS user_id, " +
- " 'Anyone' AS permission_group " +
+ " NULL AS permission_group " +
" FROM projects " +
" INNER JOIN group_roles ON group_roles.resource_id = projects.id AND group_roles.role='user' " +
" WHERE " +
private static PreparedStatement createStatement(DbClient dbClient, DbSession session, List<String> projectUuids) throws SQLException {
String sql;
- if (!projectUuids.isEmpty()) {
- sql = StringUtils.replace(SQL_TEMPLATE, "{projectsCondition}", " AND (" + repeatCondition("projects.uuid = ?", projectUuids.size(), "OR") + ")");
- } else {
+ if (projectUuids.isEmpty()) {
sql = StringUtils.replace(SQL_TEMPLATE, "{projectsCondition}", "");
+ } else {
+ sql = StringUtils.replace(SQL_TEMPLATE, "{projectsCondition}", " AND (" + repeatCondition("projects.uuid = ?", projectUuids.size(), "OR") + ")");
}
PreparedStatement stmt = dbClient.getMyBatis().newScrollingSelectStatement(session, sql);
- if (!projectUuids.isEmpty()) {
- int index = 1;
- for (int i = 1; i <= NB_OF_CONDITION_PLACEHOLDERS; i++) {
- for (int uuidIndex = 0; uuidIndex < projectUuids.size(); uuidIndex++) {
- stmt.setString(index, projectUuids.get(uuidIndex));
- index++;
- }
+ int index = 1;
+ for (int i = 1; i <= NB_OF_CONDITION_PLACEHOLDERS; i++) {
+ for (String projectUuid : projectUuids) {
+ stmt.setString(index, projectUuid);
+ index++;
}
}
return stmt;
}
private static void processRow(ResultSet rs, Map<String, Dto> dtosByProjectUuid) throws SQLException {
- String projectUuid = rs.getString(1);
- String group = rs.getString(3);
+ RowKind rowKind = RowKind.valueOf(rs.getString(1));
+ String projectUuid = rs.getString(2);
Dto dto = dtosByProjectUuid.get(projectUuid);
if (dto == null) {
- long updatedAt = rs.getLong(4);
- String qualifier = rs.getString(5);
+ long updatedAt = rs.getLong(5);
+ String qualifier = rs.getString(6);
dto = new Dto(projectUuid, updatedAt, qualifier);
dtosByProjectUuid.put(projectUuid, dto);
}
- Long userId = rs.getLong(2);
- if (!rs.wasNull()) {
- dto.addUser(userId);
- }
- if (StringUtils.isNotBlank(group)) {
- dto.addGroup(group);
+ switch (rowKind) {
+ case USER:
+ Long userId = rs.getLong(3);
+ if (!rs.wasNull()) {
+ dto.addUser(userId);
+ }
+ break;
+ case GROUP:
+ String group = rs.getString(4);
+ if (!rs.wasNull()) {
+ dto.addGroup(group);
+ }
+ break;
+ case ANYONE:
+ dto.allowAnyone();
+ break;
}
}
}
private DbSession dbSession = db.getSession();
private ProjectIndexer projectIndexer = mock(ProjectIndexer.class);
private ResourceTypes mockResourceTypes = mock(ResourceTypes.class);
- private ComponentCleanerService underTest = new ComponentCleanerService(dbClient, mockResourceTypes, new ProjectIndexer[] {projectIndexer});
+ private ComponentCleanerService underTest = new ComponentCleanerService(dbClient, mockResourceTypes, projectIndexer);
@Test
public void delete_project_from_db_and_index() {
private DbSession dbSession = dbTester.getSession();
private ProjectIndexer projectIndexer = mock(ProjectIndexer.class);
- private ComponentService underTest = new ComponentService(dbClient, userSession, new ProjectIndexer[] {projectIndexer});
+ private ComponentService underTest = new ComponentService(dbClient, userSession, projectIndexer);
@Test
public void should_fail_silently_on_components_not_found_if_told_so() {
private DbClient dbClient = db.getDbClient();
private DbSession dbSession = db.getSession();
private ProjectIndexer projectIndexer = mock(ProjectIndexer.class);
- private ComponentService underTest = new ComponentService(dbClient, userSession, new ProjectIndexer[] {projectIndexer});
+ private ComponentService underTest = new ComponentService(dbClient, userSession, projectIndexer);
@Test
public void update_project_key() {
private ComponentUpdater underTest = new ComponentUpdater(db.getDbClient(), i18n, system2,
permissionTemplateService,
new FavoriteUpdater(db.getDbClient()),
- new ProjectIndexer[] {projectIndexer});
+ projectIndexer);
@Test
public void should_persist_and_index_when_creating_project() throws Exception {
import static org.assertj.core.api.Assertions.assertThat;
import static org.sonar.api.resources.Qualifiers.PROJECT;
import static org.sonar.api.resources.Qualifiers.VIEW;
-import static org.sonar.api.security.DefaultGroups.ANYONE;
import static org.sonar.api.web.UserRole.ADMIN;
import static org.sonar.api.web.UserRole.USER;
assertThat(dtos).hasSize(4);
PermissionIndexerDao.Dto project1Authorization = getByProjectUuid(project1.uuid(), dtos);
- assertThat(project1Authorization.getGroups()).containsOnly(ANYONE, group.getName());
+ assertThat(project1Authorization.getGroups()).containsOnly(group.getName());
+ assertThat(project1Authorization.isAllowAnyone()).isTrue();
assertThat(project1Authorization.getUsers()).containsOnly(user1.getId());
assertThat(project1Authorization.getUpdatedAt()).isNotNull();
assertThat(project1Authorization.getQualifier()).isEqualTo(PROJECT);
PermissionIndexerDao.Dto view1Authorization = getByProjectUuid(view1.uuid(), dtos);
- assertThat(view1Authorization.getGroups()).containsOnly(ANYONE, group.getName());
+ assertThat(view1Authorization.getGroups()).containsOnly(group.getName());
+ assertThat(view1Authorization.isAllowAnyone()).isTrue();
assertThat(view1Authorization.getUsers()).containsOnly(user1.getId());
assertThat(view1Authorization.getUpdatedAt()).isNotNull();
assertThat(view1Authorization.getQualifier()).isEqualTo(VIEW);
PermissionIndexerDao.Dto project2Authorization = getByProjectUuid(project2.uuid(), dtos);
- assertThat(project2Authorization.getGroups()).containsOnly(ANYONE);
+ assertThat(project2Authorization.getGroups()).isEmpty();
+ assertThat(project2Authorization.isAllowAnyone()).isTrue();
assertThat(project2Authorization.getUsers()).containsOnly(user1.getId(), user2.getId());
assertThat(project2Authorization.getUpdatedAt()).isNotNull();
assertThat(project2Authorization.getQualifier()).isEqualTo(PROJECT);
PermissionIndexerDao.Dto view2Authorization = getByProjectUuid(view2.uuid(), dtos);
- assertThat(view2Authorization.getGroups()).containsOnly(ANYONE);
+ assertThat(view2Authorization.getGroups()).isEmpty();
+ assertThat(view2Authorization.isAllowAnyone()).isTrue();
assertThat(view2Authorization.getUsers()).containsOnly(user1.getId(), user2.getId());
assertThat(view2Authorization.getUpdatedAt()).isNotNull();
assertThat(view2Authorization.getQualifier()).isEqualTo(VIEW);
assertThat(dtos).hasSize(4);
PermissionIndexerDao.Dto project1Authorization = dtos.get(project1.uuid());
- assertThat(project1Authorization.getGroups()).containsOnly(ANYONE, group.getName());
+ assertThat(project1Authorization.getGroups()).containsOnly(group.getName());
+ assertThat(project1Authorization.isAllowAnyone()).isTrue();
assertThat(project1Authorization.getUsers()).containsOnly(user1.getId());
assertThat(project1Authorization.getUpdatedAt()).isNotNull();
assertThat(project1Authorization.getQualifier()).isEqualTo(PROJECT);
PermissionIndexerDao.Dto view1Authorization = dtos.get(view1.uuid());
- assertThat(view1Authorization.getGroups()).containsOnly(ANYONE, group.getName());
+ assertThat(view1Authorization.getGroups()).containsOnly(group.getName());
+ assertThat(view1Authorization.isAllowAnyone()).isTrue();
assertThat(view1Authorization.getUsers()).containsOnly(user1.getId());
assertThat(view1Authorization.getUpdatedAt()).isNotNull();
assertThat(view1Authorization.getQualifier()).isEqualTo(VIEW);
PermissionIndexerDao.Dto project2Authorization = dtos.get(project2.uuid());
- assertThat(project2Authorization.getGroups()).containsOnly(ANYONE);
+ assertThat(project2Authorization.getGroups()).isEmpty();
+ assertThat(project2Authorization.isAllowAnyone()).isTrue();
assertThat(project2Authorization.getUsers()).containsOnly(user1.getId(), user2.getId());
assertThat(project2Authorization.getUpdatedAt()).isNotNull();
assertThat(project2Authorization.getQualifier()).isEqualTo(PROJECT);
PermissionIndexerDao.Dto view2Authorization = dtos.get(view2.uuid());
- assertThat(view2Authorization.getGroups()).containsOnly(ANYONE);
+ assertThat(view2Authorization.getGroups()).isEmpty();
+ assertThat(view2Authorization.isAllowAnyone()).isTrue();
assertThat(view2Authorization.getUsers()).containsOnly(user1.getId(), user2.getId());
assertThat(view2Authorization.getUpdatedAt()).isNotNull();
assertThat(view2Authorization.getQualifier()).isEqualTo(VIEW);
import org.sonar.db.DbTester;
import org.sonar.db.component.ComponentDbTester;
import org.sonar.db.component.ComponentDto;
+import org.sonar.db.organization.OrganizationDto;
import org.sonar.db.user.GroupDto;
import org.sonar.db.user.UserDbTester;
import org.sonar.db.user.UserDto;
underTest.indexAllIfEmpty();
// anonymous
- verifyAnonymousNotAuthorized(project);
+ verifyAnyoneNotAuthorized(project);
// user1 has access
verifyAuthorized(project, user1);
underTest.indexAllIfEmpty();
// anonymous
- verifyAnonymousNotAuthorized(project);
+ verifyAnyoneNotAuthorized(project);
// group1 has access
verifyAuthorized(project, user1, group1);
underTest.indexAllIfEmpty();
// anonymous
- verifyAnonymousNotAuthorized(project);
+ verifyAnyoneNotAuthorized(project);
// has direct access
verifyAuthorized(project, user1);
underTest.indexAllIfEmpty();
- verifyAnonymousNotAuthorized(project);
+ verifyAnyoneNotAuthorized(project);
verifyNotAuthorized(project, user);
verifyNotAuthorized(project, user, group);
}
underTest.indexAllIfEmpty();
- verifyAnonymousAuthorized(project);
+ verifyAnyoneAuthorized(project);
verifyAuthorized(project, user);
verifyAuthorized(project, user, group);
}
underTest.indexAllIfEmpty();
- verifyAnonymousNotAuthorized(project);
+ verifyAnyoneNotAuthorized(project);
verifyAuthorized(project, user1);
verifyNotAuthorized(project, user2);
}
underTest.indexAllIfEmpty();
- verifyAnonymousNotAuthorized(project);
+ verifyAnyoneNotAuthorized(project);
verifyNotAuthorized(project, user1);
}
+ @Test
+ public void permissions_on_anyone_should_not_conflict_between_organizations() {
+ ComponentDto projectOnOrg1 = createAndIndexProject(dbTester.organizations().insert());
+ ComponentDto projectOnOrg2 = createAndIndexProject(dbTester.organizations().insert());
+ UserDto user = userDbTester.insertUser();
+ userDbTester.insertProjectPermissionOnAnyone(USER, projectOnOrg1);
+ userDbTester.insertProjectPermissionOnUser(user, USER, projectOnOrg2);
+
+ underTest.indexAllIfEmpty();
+
+ verifyAnyoneAuthorized(projectOnOrg1);
+ verifyAnyoneNotAuthorized(projectOnOrg2);
+ verifyAuthorized(projectOnOrg1, user);// because anyone
+ verifyAuthorized(projectOnOrg2, user);
+ }
+
private void verifyAuthorized(ComponentDto project, UserDto user) {
logIn(user);
verifyAuthorized(project, true);
verifyAuthorized(project, false);
}
- private void verifyAnonymousAuthorized(ComponentDto project) {
+ private void verifyAnyoneAuthorized(ComponentDto project) {
userSession.anonymous();
verifyAuthorized(project, true);
}
- private void verifyAnonymousNotAuthorized(ComponentDto project) {
+ private void verifyAnyoneNotAuthorized(ComponentDto project) {
userSession.anonymous();
verifyAuthorized(project, false);
}
fooIndexer.indexProject(project.uuid(), ProjectIndexer.Cause.PROJECT_CREATION);
return project;
}
+
+ private ComponentDto createAndIndexProject(OrganizationDto org) {
+ ComponentDto project = componentDbTester.insertProject(org);
+ fooIndexer.indexProject(project.uuid(), ProjectIndexer.Cause.PROJECT_CREATION);
+ return project;
+ }
}