Browse Source

SONAR-13936 Remove use of organizations in Groups

tags/8.6.0.39681
Michal Duda 3 years ago
parent
commit
4d8f0c4bf3
45 changed files with 537 additions and 360 deletions
  1. 0
    2
      server/sonar-db-dao/src/main/resources/org/sonar/db/user/GroupMapper.xml
  2. 2
    2
      server/sonar-db-dao/src/schema/schema-sq.ddl
  3. 1
    1
      server/sonar-db-dao/src/test/java/org/sonar/db/permission/AuthorizationDaoTest.java
  4. 6
    18
      server/sonar-db-dao/src/testFixtures/java/org/sonar/db/user/UserDbTester.java
  5. 50
    0
      server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/AddUniqueIndexOnNameColumnOfGroupsTable.java
  6. 3
    0
      server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/DbVersion86.java
  7. 39
    0
      server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/DropOrganizationInGroups.java
  8. 50
    0
      server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/MakeNameColumnInGroupsTableNotNullable.java
  9. 41
    0
      server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v86/AddUniqueIndexOnNameColumnOfGroupsTableTest.java
  10. 41
    0
      server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v86/DropOrganizationInGroupsTest.java
  11. 42
    0
      server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v86/MakeNameColumnInGroupsTableNotNullableTest.java
  12. 8
    0
      server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v86/AddUniqueIndexOnNameColumnOfGroupsTableTest/schema.sql
  13. 9
    0
      server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v86/DropOrganizationInGroupsTest/schema.sql
  14. 8
    0
      server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v86/MakeNameColumnInGroupsTableNotNullableTest/schema.sql
  15. 1
    0
      server/sonar-server-common/src/testFixtures/java/org/sonar/server/organization/TestDefaultOrganizationProvider.java
  16. 4
    10
      server/sonar-webserver-auth/src/main/java/org/sonar/server/usergroups/DefaultGroupFinder.java
  17. 6
    5
      server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/HttpHeadersAuthenticationTest.java
  18. 2
    2
      server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/UserRegistrarImplTest.java
  19. 6
    6
      server/sonar-webserver-auth/src/test/java/org/sonar/server/user/UserUpdaterCreateTest.java
  20. 10
    10
      server/sonar-webserver-auth/src/test/java/org/sonar/server/user/UserUpdaterReactivateTest.java
  21. 1
    1
      server/sonar-webserver-auth/src/test/java/org/sonar/server/user/UserUpdaterUpdateTest.java
  22. 15
    19
      server/sonar-webserver-auth/src/test/java/org/sonar/server/usergroups/DefaultGroupFinderTest.java
  23. 1
    1
      server/sonar-webserver-webapi/src/main/java/org/sonar/server/permission/ws/PermissionWsSupport.java
  24. 1
    9
      server/sonar-webserver-webapi/src/main/java/org/sonar/server/usergroups/ws/CreateAction.java
  25. 14
    34
      server/sonar-webserver-webapi/src/main/java/org/sonar/server/usergroups/ws/GroupWsRef.java
  26. 3
    12
      server/sonar-webserver-webapi/src/main/java/org/sonar/server/usergroups/ws/GroupWsSupport.java
  27. 1
    9
      server/sonar-webserver-webapi/src/main/java/org/sonar/server/usergroups/ws/SearchAction.java
  28. 2
    5
      server/sonar-webserver-webapi/src/main/java/org/sonar/server/usergroups/ws/UpdateAction.java
  29. 1
    1
      server/sonar-webserver-webapi/src/main/resources/org/sonar/server/user/ws/groups-example.json
  30. 1
    1
      server/sonar-webserver-webapi/src/main/resources/org/sonar/server/usergroups/ws/search-example.json
  31. 1
    1
      server/sonar-webserver-webapi/src/test/java/org/sonar/server/permission/ws/BasePermissionWsTest.java
  32. 1
    1
      server/sonar-webserver-webapi/src/test/java/org/sonar/server/permission/ws/template/DeleteTemplateActionTest.java
  33. 2
    4
      server/sonar-webserver-webapi/src/test/java/org/sonar/server/user/ws/ChangePasswordActionTest.java
  34. 2
    3
      server/sonar-webserver-webapi/src/test/java/org/sonar/server/user/ws/CreateActionTest.java
  35. 16
    28
      server/sonar-webserver-webapi/src/test/java/org/sonar/server/user/ws/GroupsActionTest.java
  36. 2
    2
      server/sonar-webserver-webapi/src/test/java/org/sonar/server/user/ws/UpdateActionTest.java
  37. 6
    8
      server/sonar-webserver-webapi/src/test/java/org/sonar/server/usergroups/ws/AddUserActionTest.java
  38. 4
    11
      server/sonar-webserver-webapi/src/test/java/org/sonar/server/usergroups/ws/CreateActionTest.java
  39. 13
    15
      server/sonar-webserver-webapi/src/test/java/org/sonar/server/usergroups/ws/DeleteActionTest.java
  40. 18
    19
      server/sonar-webserver-webapi/src/test/java/org/sonar/server/usergroups/ws/GroupWsRefTest.java
  41. 6
    7
      server/sonar-webserver-webapi/src/test/java/org/sonar/server/usergroups/ws/RemoveUserActionTest.java
  42. 35
    47
      server/sonar-webserver-webapi/src/test/java/org/sonar/server/usergroups/ws/SearchActionTest.java
  43. 6
    9
      server/sonar-webserver-webapi/src/test/java/org/sonar/server/usergroups/ws/UpdateActionTest.java
  44. 55
    56
      server/sonar-webserver-webapi/src/test/java/org/sonar/server/usergroups/ws/UsersActionTest.java
  45. 1
    1
      sonar-ws/src/main/protobuf/ws-user_groups.proto

+ 0
- 2
server/sonar-db-dao/src/main/resources/org/sonar/db/user/GroupMapper.xml View File

<insert id="insert" parameterType="Group" useGeneratedKeys="false"> <insert id="insert" parameterType="Group" useGeneratedKeys="false">
insert into groups ( insert into groups (
uuid, uuid,
organization_uuid,
name, name,
description, description,
created_at, created_at,
updated_at updated_at
) values ( ) values (
#{uuid,jdbcType=VARCHAR}, #{uuid,jdbcType=VARCHAR},
'asd',
#{name,jdbcType=VARCHAR}, #{name,jdbcType=VARCHAR},
#{description,jdbcType=VARCHAR}, #{description,jdbcType=VARCHAR},
#{createdAt,jdbcType=TIMESTAMP}, #{createdAt,jdbcType=TIMESTAMP},

+ 2
- 2
server/sonar-db-dao/src/schema/schema-sq.ddl View File

CREATE UNIQUE INDEX "UNIQ_GROUP_ROLES" ON "GROUP_ROLES"("GROUP_UUID", "COMPONENT_UUID", "ROLE"); CREATE UNIQUE INDEX "UNIQ_GROUP_ROLES" ON "GROUP_ROLES"("GROUP_UUID", "COMPONENT_UUID", "ROLE");


CREATE TABLE "GROUPS"( CREATE TABLE "GROUPS"(
"ORGANIZATION_UUID" VARCHAR(40) NOT NULL,
"NAME" VARCHAR(500),
"NAME" VARCHAR(500) NOT NULL,
"DESCRIPTION" VARCHAR(200), "DESCRIPTION" VARCHAR(200),
"CREATED_AT" TIMESTAMP, "CREATED_AT" TIMESTAMP,
"UPDATED_AT" TIMESTAMP, "UPDATED_AT" TIMESTAMP,
"UUID" VARCHAR(40) NOT NULL "UUID" VARCHAR(40) NOT NULL
); );
ALTER TABLE "GROUPS" ADD CONSTRAINT "PK_GROUPS" PRIMARY KEY("UUID"); ALTER TABLE "GROUPS" ADD CONSTRAINT "PK_GROUPS" PRIMARY KEY("UUID");
CREATE UNIQUE INDEX "UNIQ_GROUPS_NAME" ON "GROUPS"("NAME");


CREATE TABLE "GROUPS_USERS"( CREATE TABLE "GROUPS_USERS"(
"GROUP_UUID" VARCHAR(40) NOT NULL, "GROUP_UUID" VARCHAR(40) NOT NULL,

+ 1
- 1
server/sonar-db-dao/src/test/java/org/sonar/db/permission/AuthorizationDaoTest.java View File

db.users().insertMember(group2, user3); db.users().insertMember(group2, user3);


// group3 has the permission "perm1" but has no users // group3 has the permission "perm1" but has no users
GroupDto group3 = db.users().insertGroup("g2");
GroupDto group3 = db.users().insertGroup("g3");
db.users().insertPermissionOnGroup(group3, "perm1"); db.users().insertPermissionOnGroup(group3, "perm1");


db.users().insertPermissionOnUser(user4, "perm1"); db.users().insertPermissionOnUser(user4, "perm1");

+ 6
- 18
server/sonar-db-dao/src/testFixtures/java/org/sonar/db/user/UserDbTester.java View File

import java.util.function.Consumer; import java.util.function.Consumer;
import javax.annotation.CheckForNull; import javax.annotation.CheckForNull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import org.sonar.api.security.DefaultGroups;
import org.sonar.api.web.UserRole; import org.sonar.api.web.UserRole;
import org.sonar.core.util.Uuids; import org.sonar.core.util.Uuids;
import org.sonar.core.util.stream.MoreCollectors; import org.sonar.core.util.stream.MoreCollectors;
import org.sonar.db.project.ProjectDto; import org.sonar.db.project.ProjectDto;


import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
import static java.lang.String.format;
import static java.util.Arrays.stream; import static java.util.Arrays.stream;
import static org.apache.commons.lang.math.RandomUtils.nextLong; import static org.apache.commons.lang.math.RandomUtils.nextLong;
import static org.sonar.db.permission.OrganizationPermission.ADMINISTER; import static org.sonar.db.permission.OrganizationPermission.ADMINISTER;
import static org.sonar.db.user.GroupTesting.newGroupDto;


public class UserDbTester { public class UserDbTester {
private static final Set<String> PUBLIC_PERMISSIONS = ImmutableSet.of(UserRole.USER, UserRole.CODEVIEWER); // FIXME to check with Simon private static final Set<String> PUBLIC_PERMISSIONS = ImmutableSet.of(UserRole.USER, UserRole.CODEVIEWER); // FIXME to check with Simon
// GROUPS // GROUPS


public GroupDto insertGroup(String name) { public GroupDto insertGroup(String name) {
GroupDto group = GroupTesting.newGroupDto().setName(name);
GroupDto group = newGroupDto().setName(name);
return insertGroup(group); return insertGroup(group);
} }


public GroupDto insertGroup() { public GroupDto insertGroup() {
GroupDto group = GroupTesting.newGroupDto();
GroupDto group = newGroupDto();
return insertGroup(group); return insertGroup(group);
} }


return dto; return dto;
} }


public GroupDto insertDefaultGroup(GroupDto dto) {
db.getDbClient().organizationDao().getDefaultGroupUuid(db.getSession(), db.getDefaultOrganization().getUuid())
.ifPresent(groupUuid -> {
throw new IllegalArgumentException(format("Organization '%s' has already a default group", db.getDefaultOrganization().getUuid()));
});
db.getDbClient().groupDao().insert(db.getSession(), dto);
db.getDbClient().organizationDao().setDefaultGroupUuid(db.getSession(), db.getDefaultOrganization().getUuid(), dto);
public GroupDto insertDefaultGroup() {
GroupDto dto = db.getDbClient().groupDao().insert(db.getSession(), newGroupDto().setName(DefaultGroups.USERS).setDescription("Users"));
db.commit(); db.commit();
return dto; return dto;
} }


public GroupDto insertDefaultGroup(String name) {
return insertDefaultGroup(GroupTesting.newGroupDto().setName(name));
}

public GroupDto insertDefaultGroup() {
return insertDefaultGroup(GroupTesting.newGroupDto());
}

@CheckForNull @CheckForNull
public GroupDto selectGroupByUuid(String groupUuid) { public GroupDto selectGroupByUuid(String groupUuid) {
return db.getDbClient().groupDao().selectByUuid(db.getSession(), groupUuid); return db.getDbClient().groupDao().selectByUuid(db.getSession(), groupUuid);

+ 50
- 0
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/AddUniqueIndexOnNameColumnOfGroupsTable.java View File

/*
* SonarQube
* Copyright (C) 2009-2020 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.platform.db.migration.version.v86;

import java.sql.SQLException;
import org.sonar.db.Database;
import org.sonar.server.platform.db.migration.def.VarcharColumnDef;
import org.sonar.server.platform.db.migration.sql.CreateIndexBuilder;
import org.sonar.server.platform.db.migration.step.DdlChange;

public class AddUniqueIndexOnNameColumnOfGroupsTable extends DdlChange {
private static final String TABLE_NAME = "groups";
private static final String INDEX_NAME = "uniq_groups_name";

public AddUniqueIndexOnNameColumnOfGroupsTable(Database db) {
super(db);
}

@Override
public void execute(Context context) throws SQLException {
context.execute(new CreateIndexBuilder()
.setUnique(true)
.setTable(TABLE_NAME)
.setName(INDEX_NAME)
.addColumn(new VarcharColumnDef.Builder()
.setColumnName("name")
.setIgnoreOracleUnit(true)
.setLimit(500)
.setIsNullable(false)
.build())
.build());
}
}

+ 3
- 0
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/DbVersion86.java View File

.add(4108, "Drop 'organization_uuid' in 'user_roles'", DropOrganizationInUserRoles.class) .add(4108, "Drop 'organization_uuid' in 'user_roles'", DropOrganizationInUserRoles.class)
.add(4109, "Drop 'organization_uuid' in 'group_roles'", DropOrganizationInGroupRoles.class) .add(4109, "Drop 'organization_uuid' in 'group_roles'", DropOrganizationInGroupRoles.class)
.add(4110, "Drop 'organization_uuid' in 'permission_templates'", DropOrganizationInPermissionTemplates.class) .add(4110, "Drop 'organization_uuid' in 'permission_templates'", DropOrganizationInPermissionTemplates.class)
.add(4111, "Drop 'organization_uuid' in 'groups'", DropOrganizationInGroups.class)
.add(4112, "Make 'name' column in 'groups' table not nullable", MakeNameColumnInGroupsTableNotNullable.class)
.add(4113, "Make 'name' column in 'groups' table unique", AddUniqueIndexOnNameColumnOfGroupsTable.class)
; ;
} }
} }

+ 39
- 0
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/DropOrganizationInGroups.java View File

/*
* SonarQube
* Copyright (C) 2009-2020 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.platform.db.migration.version.v86;

import java.sql.SQLException;
import org.sonar.db.Database;
import org.sonar.server.platform.db.migration.sql.DropColumnsBuilder;
import org.sonar.server.platform.db.migration.step.DdlChange;

public class DropOrganizationInGroups extends DdlChange {

private static final String TABLE_NAME = "groups";

public DropOrganizationInGroups(Database db) {
super(db);
}

@Override
public void execute(Context context) throws SQLException {
context.execute(new DropColumnsBuilder(getDialect(), TABLE_NAME, "organization_uuid").build());
}
}

+ 50
- 0
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/MakeNameColumnInGroupsTableNotNullable.java View File

/*
* SonarQube
* Copyright (C) 2009-2020 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.platform.db.migration.version.v86;

import java.sql.SQLException;
import org.sonar.db.Database;
import org.sonar.server.platform.db.migration.def.VarcharColumnDef;
import org.sonar.server.platform.db.migration.sql.AlterColumnsBuilder;
import org.sonar.server.platform.db.migration.step.DdlChange;

import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.newVarcharColumnDefBuilder;

public class MakeNameColumnInGroupsTableNotNullable extends DdlChange {
private static final String TABLE = "groups";

private static final VarcharColumnDef columnDefinition = newVarcharColumnDefBuilder()
.setColumnName("name")
.setIsNullable(false)
.setIgnoreOracleUnit(true)
.setLimit(500)
.build();

public MakeNameColumnInGroupsTableNotNullable(Database db) {
super(db);
}

@Override
public void execute(Context context) throws SQLException {
context.execute(new AlterColumnsBuilder(getDialect(), TABLE)
.updateColumn(columnDefinition)
.build());
}
}

+ 41
- 0
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v86/AddUniqueIndexOnNameColumnOfGroupsTableTest.java View File

/*
* SonarQube
* Copyright (C) 2009-2020 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.platform.db.migration.version.v86;

import java.sql.SQLException;
import org.junit.Rule;
import org.junit.Test;
import org.sonar.db.CoreDbTester;
import org.sonar.server.platform.db.migration.step.MigrationStep;

public class AddUniqueIndexOnNameColumnOfGroupsTableTest {

@Rule
public CoreDbTester db = CoreDbTester.createForSchema(AddUniqueIndexOnNameColumnOfGroupsTableTest.class, "schema.sql");

private final MigrationStep underTest = new AddUniqueIndexOnNameColumnOfGroupsTable(db.database());

@Test
public void execute() throws SQLException {
underTest.execute();

db.assertUniqueIndex("groups", "uniq_groups_name", "name");
}
}

+ 41
- 0
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v86/DropOrganizationInGroupsTest.java View File

/*
* SonarQube
* Copyright (C) 2009-2020 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.platform.db.migration.version.v86;

import java.sql.SQLException;
import org.junit.Rule;
import org.junit.Test;
import org.sonar.db.CoreDbTester;
import org.sonar.server.platform.db.migration.step.MigrationStep;

public class DropOrganizationInGroupsTest {

@Rule
public CoreDbTester dbTester = CoreDbTester.createForSchema(DropOrganizationInGroupsTest.class, "schema.sql");

private final MigrationStep underTest = new DropOrganizationInGroups(dbTester.database());

@Test
public void column_has_been_dropped() throws SQLException {
underTest.execute();
dbTester.assertColumnDoesNotExist("groups", "organization_uuid");
}

}

+ 42
- 0
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v86/MakeNameColumnInGroupsTableNotNullableTest.java View File

/*
* SonarQube
* Copyright (C) 2009-2020 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.platform.db.migration.version.v86;

import java.sql.SQLException;
import org.junit.Rule;
import org.junit.Test;
import org.sonar.db.CoreDbTester;
import org.sonar.server.platform.db.migration.step.MigrationStep;

import static java.sql.Types.VARCHAR;

public class MakeNameColumnInGroupsTableNotNullableTest {
@Rule
public CoreDbTester db = CoreDbTester.createForSchema(MakeNameColumnInGroupsTableNotNullableTest.class, "schema.sql");

private final MigrationStep underTest = new MakeNameColumnInGroupsTableNotNullable(db.database());

@Test
public void uuid_column_is_not_null() throws SQLException {
underTest.execute();

db.assertColumnDefinition("groups", "name", VARCHAR, 500, false);
}
}

+ 8
- 0
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v86/AddUniqueIndexOnNameColumnOfGroupsTableTest/schema.sql View File

CREATE TABLE "GROUPS"(
"NAME" VARCHAR(500) NOT NULL,
"DESCRIPTION" VARCHAR(200),
"CREATED_AT" TIMESTAMP,
"UPDATED_AT" TIMESTAMP,
"UUID" VARCHAR(40) NOT NULL
);
ALTER TABLE "GROUPS" ADD CONSTRAINT "PK_GROUPS" PRIMARY KEY("UUID");

+ 9
- 0
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v86/DropOrganizationInGroupsTest/schema.sql View File

CREATE TABLE "GROUPS"(
"ORGANIZATION_UUID" VARCHAR(40) NOT NULL,
"NAME" VARCHAR(500),
"DESCRIPTION" VARCHAR(200),
"CREATED_AT" TIMESTAMP,
"UPDATED_AT" TIMESTAMP,
"UUID" VARCHAR(40) NOT NULL
);
ALTER TABLE "GROUPS" ADD CONSTRAINT "PK_GROUPS" PRIMARY KEY("UUID");

+ 8
- 0
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v86/MakeNameColumnInGroupsTableNotNullableTest/schema.sql View File

CREATE TABLE "GROUPS"(
"NAME" VARCHAR(500),
"DESCRIPTION" VARCHAR(200),
"CREATED_AT" TIMESTAMP,
"UPDATED_AT" TIMESTAMP,
"UUID" VARCHAR(40) NOT NULL
);
ALTER TABLE "GROUPS" ADD CONSTRAINT "PK_GROUPS" PRIMARY KEY("UUID");

+ 1
- 0
server/sonar-server-common/src/testFixtures/java/org/sonar/server/organization/TestDefaultOrganizationProvider.java View File

import org.sonar.db.DbTester; import org.sonar.db.DbTester;
import org.sonar.db.organization.OrganizationDto; import org.sonar.db.organization.OrganizationDto;


//TODO fix this
public class TestDefaultOrganizationProvider implements DefaultOrganizationProvider { public class TestDefaultOrganizationProvider implements DefaultOrganizationProvider {


private final DefaultOrganizationProvider delegate; private final DefaultOrganizationProvider delegate;

+ 4
- 10
server/sonar-webserver-auth/src/main/java/org/sonar/server/usergroups/DefaultGroupFinder.java View File

*/ */
package org.sonar.server.usergroups; package org.sonar.server.usergroups;


import org.sonar.api.security.DefaultGroups;
import org.sonar.db.DbClient; import org.sonar.db.DbClient;
import org.sonar.db.DbSession; import org.sonar.db.DbSession;
import org.sonar.db.user.GroupDto; import org.sonar.db.user.GroupDto;
import org.sonar.server.organization.DefaultOrganizationProvider;

import static java.lang.String.format;
import static java.util.Objects.requireNonNull;


public class DefaultGroupFinder { public class DefaultGroupFinder {


private final DbClient dbClient; private final DbClient dbClient;
private final DefaultOrganizationProvider defaultOrganizationProvider;


public DefaultGroupFinder(DbClient dbClient, DefaultOrganizationProvider defaultOrganizationProvider) {
public DefaultGroupFinder(DbClient dbClient) {
this.dbClient = dbClient; this.dbClient = dbClient;
this.defaultOrganizationProvider = defaultOrganizationProvider;
} }


public GroupDto findDefaultGroup(DbSession dbSession) { public GroupDto findDefaultGroup(DbSession dbSession) {
String defaultGroupUuid = dbClient.organizationDao().getDefaultGroupUuid(dbSession, defaultOrganizationProvider.get().getUuid())
.orElseThrow(() -> new IllegalStateException("Default group cannot be found "));
return requireNonNull(dbClient.groupDao().selectByUuid(dbSession, defaultGroupUuid), format("Group '%s' cannot be found", defaultGroupUuid));
return dbClient.groupDao().selectByName(dbSession, DefaultGroups.USERS)
.orElseThrow(() -> new IllegalStateException("Default group cannot be found"));
} }


} }

+ 6
- 5
server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/HttpHeadersAuthenticationTest.java View File

private CredentialsLocalAuthentication localAuthentication = new CredentialsLocalAuthentication(db.getDbClient()); private CredentialsLocalAuthentication localAuthentication = new CredentialsLocalAuthentication(db.getDbClient());


private UserIndexer userIndexer = new UserIndexer(db.getDbClient(), es.client()); private UserIndexer userIndexer = new UserIndexer(db.getDbClient(), es.client());
private UserRegistrarImpl userIdentityAuthenticator = new UserRegistrarImpl(

private final DefaultGroupFinder defaultGroupFinder = new DefaultGroupFinder(db.getDbClient());
private final UserRegistrarImpl userIdentityAuthenticator = new UserRegistrarImpl(
db.getDbClient(), db.getDbClient(),
new UserUpdater(mock(NewUserNotifier.class), db.getDbClient(), userIndexer, defaultOrganizationProvider, new DefaultGroupFinder(db.getDbClient(), defaultOrganizationProvider),
settings.asConfig(),
new UserUpdater(mock(NewUserNotifier.class), db.getDbClient(), userIndexer, defaultOrganizationProvider, defaultGroupFinder, settings.asConfig(),
localAuthentication), localAuthentication),
new DefaultGroupFinder(db.getDbClient(), defaultOrganizationProvider));
defaultGroupFinder);


private HttpServletResponse response = mock(HttpServletResponse.class); private HttpServletResponse response = mock(HttpServletResponse.class);
private JwtHttpHandler jwtHttpHandler = mock(JwtHttpHandler.class); private JwtHttpHandler jwtHttpHandler = mock(JwtHttpHandler.class);
when(system2.now()).thenReturn(NOW); when(system2.now()).thenReturn(NOW);
group1 = db.users().insertGroup(GROUP1); group1 = db.users().insertGroup(GROUP1);
group2 = db.users().insertGroup(GROUP2); group2 = db.users().insertGroup(GROUP2);
sonarUsers = db.users().insertDefaultGroup("sonar-users");
sonarUsers = db.users().insertDefaultGroup();
} }


@Test @Test

+ 2
- 2
server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/UserRegistrarImplTest.java View File

private UserIndexer userIndexer = new UserIndexer(db.getDbClient(), es.client()); private UserIndexer userIndexer = new UserIndexer(db.getDbClient(), es.client());
private DefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(db); private DefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(db);
private CredentialsLocalAuthentication localAuthentication = new CredentialsLocalAuthentication(db.getDbClient()); private CredentialsLocalAuthentication localAuthentication = new CredentialsLocalAuthentication(db.getDbClient());
private DefaultGroupFinder groupFinder = new DefaultGroupFinder(db.getDbClient(), defaultOrganizationProvider);
private final DefaultGroupFinder groupFinder = new DefaultGroupFinder(db.getDbClient());
private UserUpdater userUpdater = new UserUpdater( private UserUpdater userUpdater = new UserUpdater(
mock(NewUserNotifier.class), mock(NewUserNotifier.class),
db.getDbClient(), db.getDbClient(),
} }


private GroupDto insertDefaultGroup() { private GroupDto insertDefaultGroup() {
return db.users().insertDefaultGroup("sonar-users");
return db.users().insertDefaultGroup();
} }


} }

+ 6
- 6
server/sonar-webserver-auth/src/test/java/org/sonar/server/user/UserUpdaterCreateTest.java View File

private CredentialsLocalAuthentication localAuthentication = new CredentialsLocalAuthentication(db.getDbClient()); private CredentialsLocalAuthentication localAuthentication = new CredentialsLocalAuthentication(db.getDbClient());


private UserUpdater underTest = new UserUpdater(newUserNotifier, dbClient, userIndexer, defaultOrganizationProvider, private UserUpdater underTest = new UserUpdater(newUserNotifier, dbClient, userIndexer, defaultOrganizationProvider,
new DefaultGroupFinder(dbClient, defaultOrganizationProvider), settings.asConfig(), localAuthentication);
new DefaultGroupFinder(dbClient), settings.asConfig(), localAuthentication);


@Test @Test
public void create_user() { public void create_user() {
.setName("Marius") .setName("Marius")
.setEmail("marius@mail.com") .setEmail("marius@mail.com")
.setPassword("password") .setPassword("password")
.setScmAccounts(asList("jo"))
.setScmAccounts(singletonList("jo"))
.build(), u -> { .build(), u -> {
}); });
} }
.setName("Marius2") .setName("Marius2")
.setEmail("marius2@mail.com") .setEmail("marius2@mail.com")
.setPassword("password2") .setPassword("password2")
.setScmAccounts(asList(DEFAULT_LOGIN))
.setScmAccounts(singletonList(DEFAULT_LOGIN))
.build(), u -> { .build(), u -> {
}); });
} }
.setName("Marius2") .setName("Marius2")
.setEmail("marius2@mail.com") .setEmail("marius2@mail.com")
.setPassword("password2") .setPassword("password2")
.setScmAccounts(asList("marius2@mail.com"))
.setScmAccounts(singletonList("marius2@mail.com"))
.build(), u -> { .build(), u -> {
}); });
} }
} }


@Test @Test
public void associate_default_group_when_creating_user_and_organizations_are_disabled() {
public void associate_default_group_when_creating_user() {
GroupDto defaultGroup = createDefaultGroup(); GroupDto defaultGroup = createDefaultGroup();


underTest.createAndCommit(db.getSession(), NewUser.builder() underTest.createAndCommit(db.getSession(), NewUser.builder()
.build(), u -> { .build(), u -> {
}); });


Multimap<String, String> groups = dbClient.groupMembershipDao().selectGroupsByLogins(session, asList("user"));
Multimap<String, String> groups = dbClient.groupMembershipDao().selectGroupsByLogins(session, singletonList("user"));
assertThat(groups.get("user")).containsOnly(defaultGroup.getName()); assertThat(groups.get("user")).containsOnly(defaultGroup.getName());
} }



+ 10
- 10
server/sonar-webserver-auth/src/test/java/org/sonar/server/user/UserUpdaterReactivateTest.java View File



public class UserUpdaterReactivateTest { public class UserUpdaterReactivateTest {


private System2 system2 = new AlwaysIncreasingSystem2();
private final System2 system2 = new AlwaysIncreasingSystem2();


@Rule @Rule
public ExpectedException expectedException = ExpectedException.none(); public ExpectedException expectedException = ExpectedException.none();
@Rule @Rule
public DbTester db = DbTester.create(system2); public DbTester db = DbTester.create(system2);


private DbClient dbClient = db.getDbClient();
private NewUserNotifier newUserNotifier = mock(NewUserNotifier.class);
private DbSession session = db.getSession();
private UserIndexer userIndexer = new UserIndexer(dbClient, es.client());
private DefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(db);
private MapSettings settings = new MapSettings();
private CredentialsLocalAuthentication localAuthentication = new CredentialsLocalAuthentication(db.getDbClient());
private UserUpdater underTest = new UserUpdater(newUserNotifier, dbClient, userIndexer, defaultOrganizationProvider,
new DefaultGroupFinder(dbClient, defaultOrganizationProvider),
private final DbClient dbClient = db.getDbClient();
private final NewUserNotifier newUserNotifier = mock(NewUserNotifier.class);
private final DbSession session = db.getSession();
private final UserIndexer userIndexer = new UserIndexer(dbClient, es.client());
private final DefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(db);
private final MapSettings settings = new MapSettings();
private final CredentialsLocalAuthentication localAuthentication = new CredentialsLocalAuthentication(db.getDbClient());
private final UserUpdater underTest = new UserUpdater(newUserNotifier, dbClient, userIndexer, defaultOrganizationProvider,
new DefaultGroupFinder(dbClient),
settings.asConfig(), localAuthentication); settings.asConfig(), localAuthentication);


@Test @Test

+ 1
- 1
server/sonar-webserver-auth/src/test/java/org/sonar/server/user/UserUpdaterUpdateTest.java View File

private MapSettings settings = new MapSettings(); private MapSettings settings = new MapSettings();
private CredentialsLocalAuthentication localAuthentication = new CredentialsLocalAuthentication(db.getDbClient()); private CredentialsLocalAuthentication localAuthentication = new CredentialsLocalAuthentication(db.getDbClient());
private UserUpdater underTest = new UserUpdater(newUserNotifier, dbClient, userIndexer, defaultOrganizationProvider, private UserUpdater underTest = new UserUpdater(newUserNotifier, dbClient, userIndexer, defaultOrganizationProvider,
new DefaultGroupFinder(dbClient, defaultOrganizationProvider), settings.asConfig(), localAuthentication);
new DefaultGroupFinder(dbClient), settings.asConfig(), localAuthentication);


@Test @Test
public void update_user() { public void update_user() {

+ 15
- 19
server/sonar-webserver-auth/src/test/java/org/sonar/server/usergroups/DefaultGroupFinderTest.java View File



import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.sonar.db.DbSession;
import org.sonar.db.DbTester; import org.sonar.db.DbTester;
import org.sonar.db.user.GroupDto; import org.sonar.db.user.GroupDto;
import org.sonar.server.organization.TestDefaultOrganizationProvider;


import static java.lang.String.format;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;


public class DefaultGroupFinderTest { public class DefaultGroupFinderTest {


@Rule
public ExpectedException expectedException = ExpectedException.none();

@Rule @Rule
public DbTester db = DbTester.create(); public DbTester db = DbTester.create();


private DefaultGroupFinder underTest = new DefaultGroupFinder(db.getDbClient(), TestDefaultOrganizationProvider.from(db));
private final DefaultGroupFinder underTest = new DefaultGroupFinder(db.getDbClient());


@Test @Test
public void find_default_group() { public void find_default_group() {
GroupDto defaultGroup = db.users().insertDefaultGroup("default");
GroupDto defaultGroup = db.users().insertDefaultGroup();


GroupDto result = underTest.findDefaultGroup(db.getSession()); GroupDto result = underTest.findDefaultGroup(db.getSession());


assertThat(result.getUuid()).isEqualTo(defaultGroup.getUuid()); assertThat(result.getUuid()).isEqualTo(defaultGroup.getUuid());
assertThat(result.getName()).isEqualTo("default");
assertThat(result.getName()).isEqualTo("sonar-users");
} }


@Test @Test
public void fail_with_ISE_when_no_default_group() { public void fail_with_ISE_when_no_default_group() {
db.users().insertGroup(); db.users().insertGroup();
DbSession session = db.getSession();


expectedException.expect(IllegalStateException.class);
expectedException.expectMessage(format("Default group cannot be found"));

underTest.findDefaultGroup(db.getSession());
assertThatThrownBy(() -> underTest.findDefaultGroup(session))
.isInstanceOf(IllegalStateException.class)
.hasMessage("Default group cannot be found");
} }


@Test @Test
public void fail_with_NPE_when_default_group_does_not_exist() {
GroupDto defaultGroup = db.users().insertDefaultGroup("default");
public void fail_with_ISE_when_default_group_does_not_exist() {
GroupDto defaultGroup = db.users().insertDefaultGroup();
db.getDbClient().groupDao().deleteByUuid(db.getSession(), defaultGroup.getUuid()); db.getDbClient().groupDao().deleteByUuid(db.getSession(), defaultGroup.getUuid());
DbSession session = db.getSession();


expectedException.expect(NullPointerException.class);
expectedException.expectMessage(format("Group '%s' cannot be found", defaultGroup.getUuid()));

underTest.findDefaultGroup(db.getSession());
assertThatThrownBy(() -> underTest.findDefaultGroup(session))
.isInstanceOf(IllegalStateException.class)
.hasMessage("Default group cannot be found");
} }
} }

+ 1
- 1
server/sonar-webserver-webapi/src/main/java/org/sonar/server/permission/ws/PermissionWsSupport.java View File

public GroupUuidOrAnyone findGroup(DbSession dbSession, Request request) { public GroupUuidOrAnyone findGroup(DbSession dbSession, Request request) {
String groupUuid = request.param(PARAM_GROUP_ID); String groupUuid = request.param(PARAM_GROUP_ID);
String groupName = request.param(PARAM_GROUP_NAME); String groupName = request.param(PARAM_GROUP_NAME);
GroupWsRef groupRef = GroupWsRef.create(groupUuid, null, groupName);
GroupWsRef groupRef = GroupWsRef.create(groupUuid, groupName);
return groupWsSupport.findGroupOrAnyone(dbSession, groupRef); return groupWsSupport.findGroupOrAnyone(dbSession, groupRef);
} }



+ 1
- 9
server/sonar-webserver-webapi/src/main/java/org/sonar/server/usergroups/ws/CreateAction.java View File

import static org.sonar.server.usergroups.ws.GroupWsSupport.DESCRIPTION_MAX_LENGTH; import static org.sonar.server.usergroups.ws.GroupWsSupport.DESCRIPTION_MAX_LENGTH;
import static org.sonar.server.usergroups.ws.GroupWsSupport.PARAM_GROUP_DESCRIPTION; import static org.sonar.server.usergroups.ws.GroupWsSupport.PARAM_GROUP_DESCRIPTION;
import static org.sonar.server.usergroups.ws.GroupWsSupport.PARAM_GROUP_NAME; import static org.sonar.server.usergroups.ws.GroupWsSupport.PARAM_GROUP_NAME;
import static org.sonar.server.usergroups.ws.GroupWsSupport.PARAM_ORGANIZATION_KEY;
import static org.sonar.server.usergroups.ws.GroupWsSupport.toProtobuf; import static org.sonar.server.usergroups.ws.GroupWsSupport.toProtobuf;
import static org.sonar.server.ws.WsUtils.writeProtobuf; import static org.sonar.server.ws.WsUtils.writeProtobuf;


.setChangelog( .setChangelog(
new Change("8.4", "Field 'id' format in the response changes from integer to string.")); new Change("8.4", "Field 'id' format in the response changes from integer to string."));


action.createParam(PARAM_ORGANIZATION_KEY)
.setDescription("Key of organization. If unset then default organization is used.")
.setExampleValue("my-org")
.setSince("6.2")
.setInternal(true);

action.createParam(PARAM_GROUP_NAME) action.createParam(PARAM_GROUP_NAME)
.setRequired(true) .setRequired(true)
.setMaximumLength(GROUP_NAME_MAX_LENGTH) .setMaximumLength(GROUP_NAME_MAX_LENGTH)
private void writeResponse(Request request, Response response, GroupDto group) { private void writeResponse(Request request, Response response, GroupDto group) {
UserGroups.CreateWsResponse.Builder respBuilder = UserGroups.CreateWsResponse.newBuilder(); UserGroups.CreateWsResponse.Builder respBuilder = UserGroups.CreateWsResponse.newBuilder();
// 'default' is always false as it's not possible to create a default group // 'default' is always false as it's not possible to create a default group
// TODO
respBuilder.setGroup(toProtobuf("org", group, 0, false));
respBuilder.setGroup(toProtobuf(group, 0, false));
writeProtobuf(respBuilder.build(), request, response); writeProtobuf(respBuilder.build(), request, response);
} }
} }

+ 14
- 34
server/sonar-webserver-webapi/src/main/java/org/sonar/server/usergroups/ws/GroupWsRef.java View File

package org.sonar.server.usergroups.ws; package org.sonar.server.usergroups.ws;


import java.util.Objects; import java.util.Objects;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import org.sonar.api.security.DefaultGroups; import org.sonar.api.security.DefaultGroups;
* of these two options: * of these two options:
* <ul> * <ul>
* <li>group uuid, for instance 1234</li> * <li>group uuid, for instance 1234</li>
* <li>group name and optional organization key</li>
* <li>group name</li>
* </ul> * </ul>
* *
* The reference is then converted to a {@link GroupUuid} or {@link GroupUuidOrAnyone}. * The reference is then converted to a {@link GroupUuid} or {@link GroupUuidOrAnyone}.
public class GroupWsRef { public class GroupWsRef {


private final String uuid; private final String uuid;
private final String organizationKey;
private final String name; private final String name;


private GroupWsRef(String uuid, @Nullable String organizationKey, @Nullable String name) {
private GroupWsRef(@Nullable String uuid, @Nullable String name) {
this.uuid = uuid; this.uuid = uuid;
this.organizationKey = organizationKey;
this.name = name; this.name = name;
} }


/** /**
* @return {@code true} if uuid is defined and {@link #getUuid()} can be called. If {@code false}, then * @return {@code true} if uuid is defined and {@link #getUuid()} can be called. If {@code false}, then
* the couple {organizationKey, name} is defined and the methods {@link #getOrganizationKey()}/{@link #getName()}
* can be called.
* the name is defined and the method {@link #getName()} can be called.
*/ */
public boolean hasUuid() { public boolean hasUuid() {
return uuid != null; return uuid != null;


/** /**
* @return the group uuid * @return the group uuid
* @throws IllegalStateException if {@link #getUuid()} is {@code false}
* @throws IllegalStateException if {@link #hasUuid()} is {@code false}
*/ */
public String getUuid() { public String getUuid() {
checkState(hasUuid(), "Id is not present. Please see hasUuid()."); checkState(hasUuid(), "Id is not present. Please see hasUuid().");
return uuid; return uuid;
} }


/**
* @return the organization key
* @throws IllegalStateException if {@link #getUuid()} is {@code true}
*/
@CheckForNull
public String getOrganizationKey() {
checkState(!hasUuid(), "Organization is not present. Please see hasId().");
return organizationKey;
}

/** /**
* @return the non-null group name. Can be anyone. * @return the non-null group name. Can be anyone.
* @throws IllegalStateException if {@link #getUuid()} is {@code true} * @throws IllegalStateException if {@link #getUuid()} is {@code true}
* as they can't be referenced by an uuid. * as they can't be referenced by an uuid.
*/ */
static GroupWsRef fromUuid(String uuid) { static GroupWsRef fromUuid(String uuid) {
return new GroupWsRef(uuid, null, null);
return new GroupWsRef(uuid, null);
} }


/** /**
* Creates a reference to a group by its organization and name. Virtual groups "Anyone" are
* Creates a reference to a group by its name. Virtual groups "Anyone" are
* supported. * supported.
* *
* @param organizationKey key of organization. If {@code null}, then default organization will be used.
* @param name non-null name. Can refer to anyone group (case-insensitive {@code "anyone"}). * @param name non-null name. Can refer to anyone group (case-insensitive {@code "anyone"}).
*/ */
static GroupWsRef fromName(@Nullable String organizationKey, String name) {
return new GroupWsRef(null, organizationKey, requireNonNull(name));
static GroupWsRef fromName(String name) {
return new GroupWsRef(null, requireNonNull(name));
} }


public static GroupWsRef create(@Nullable String uuid, @Nullable String organizationKey, @Nullable String name) {
public static GroupWsRef create(@Nullable String uuid, @Nullable String name) {
if (uuid != null) { if (uuid != null) {
checkRequest(organizationKey == null && name == null, "Either group id or couple organization/group name must be set");
checkRequest(name == null, "Either group id or group name must be set");
return fromUuid(uuid); return fromUuid(uuid);
} }


checkRequest(name != null, "Group name or group id must be provided"); checkRequest(name != null, "Group name or group id must be provided");
return fromName(organizationKey, name);
return fromName(name);
} }


public boolean isAnyone() { public boolean isAnyone() {
return false; return false;
} }
GroupWsRef that = (GroupWsRef) o; GroupWsRef that = (GroupWsRef) o;
return Objects.equals(uuid, that.uuid) && Objects.equals(organizationKey, that.organizationKey) && Objects.equals(name, that.name);
return Objects.equals(uuid, that.uuid) && Objects.equals(name, that.name);
} }


@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hash(uuid, organizationKey, name);
return Objects.hash(uuid, name);
} }


@Override @Override
public String toString() { public String toString() {
StringBuilder sb = new StringBuilder("GroupWsRef{");
sb.append("uuid=").append(uuid);
sb.append(", organizationKey='").append(organizationKey).append('\'');
sb.append(", name='").append(name).append('\'');
sb.append('}');
return sb.toString();
return "GroupWsRef{uuid=" + uuid + ", name='" + name + "'}";
} }
} }

+ 3
- 12
server/sonar-webserver-webapi/src/main/java/org/sonar/server/usergroups/ws/GroupWsSupport.java View File

public class GroupWsSupport { public class GroupWsSupport {


static final String PARAM_GROUP_ID = "id"; static final String PARAM_GROUP_ID = "id";
static final String PARAM_ORGANIZATION_KEY = "organization";
static final String PARAM_GROUP_NAME = "name"; static final String PARAM_GROUP_NAME = "name";
static final String PARAM_GROUP_DESCRIPTION = "description"; static final String PARAM_GROUP_DESCRIPTION = "description";
static final String PARAM_LOGIN = "login"; static final String PARAM_LOGIN = "login";
} }


/** /**
* Find a group by its id (parameter {@link #PARAM_GROUP_ID}) or couple organization key/group name
* (parameters {@link #PARAM_ORGANIZATION_KEY} and {@link #PARAM_GROUP_NAME}). The virtual
* Find a group by its id (parameter {@link #PARAM_GROUP_ID}) or group name (parameter {@link #PARAM_GROUP_NAME}). The virtual
* group "Anyone" is not supported. * group "Anyone" is not supported.
* *
* @throws NotFoundException if parameters are missing/incorrect, if the requested group does not exist * @throws NotFoundException if parameters are missing/incorrect, if the requested group does not exist


public GroupDto findGroupDto(DbSession dbSession, Request request) { public GroupDto findGroupDto(DbSession dbSession, Request request) {
String uuid = request.param(PARAM_GROUP_ID); String uuid = request.param(PARAM_GROUP_ID);
String organizationKey = request.param(PARAM_ORGANIZATION_KEY);
String name = request.param(PARAM_GROUP_NAME); String name = request.param(PARAM_GROUP_NAME);
return findGroupDto(dbSession, GroupWsRef.create(uuid, organizationKey, name));
return findGroupDto(dbSession, GroupWsRef.create(uuid, name));
} }


public GroupDto findGroupDto(DbSession dbSession, GroupWsRef ref) { public GroupDto findGroupDto(DbSession dbSession, GroupWsRef ref) {
checkArgument(!defaultGroup.getUuid().equals(groupDto.getUuid()), "Default group '%s' cannot be used to perform this action", groupDto.getName()); checkArgument(!defaultGroup.getUuid().equals(groupDto.getUuid()), "Default group '%s' cannot be used to perform this action", groupDto.getName());
} }


static UserGroups.Group.Builder toProtobuf(String org, GroupDto group, int membersCount, boolean isDefault) {
static UserGroups.Group.Builder toProtobuf(GroupDto group, int membersCount, boolean isDefault) {
UserGroups.Group.Builder wsGroup = UserGroups.Group.newBuilder() UserGroups.Group.Builder wsGroup = UserGroups.Group.newBuilder()
.setId(group.getUuid()) .setId(group.getUuid())
.setOrganization(org)
.setName(group.getName()) .setName(group.getName())
.setMembersCount(membersCount) .setMembersCount(membersCount)
.setDefault(isDefault); .setDefault(isDefault);
} }


private static void defineGroupNameWsParameter(WebService.NewAction action) { private static void defineGroupNameWsParameter(WebService.NewAction action) {
action.createParam(PARAM_ORGANIZATION_KEY)
.setDescription("Key of organization")
.setExampleValue("my-org")
.setInternal(true)
.setSince("6.2");
action.createParam(PARAM_GROUP_NAME) action.createParam(PARAM_GROUP_NAME)
.setDescription("Group name") .setDescription("Group name")
.setExampleValue("sonar-administrators"); .setExampleValue("sonar-administrators");

+ 1
- 9
server/sonar-webserver-webapi/src/main/java/org/sonar/server/usergroups/ws/SearchAction.java View File

import org.sonar.api.server.ws.Change; import org.sonar.api.server.ws.Change;
import org.sonar.api.server.ws.Request; import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response; import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
import org.sonar.api.server.ws.WebService.NewController; import org.sonar.api.server.ws.WebService.NewController;
import org.sonar.api.server.ws.WebService.Param; import org.sonar.api.server.ws.WebService.Param;
import org.sonar.api.utils.Paging; import org.sonar.api.utils.Paging;
import static org.sonar.api.utils.Paging.forPageIndex; import static org.sonar.api.utils.Paging.forPageIndex;
import static org.sonar.db.permission.OrganizationPermission.ADMINISTER; import static org.sonar.db.permission.OrganizationPermission.ADMINISTER;
import static org.sonar.server.es.SearchOptions.MAX_PAGE_SIZE; import static org.sonar.server.es.SearchOptions.MAX_PAGE_SIZE;
import static org.sonar.server.usergroups.ws.GroupWsSupport.PARAM_ORGANIZATION_KEY;
import static org.sonar.server.ws.WsUtils.writeProtobuf; import static org.sonar.server.ws.WsUtils.writeProtobuf;
import static org.sonarqube.ws.UserGroups.Group; import static org.sonarqube.ws.UserGroups.Group;
import static org.sonarqube.ws.UserGroups.SearchWsResponse; import static org.sonarqube.ws.UserGroups.SearchWsResponse;


@Override @Override
public void define(NewController context) { public void define(NewController context) {
WebService.NewAction action = context.createAction("search")
context.createAction("search")
.setDescription("Search for user groups.<br>" + .setDescription("Search for user groups.<br>" +
"Requires the following permission: 'Administer System'.") "Requires the following permission: 'Administer System'.")
.setHandler(this) .setHandler(this)
new Change("8.4", "Field 'id' in the response is deprecated. Format changes from integer to string."), new Change("8.4", "Field 'id' in the response is deprecated. Format changes from integer to string."),
new Change("6.4", "Paging response fields moved to a Paging object"), new Change("6.4", "Paging response fields moved to a Paging object"),
new Change("6.4", "'default' response field has been added")); new Change("6.4", "'default' response field has been added"));

action.createParam(PARAM_ORGANIZATION_KEY)
.setDescription("Key of organization. If not set then groups are searched in default organization.")
.setExampleValue("my-org")
.setSince("6.2")
.setInternal(true);
} }


@Override @Override

+ 2
- 5
server/sonar-webserver-webapi/src/main/java/org/sonar/server/usergroups/ws/UpdateAction.java View File

import org.sonar.db.DbSession; import org.sonar.db.DbSession;
import org.sonar.db.user.GroupDto; import org.sonar.db.user.GroupDto;
import org.sonar.db.user.UserMembershipQuery; import org.sonar.db.user.UserMembershipQuery;
import org.sonar.server.organization.DefaultOrganizationProvider;
import org.sonar.server.user.UserSession; import org.sonar.server.user.UserSession;
import org.sonarqube.ws.UserGroups; import org.sonarqube.ws.UserGroups;


private final DbClient dbClient; private final DbClient dbClient;
private final UserSession userSession; private final UserSession userSession;
private final GroupWsSupport support; private final GroupWsSupport support;
private final DefaultOrganizationProvider defaultOrganizationProvider;


public UpdateAction(DbClient dbClient, UserSession userSession, GroupWsSupport support, DefaultOrganizationProvider defaultOrganizationProvider) {
public UpdateAction(DbClient dbClient, UserSession userSession, GroupWsSupport support) {
this.dbClient = dbClient; this.dbClient = dbClient;
this.userSession = userSession; this.userSession = userSession;
this.support = support; this.support = support;
this.defaultOrganizationProvider = defaultOrganizationProvider;
} }


@Override @Override


UserGroups.UpdateWsResponse.Builder respBuilder = UserGroups.UpdateWsResponse.newBuilder(); UserGroups.UpdateWsResponse.Builder respBuilder = UserGroups.UpdateWsResponse.newBuilder();
// 'default' is always false as it's not possible to update a default group // 'default' is always false as it's not possible to update a default group
respBuilder.setGroup(toProtobuf(defaultOrganizationProvider.get().getKey(), group, membersCount, false));
respBuilder.setGroup(toProtobuf(group, membersCount, false));
writeProtobuf(respBuilder.build(), request, response); writeProtobuf(respBuilder.build(), request, response);
} }



+ 1
- 1
server/sonar-webserver-webapi/src/main/resources/org/sonar/server/user/ws/groups-example.json View File

{ {
"id": 2, "id": 2,
"name": "sonar-users", "name": "sonar-users",
"description": "Sonar Users",
"description": "Users",
"selected": true, "selected": true,
"default": true "default": true
} }

+ 1
- 1
server/sonar-webserver-webapi/src/main/resources/org/sonar/server/usergroups/ws/search-example.json View File

"groups": [ "groups": [
{ {
"id": "AU-Tpxb--iU5OvuD2FLy", "id": "AU-Tpxb--iU5OvuD2FLy",
"name": "users",
"name": "sonar-users",
"description": "Users", "description": "Users",
"membersCount": 17, "membersCount": 17,
"default": true "default": true

+ 1
- 1
server/sonar-webserver-webapi/src/test/java/org/sonar/server/permission/ws/BasePermissionWsTest.java View File

protected abstract A buildWsAction(); protected abstract A buildWsAction();


protected GroupWsSupport newGroupWsSupport() { protected GroupWsSupport newGroupWsSupport() {
return new GroupWsSupport(db.getDbClient(), new DefaultGroupFinder(db.getDbClient(), defaultOrganizationProvider));
return new GroupWsSupport(db.getDbClient(), new DefaultGroupFinder(db.getDbClient()));
} }


protected PermissionWsSupport newPermissionWsSupport() { protected PermissionWsSupport newPermissionWsSupport() {

+ 1
- 1
server/sonar-webserver-webapi/src/test/java/org/sonar/server/permission/ws/template/DeleteTemplateActionTest.java View File

@Before @Before
public void setUp() { public void setUp() {
DefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(db); DefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(db);
GroupWsSupport groupWsSupport = new GroupWsSupport(dbClient, new DefaultGroupFinder(db.getDbClient(), defaultOrganizationProvider));
GroupWsSupport groupWsSupport = new GroupWsSupport(dbClient, new DefaultGroupFinder(db.getDbClient()));
this.underTestWithoutViews = new WsActionTester(new DeleteTemplateAction(dbClient, userSession, this.underTestWithoutViews = new WsActionTester(new DeleteTemplateAction(dbClient, userSession,
new PermissionWsSupport(dbClient, new ComponentFinder(dbClient, resourceTypes), groupWsSupport), defaultTemplatesResolver, defaultOrganizationProvider)); new PermissionWsSupport(dbClient, new ComponentFinder(dbClient, resourceTypes), groupWsSupport), defaultTemplatesResolver, defaultOrganizationProvider));
this.underTestWithViews = new WsActionTester(new DeleteTemplateAction(dbClient, userSession, this.underTestWithViews = new WsActionTester(new DeleteTemplateAction(dbClient, userSession,

+ 2
- 4
server/sonar-webserver-webapi/src/test/java/org/sonar/server/user/ws/ChangePasswordActionTest.java View File



public class ChangePasswordActionTest { public class ChangePasswordActionTest {


private System2 system2 = new AlwaysIncreasingSystem2();

@Rule @Rule
public ExpectedException expectedException = ExpectedException.none(); public ExpectedException expectedException = ExpectedException.none();
@Rule @Rule


private UserUpdater userUpdater = new UserUpdater( private UserUpdater userUpdater = new UserUpdater(
mock(NewUserNotifier.class), db.getDbClient(), new UserIndexer(db.getDbClient(), es.client()), testDefaultOrganizationProvider, mock(NewUserNotifier.class), db.getDbClient(), new UserIndexer(db.getDbClient(), es.client()), testDefaultOrganizationProvider,
new DefaultGroupFinder(db.getDbClient(), testDefaultOrganizationProvider),
new DefaultGroupFinder(db.getDbClient()),
new MapSettings().asConfig(), new MapSettings().asConfig(),
localAuthentication); localAuthentication);




@Before @Before
public void setUp() { public void setUp() {
db.users().insertDefaultGroup("sonar-users");
db.users().insertDefaultGroup();
} }


@Test @Test

+ 2
- 3
server/sonar-webserver-webapi/src/test/java/org/sonar/server/user/ws/CreateActionTest.java View File



public class CreateActionTest { public class CreateActionTest {


private static final String DEFAULT_GROUP_NAME = "sonar-users";
private MapSettings settings = new MapSettings(); private MapSettings settings = new MapSettings();
private System2 system2 = new AlwaysIncreasingSystem2(); private System2 system2 = new AlwaysIncreasingSystem2();


private WsActionTester tester = new WsActionTester(new CreateAction( private WsActionTester tester = new WsActionTester(new CreateAction(
db.getDbClient(), db.getDbClient(),
new UserUpdater(mock(NewUserNotifier.class), db.getDbClient(), userIndexer, defaultOrganizationProvider, new UserUpdater(mock(NewUserNotifier.class), db.getDbClient(), userIndexer, defaultOrganizationProvider,
new DefaultGroupFinder(db.getDbClient(), defaultOrganizationProvider), settings.asConfig(), localAuthentication),
new DefaultGroupFinder(db.getDbClient()), settings.asConfig(), localAuthentication),
userSessionRule)); userSessionRule));


@Before @Before
public void setUp() { public void setUp() {
defaultGroup = db.users().insertDefaultGroup(DEFAULT_GROUP_NAME);
defaultGroup = db.users().insertDefaultGroup();
} }


@Test @Test

+ 16
- 28
server/sonar-webserver-webapi/src/test/java/org/sonar/server/user/ws/GroupsActionTest.java View File

import org.sonar.db.user.UserDto; import org.sonar.db.user.UserDto;
import org.sonar.server.exceptions.ForbiddenException; import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.exceptions.NotFoundException; import org.sonar.server.exceptions.NotFoundException;
import org.sonar.server.organization.TestDefaultOrganizationProvider;
import org.sonar.server.tester.UserSessionRule; import org.sonar.server.tester.UserSessionRule;
import org.sonar.server.usergroups.DefaultGroupFinder; import org.sonar.server.usergroups.DefaultGroupFinder;
import org.sonar.server.ws.TestRequest; import org.sonar.server.ws.TestRequest;
public UserSessionRule userSession = UserSessionRule.standalone().logIn().setRoot(); public UserSessionRule userSession = UserSessionRule.standalone().logIn().setRoot();


private WsActionTester ws = new WsActionTester(new GroupsAction(db.getDbClient(), userSession, private WsActionTester ws = new WsActionTester(new GroupsAction(db.getDbClient(), userSession,
new DefaultGroupFinder(db.getDbClient(), TestDefaultOrganizationProvider.from(db))));
new DefaultGroupFinder(db.getDbClient())));


@Test @Test
public void empty_groups() { public void empty_groups() {
insertUser(); insertUser();
insertDefaultGroup("sonar-users", "Sonar Users");
insertDefaultGroup();


GroupsWsResponse response = call(ws.newRequest().setParam("login", USER_LOGIN)); GroupsWsResponse response = call(ws.newRequest().setParam("login", USER_LOGIN));


@Test @Test
public void return_selected_groups_selected_param_is_set_to_all() { public void return_selected_groups_selected_param_is_set_to_all() {
UserDto user = insertUser(); UserDto user = insertUser();
GroupDto usersGroup = insertDefaultGroup("sonar-users", "Sonar Users");
GroupDto usersGroup = insertDefaultGroup();
GroupDto adminGroup = insertGroup("sonar-admins", "Sonar Admins"); GroupDto adminGroup = insertGroup("sonar-admins", "Sonar Admins");
addUserToGroup(user, usersGroup); addUserToGroup(user, usersGroup);


@Test @Test
public void return_selected_groups_selected_param_is_set_to_selected() { public void return_selected_groups_selected_param_is_set_to_selected() {
UserDto user = insertUser(); UserDto user = insertUser();
GroupDto usersGroup = insertDefaultGroup("sonar-users", "Sonar Users");
GroupDto usersGroup = insertDefaultGroup();
insertGroup("sonar-admins", "Sonar Admins"); insertGroup("sonar-admins", "Sonar Admins");
addUserToGroup(user, usersGroup); addUserToGroup(user, usersGroup);


@Test @Test
public void return_selected_groups_selected_param_is_not_set() { public void return_selected_groups_selected_param_is_not_set() {
UserDto user = insertUser(); UserDto user = insertUser();
GroupDto usersGroup = insertDefaultGroup("sonar-users", "Sonar Users");
GroupDto usersGroup = insertDefaultGroup();
insertGroup("sonar-admins", "Sonar Admins"); insertGroup("sonar-admins", "Sonar Admins");
addUserToGroup(user, usersGroup); addUserToGroup(user, usersGroup);


@Test @Test
public void return_not_selected_groups_selected_param_is_set_to_deselected() { public void return_not_selected_groups_selected_param_is_set_to_deselected() {
UserDto user = insertUser(); UserDto user = insertUser();
GroupDto usersGroup = insertDefaultGroup("sonar-users", "Sonar Users");
GroupDto usersGroup = insertDefaultGroup();
GroupDto adminGroup = insertGroup("sonar-admins", "Sonar Admins"); GroupDto adminGroup = insertGroup("sonar-admins", "Sonar Admins");
addUserToGroup(user, usersGroup); addUserToGroup(user, usersGroup);


.containsOnly(tuple(adminGroup.getUuid(), adminGroup.getName(), adminGroup.getDescription(), false)); .containsOnly(tuple(adminGroup.getUuid(), adminGroup.getName(), adminGroup.getDescription(), false));
} }


@Test
public void return_group_not_having_description() {
UserDto user = insertUser();
GroupDto group = insertDefaultGroup("sonar-users", null);
addUserToGroup(user, group);

GroupsWsResponse response = call(ws.newRequest().setParam("login", "john").setParam(Param.SELECTED, ALL.value()));

assertThat(response.getGroupsList()).extracting(GroupsWsResponse.Group::hasDescription).containsOnly(false);
}

@Test @Test
public void search_with_pagination() { public void search_with_pagination() {
UserDto user = insertUser(); UserDto user = insertUser();
insertDefaultGroup("sonar-users", "Sonar Users");
insertDefaultGroup();
for (int i = 1; i <= 9; i++) { for (int i = 1; i <= 9; i++) {
GroupDto groupDto = insertGroup("group-" + i, "group-" + i); GroupDto groupDto = insertGroup("group-" + i, "group-" + i);
addUserToGroup(user, groupDto); addUserToGroup(user, groupDto);
@Test @Test
public void search_by_text_query() { public void search_by_text_query() {
UserDto user = insertUser(); UserDto user = insertUser();
GroupDto usersGroup = insertDefaultGroup("sonar-users", "Sonar Users");
GroupDto usersGroup = insertDefaultGroup();
GroupDto adminGroup = insertGroup("sonar-admins", "Sonar Admins"); GroupDto adminGroup = insertGroup("sonar-admins", "Sonar Admins");
addUserToGroup(user, usersGroup); addUserToGroup(user, usersGroup);


@Test @Test
public void return_default_group_information() { public void return_default_group_information() {
UserDto user = insertUser(); UserDto user = insertUser();
GroupDto usersGroup = insertDefaultGroup("sonar-users", "Sonar Users");
GroupDto usersGroup = insertDefaultGroup();
GroupDto adminGroup = insertGroup("sonar-admins", "Sonar Admins"); GroupDto adminGroup = insertGroup("sonar-admins", "Sonar Admins");
addUserToGroup(user, usersGroup); addUserToGroup(user, usersGroup);


@Test @Test
public void return_groups() { public void return_groups() {
UserDto user = insertUser(); UserDto user = insertUser();
GroupDto group = db.users().insertDefaultGroup(newGroupDto().setName("group1"));
GroupDto group = db.users().insertDefaultGroup();
addUserToGroup(user, group); addUserToGroup(user, group);


GroupsWsResponse response = call(ws.newRequest() GroupsWsResponse response = call(ws.newRequest()


@Test @Test
public void fail_on_unknown_user() { public void fail_on_unknown_user() {
insertDefaultGroup("sonar-users", "Sonar Users");
insertDefaultGroup();


expectedException.expect(NotFoundException.class); expectedException.expect(NotFoundException.class);
expectedException.expectMessage("Unknown user: john"); expectedException.expectMessage("Unknown user: john");
@Test @Test
public void fail_on_disabled_user() { public void fail_on_disabled_user() {
UserDto userDto = db.users().insertUser(user -> user.setLogin("disabled").setActive(false)); UserDto userDto = db.users().insertUser(user -> user.setLogin("disabled").setActive(false));
insertDefaultGroup("sonar-users", "Sonar Users");
insertDefaultGroup();


expectedException.expect(NotFoundException.class); expectedException.expect(NotFoundException.class);
expectedException.expectMessage("Unknown user: disabled"); expectedException.expectMessage("Unknown user: disabled");
@Test @Test
public void fail_when_page_size_is_greater_than_500() { public void fail_when_page_size_is_greater_than_500() {
UserDto user = insertUser(); UserDto user = insertUser();
insertDefaultGroup("sonar-users", "Sonar Users");
insertDefaultGroup();


expectedException.expect(IllegalArgumentException.class); expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("The 'ps' parameter must be less than 500"); expectedException.expectMessage("The 'ps' parameter must be less than 500");
@Test @Test
public void test_json_example() { public void test_json_example() {
UserDto user = insertUser(); UserDto user = insertUser();
GroupDto usersGroup = insertDefaultGroup("sonar-users", "Sonar Users");
GroupDto usersGroup = insertDefaultGroup();
insertGroup("sonar-admins", "Sonar Admins"); insertGroup("sonar-admins", "Sonar Admins");
addUserToGroup(user, usersGroup); addUserToGroup(user, usersGroup);


return db.users().insertGroup(newGroupDto().setName(name).setDescription(description)); return db.users().insertGroup(newGroupDto().setName(name).setDescription(description));
} }


private GroupDto insertDefaultGroup(String name, String description) {
return db.users().insertDefaultGroup(newGroupDto().setName(name).setDescription(description));
private GroupDto insertDefaultGroup() {
return db.users().insertDefaultGroup();
} }


private void addUserToGroup(UserDto user, GroupDto usersGroup) { private void addUserToGroup(UserDto user, GroupDto usersGroup) {

+ 2
- 2
server/sonar-webserver-webapi/src/test/java/org/sonar/server/user/ws/UpdateActionTest.java View File



private WsActionTester ws = new WsActionTester(new UpdateAction( private WsActionTester ws = new WsActionTester(new UpdateAction(
new UserUpdater(mock(NewUserNotifier.class), dbClient, userIndexer, defaultOrganizationProvider, new UserUpdater(mock(NewUserNotifier.class), dbClient, userIndexer, defaultOrganizationProvider,
new DefaultGroupFinder(db.getDbClient(), defaultOrganizationProvider), settings.asConfig(), localAuthentication),
new DefaultGroupFinder(db.getDbClient()), settings.asConfig(), localAuthentication),
userSession, new UserJsonWriter(userSession), dbClient)); userSession, new UserJsonWriter(userSession), dbClient));


@Before @Before
public void setUp() { public void setUp() {
db.users().insertDefaultGroup("sonar-users");
db.users().insertDefaultGroup();
} }


@Test @Test

+ 6
- 8
server/sonar-webserver-webapi/src/test/java/org/sonar/server/usergroups/ws/AddUserActionTest.java View File

import org.sonar.db.user.UserDto; import org.sonar.db.user.UserDto;
import org.sonar.server.exceptions.NotFoundException; import org.sonar.server.exceptions.NotFoundException;
import org.sonar.server.exceptions.UnauthorizedException; import org.sonar.server.exceptions.UnauthorizedException;
import org.sonar.server.organization.TestDefaultOrganizationProvider;
import org.sonar.server.tester.UserSessionRule; import org.sonar.server.tester.UserSessionRule;
import org.sonar.server.usergroups.DefaultGroupFinder; import org.sonar.server.usergroups.DefaultGroupFinder;
import org.sonar.server.ws.TestRequest; import org.sonar.server.ws.TestRequest;
@Rule @Rule
public ExpectedException expectedException = ExpectedException.none(); public ExpectedException expectedException = ExpectedException.none();


private TestDefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(db);
private WsActionTester ws = new WsActionTester(new AddUserAction(db.getDbClient(), userSession, newGroupWsSupport()));
private final WsActionTester ws = new WsActionTester(new AddUserAction(db.getDbClient(), userSession, newGroupWsSupport()));


@Test @Test
public void verify_definition() { public void verify_definition() {
Action wsDef = ws.getDef(); Action wsDef = ws.getDef();


assertThat(wsDef.isInternal()).isEqualTo(false);
assertThat(wsDef.isInternal()).isFalse();
assertThat(wsDef.since()).isEqualTo("5.2"); assertThat(wsDef.since()).isEqualTo("5.2");
assertThat(wsDef.isPost()).isEqualTo(true);
assertThat(wsDef.isPost()).isTrue();
assertThat(wsDef.changelog()).extracting(Change::getVersion, Change::getDescription).containsOnly( assertThat(wsDef.changelog()).extracting(Change::getVersion, Change::getDescription).containsOnly(
tuple("8.4", "Parameter 'id' is deprecated. Format changes from integer to string. Use 'name' instead.")); tuple("8.4", "Parameter 'id' is deprecated. Format changes from integer to string. Use 'name' instead."));
} }
@Test @Test
public void fail_to_add_user_to_default_group() { public void fail_to_add_user_to_default_group() {
UserDto user = db.users().insertUser(); UserDto user = db.users().insertUser();
GroupDto defaultGroup = db.users().insertDefaultGroup("default");
GroupDto defaultGroup = db.users().insertDefaultGroup();
loginAsAdmin(); loginAsAdmin();


expectedException.expect(IllegalArgumentException.class); expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Default group 'default' cannot be used to perform this action");
expectedException.expectMessage("Default group 'sonar-users' cannot be used to perform this action");


newRequest() newRequest()
.setParam("id", defaultGroup.getUuid()) .setParam("id", defaultGroup.getUuid())
} }


private GroupWsSupport newGroupWsSupport() { private GroupWsSupport newGroupWsSupport() {
return new GroupWsSupport(db.getDbClient(), new DefaultGroupFinder(db.getDbClient(), defaultOrganizationProvider));
return new GroupWsSupport(db.getDbClient(), new DefaultGroupFinder(db.getDbClient()));
} }


} }

+ 4
- 11
server/sonar-webserver-webapi/src/test/java/org/sonar/server/usergroups/ws/CreateActionTest.java View File

import org.sonar.db.user.GroupDto; import org.sonar.db.user.GroupDto;
import org.sonar.server.exceptions.ForbiddenException; import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.exceptions.ServerException; import org.sonar.server.exceptions.ServerException;
import org.sonar.server.organization.DefaultOrganization;
import org.sonar.server.organization.TestDefaultOrganizationProvider;
import org.sonar.server.tester.UserSessionRule; import org.sonar.server.tester.UserSessionRule;
import org.sonar.server.usergroups.DefaultGroupFinder; import org.sonar.server.usergroups.DefaultGroupFinder;
import org.sonar.server.ws.WsActionTester; import org.sonar.server.ws.WsActionTester;
@Rule @Rule
public ExpectedException expectedException = ExpectedException.none(); public ExpectedException expectedException = ExpectedException.none();


private TestDefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(db);
private CreateAction underTest = new CreateAction(db.getDbClient(), userSession, newGroupWsSupport(), new SequenceUuidFactory());
private WsActionTester tester = new WsActionTester(underTest);
private final CreateAction underTest = new CreateAction(db.getDbClient(), userSession, newGroupWsSupport(), new SequenceUuidFactory());
private final WsActionTester tester = new WsActionTester(underTest);


@Test @Test
public void define_create_action() { public void define_create_action() {
assertThat(action.key()).isEqualTo("create"); assertThat(action.key()).isEqualTo("create");
assertThat(action.isPost()).isTrue(); assertThat(action.isPost()).isTrue();
assertThat(action.responseExampleAsString()).isNotEmpty(); assertThat(action.responseExampleAsString()).isNotEmpty();
assertThat(action.params()).hasSize(3);
assertThat(action.params()).hasSize(2);
assertThat(action.changelog()).extracting(Change::getVersion, Change::getDescription).containsOnly( assertThat(action.changelog()).extracting(Change::getVersion, Change::getDescription).containsOnly(
tuple("8.4", "Field 'id' format in the response changes from integer to string.")); tuple("8.4", "Field 'id' format in the response changes from integer to string."));
} }
} }


private GroupWsSupport newGroupWsSupport() { private GroupWsSupport newGroupWsSupport() {
return new GroupWsSupport(db.getDbClient(), new DefaultGroupFinder(db.getDbClient(), defaultOrganizationProvider));
}

private DefaultOrganization getDefaultOrganization() {
return defaultOrganizationProvider.get();
return new GroupWsSupport(db.getDbClient(), new DefaultGroupFinder(db.getDbClient()));
} }
} }

+ 13
- 15
server/sonar-webserver-webapi/src/test/java/org/sonar/server/usergroups/ws/DeleteActionTest.java View File

import org.sonar.db.user.GroupDto; import org.sonar.db.user.GroupDto;
import org.sonar.db.user.UserDto; import org.sonar.db.user.UserDto;
import org.sonar.server.exceptions.NotFoundException; import org.sonar.server.exceptions.NotFoundException;
import org.sonar.server.organization.TestDefaultOrganizationProvider;
import org.sonar.server.tester.UserSessionRule; import org.sonar.server.tester.UserSessionRule;
import org.sonar.server.usergroups.DefaultGroupFinder; import org.sonar.server.usergroups.DefaultGroupFinder;
import org.sonar.server.ws.TestRequest; import org.sonar.server.ws.TestRequest;
@Rule @Rule
public DbTester db = DbTester.create(new AlwaysIncreasingSystem2()); public DbTester db = DbTester.create(new AlwaysIncreasingSystem2());


private ComponentDbTester componentTester = new ComponentDbTester(db);
private TestDefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(db);
private WsActionTester ws = new WsActionTester(new DeleteAction(db.getDbClient(), userSession, newGroupWsSupport()));
private final ComponentDbTester componentTester = new ComponentDbTester(db);
private final WsActionTester ws = new WsActionTester(new DeleteAction(db.getDbClient(), userSession, newGroupWsSupport()));


@Test @Test
public void verify_definition() { public void verify_definition() {
Action wsDef = ws.getDef(); Action wsDef = ws.getDef();


assertThat(wsDef.isInternal()).isEqualTo(false);
assertThat(wsDef.isInternal()).isFalse();
assertThat(wsDef.since()).isEqualTo("5.2"); assertThat(wsDef.since()).isEqualTo("5.2");
assertThat(wsDef.isPost()).isEqualTo(true);
assertThat(wsDef.isPost()).isTrue();
assertThat(wsDef.changelog()).extracting(Change::getVersion, Change::getDescription).containsOnly( assertThat(wsDef.changelog()).extracting(Change::getVersion, Change::getDescription).containsOnly(
tuple("8.4", "Parameter 'id' is deprecated. Format changes from integer to string. Use 'name' instead.")); tuple("8.4", "Parameter 'id' is deprecated. Format changes from integer to string. Use 'name' instead."));
} }
.setParam("id", group.getUuid()) .setParam("id", group.getUuid())
.execute(); .execute();


assertThat(db.countRowsOfTable("groups_users")).isEqualTo(0);
assertThat(db.countRowsOfTable("groups_users")).isZero();
} }


@Test @Test
.setParam("id", group.getUuid()) .setParam("id", group.getUuid())
.execute(); .execute();


assertThat(db.countRowsOfTable("group_roles")).isEqualTo(0);
assertThat(db.countRowsOfTable("group_roles")).isZero();
} }


@Test @Test
.setParam("id", group.getUuid()) .setParam("id", group.getUuid())
.execute(); .execute();


assertThat(db.countRowsOfTable("perm_templates_groups")).isEqualTo(0);
assertThat(db.countRowsOfTable("perm_templates_groups")).isZero();
} }


@Test @Test
@Test @Test
public void fail_to_delete_default_group() { public void fail_to_delete_default_group() {
loginAsAdmin(); loginAsAdmin();
GroupDto defaultGroup = db.users().insertDefaultGroup("default");
GroupDto defaultGroup = db.users().insertDefaultGroup();


expectedException.expect(IllegalArgumentException.class); expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Default group 'default' cannot be used to perform this action");
expectedException.expectMessage("Default group 'sonar-users' cannot be used to perform this action");


newRequest() newRequest()
.setParam("id", defaultGroup.getUuid()) .setParam("id", defaultGroup.getUuid())
db.users().insertDefaultGroup(); db.users().insertDefaultGroup();
GroupDto adminGroup1 = db.users().insertGroup("admins"); GroupDto adminGroup1 = db.users().insertGroup("admins");
db.users().insertPermissionOnGroup(adminGroup1, SYSTEM_ADMIN); db.users().insertPermissionOnGroup(adminGroup1, SYSTEM_ADMIN);
GroupDto adminGroup2 = db.users().insertGroup("admins");
GroupDto adminGroup2 = db.users().insertGroup("admins2");
db.users().insertPermissionOnGroup(adminGroup2, SYSTEM_ADMIN); db.users().insertPermissionOnGroup(adminGroup2, SYSTEM_ADMIN);
UserDto bigBoss = db.users().insertUser(); UserDto bigBoss = db.users().insertUser();
db.users().insertMember(adminGroup2, bigBoss); db.users().insertMember(adminGroup2, bigBoss);
.setParam(PARAM_GROUP_ID, adminGroup1.getUuid()) .setParam(PARAM_GROUP_ID, adminGroup1.getUuid())
.execute(); .execute();
} }
private void addAdmin() { private void addAdmin() {
UserDto admin = db.users().insertUser(); UserDto admin = db.users().insertUser();
db.users().insertPermissionOnUser(admin, SYSTEM_ADMIN); db.users().insertPermissionOnUser(admin, SYSTEM_ADMIN);
} }
private void loginAsAdmin() { private void loginAsAdmin() {
userSession.logIn().addPermission(ADMINISTER); userSession.logIn().addPermission(ADMINISTER);
} }
} }


private GroupWsSupport newGroupWsSupport() { private GroupWsSupport newGroupWsSupport() {
return new GroupWsSupport(db.getDbClient(), new DefaultGroupFinder(db.getDbClient(), defaultOrganizationProvider));
return new GroupWsSupport(db.getDbClient(), new DefaultGroupFinder(db.getDbClient()));
} }


} }

+ 18
- 19
server/sonar-webserver-webapi/src/test/java/org/sonar/server/usergroups/ws/GroupWsRefTest.java View File



@Test @Test
public void test_ref_by_name() { public void test_ref_by_name() {
GroupWsRef ref = fromName("ORG1", "the-group");
GroupWsRef ref = fromName("the-group");
assertThat(ref.hasUuid()).isFalse(); assertThat(ref.hasUuid()).isFalse();
assertThat(ref.getOrganizationKey()).isEqualTo("ORG1");
assertThat(ref.getName()).isEqualTo("the-group"); assertThat(ref.getName()).isEqualTo("the-group");
assertThat(ref.isAnyone()).isFalse(); assertThat(ref.isAnyone()).isFalse();
} }
public void test_equals_and_hashCode() { public void test_equals_and_hashCode() {
GroupWsRef refId1 = GroupWsRef.fromUuid("10"); GroupWsRef refId1 = GroupWsRef.fromUuid("10");
GroupWsRef refId2 = GroupWsRef.fromUuid("11"); GroupWsRef refId2 = GroupWsRef.fromUuid("11");
assertThat(refId1.equals(refId1)).isTrue();
assertThat(refId1.equals(GroupWsRef.fromUuid("10"))).isTrue();
assertThat(refId1.hashCode()).isEqualTo(GroupWsRef.fromUuid("10").hashCode());
assertThat(refId1.equals(refId2)).isFalse();
assertThat(refId1)
.isEqualTo(refId1)
.isEqualTo(GroupWsRef.fromUuid("10"))
.hasSameHashCodeAs(GroupWsRef.fromUuid("10"))
.isNotEqualTo(refId2);


GroupWsRef refName1 = fromName("ORG1", "the-group");
GroupWsRef refName2 = fromName("ORG1", "the-group2");
GroupWsRef refName3 = fromName("ORG2", "the-group2");
assertThat(refName1.equals(refName1)).isTrue();
assertThat(refName1.equals(fromName("ORG1", "the-group"))).isTrue();
assertThat(refName1.hashCode()).isEqualTo(fromName("ORG1", "the-group").hashCode());
assertThat(refName1.equals(refName2)).isFalse();
assertThat(refName2.equals(refName3)).isFalse();
GroupWsRef refName1 = fromName("the-group");
GroupWsRef refName2 = fromName("the-group2");
GroupWsRef refName3 = fromName("the-group2");
assertThat(refName1)
.isEqualTo(refName1)
.isEqualTo(fromName("the-group"))
.hasSameHashCodeAs(fromName("the-group"))
.isNotEqualTo(refName2);
assertThat(refName2).isEqualTo(refName3);
} }


@Test @Test
public void test_toString() { public void test_toString() {
GroupWsRef refId = GroupWsRef.fromUuid("10"); GroupWsRef refId = GroupWsRef.fromUuid("10");
assertThat(refId.toString()).isEqualTo("GroupWsRef{uuid=10, organizationKey='null', name='null'}");
assertThat(refId).hasToString("GroupWsRef{uuid=10, name='null'}");
} }


@Test @Test
public void reference_anyone_by_its_name() { public void reference_anyone_by_its_name() {
GroupWsRef ref = GroupWsRef.fromName("my-org", "Anyone");
assertThat(ref.getOrganizationKey()).isEqualTo("my-org");
GroupWsRef ref = GroupWsRef.fromName("Anyone");
assertThat(ref.getName()).isEqualTo("Anyone"); assertThat(ref.getName()).isEqualTo("Anyone");
assertThat(ref.isAnyone()).isTrue(); assertThat(ref.isAnyone()).isTrue();


// case-insensitive // case-insensitive
ref = GroupWsRef.fromName("my-org", "anyone");
assertThat(ref.getOrganizationKey()).isEqualTo("my-org");
ref = GroupWsRef.fromName("anyone");
assertThat(ref.getName()).isEqualTo("anyone"); assertThat(ref.getName()).isEqualTo("anyone");
assertThat(ref.isAnyone()).isTrue(); assertThat(ref.isAnyone()).isTrue();
} }

+ 6
- 7
server/sonar-webserver-webapi/src/test/java/org/sonar/server/usergroups/ws/RemoveUserActionTest.java View File

import org.sonar.server.exceptions.BadRequestException; import org.sonar.server.exceptions.BadRequestException;
import org.sonar.server.exceptions.ForbiddenException; import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.exceptions.NotFoundException; import org.sonar.server.exceptions.NotFoundException;
import org.sonar.server.organization.TestDefaultOrganizationProvider;
import org.sonar.server.tester.UserSessionRule; import org.sonar.server.tester.UserSessionRule;
import org.sonar.server.usergroups.DefaultGroupFinder; import org.sonar.server.usergroups.DefaultGroupFinder;
import org.sonar.server.ws.TestRequest; import org.sonar.server.ws.TestRequest;
@Rule @Rule
public ExpectedException expectedException = ExpectedException.none(); public ExpectedException expectedException = ExpectedException.none();


private WsActionTester ws = new WsActionTester(
new RemoveUserAction(db.getDbClient(), userSession, new GroupWsSupport(db.getDbClient(), new DefaultGroupFinder(db.getDbClient(), TestDefaultOrganizationProvider.from(db)))));
private final WsActionTester ws = new WsActionTester(
new RemoveUserAction(db.getDbClient(), userSession, new GroupWsSupport(db.getDbClient(), new DefaultGroupFinder(db.getDbClient()))));


@Test @Test
public void verify_definition() { public void verify_definition() {
Action wsDef = ws.getDef(); Action wsDef = ws.getDef();


assertThat(wsDef.isInternal()).isEqualTo(false);
assertThat(wsDef.isInternal()).isFalse();
assertThat(wsDef.since()).isEqualTo("5.2"); assertThat(wsDef.since()).isEqualTo("5.2");
assertThat(wsDef.isPost()).isEqualTo(true);
assertThat(wsDef.isPost()).isTrue();
assertThat(wsDef.changelog()).extracting(Change::getVersion, Change::getDescription).containsOnly( assertThat(wsDef.changelog()).extracting(Change::getVersion, Change::getDescription).containsOnly(
tuple("8.4", "Parameter 'id' is deprecated. Format changes from integer to string. Use 'name' instead.")); tuple("8.4", "Parameter 'id' is deprecated. Format changes from integer to string. Use 'name' instead."));
} }
@Test @Test
public void fail_to_remove_user_from_default_group() { public void fail_to_remove_user_from_default_group() {
UserDto user = db.users().insertUser(); UserDto user = db.users().insertUser();
GroupDto defaultGroup = db.users().insertDefaultGroup("default");
GroupDto defaultGroup = db.users().insertDefaultGroup();
db.users().insertMember(defaultGroup, user); db.users().insertMember(defaultGroup, user);
loginAsAdmin(); loginAsAdmin();


expectedException.expect(IllegalArgumentException.class); expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Default group 'default' cannot be used to perform this action");
expectedException.expectMessage("Default group 'sonar-users' cannot be used to perform this action");


newRequest() newRequest()
.setParam("id", defaultGroup.getUuid()) .setParam("id", defaultGroup.getUuid())

+ 35
- 47
server/sonar-webserver-webapi/src/test/java/org/sonar/server/usergroups/ws/SearchActionTest.java View File

@Rule @Rule
public ExpectedException expectedException = ExpectedException.none(); public ExpectedException expectedException = ExpectedException.none();


private WsActionTester ws = new WsActionTester(new SearchAction(db.getDbClient(), userSession,
new DefaultGroupFinder(db.getDbClient(), TestDefaultOrganizationProvider.from(db))));
private final WsActionTester ws = new WsActionTester(new SearchAction(db.getDbClient(), userSession,
new DefaultGroupFinder(db.getDbClient())));


@Test @Test
public void define_search_action() { public void define_search_action() {
assertThat(action).isNotNull(); assertThat(action).isNotNull();
assertThat(action.key()).isEqualTo("search"); assertThat(action.key()).isEqualTo("search");
assertThat(action.responseExampleAsString()).isNotEmpty(); assertThat(action.responseExampleAsString()).isNotEmpty();
assertThat(action.params()).hasSize(5);
assertThat(action.params()).hasSize(4);
assertThat(action.changelog()).extracting(Change::getVersion, Change::getDescription).containsOnly( assertThat(action.changelog()).extracting(Change::getVersion, Change::getDescription).containsOnly(
tuple("8.4", "Field 'id' in the response is deprecated. Format changes from integer to string."), tuple("8.4", "Field 'id' in the response is deprecated. Format changes from integer to string."),
tuple("6.4", "Paging response fields moved to a Paging object"), tuple("6.4", "Paging response fields moved to a Paging object"),


@Test @Test
public void search_without_parameters() { public void search_without_parameters() {
insertDefaultGroup(db.getDefaultOrganization(), "users", 0);
insertGroup(db.getDefaultOrganization(), "admins", 0);
insertGroup(db.getDefaultOrganization(), "customer1", 0);
insertGroup(db.getDefaultOrganization(), "customer2", 0);
insertGroup(db.getDefaultOrganization(), "customer3", 0);
insertDefaultGroup(0);
insertGroup("admins", 0);
insertGroup("customer1", 0);
insertGroup("customer2", 0);
insertGroup("customer3", 0);
loginAsAdmin(); loginAsAdmin();


SearchWsResponse response = call(ws.newRequest()); SearchWsResponse response = call(ws.newRequest());
tuple("customer1", "Customer1", 0), tuple("customer1", "Customer1", 0),
tuple("customer2", "Customer2", 0), tuple("customer2", "Customer2", 0),
tuple("customer3", "Customer3", 0), tuple("customer3", "Customer3", 0),
tuple("users", "Users", 0));
tuple("sonar-users", "Users", 0));
} }


@Test @Test
public void search_with_members() { public void search_with_members() {
insertDefaultGroup(db.getDefaultOrganization(), "users", 5);
insertGroup(db.getDefaultOrganization(), "admins", 1);
insertGroup(db.getDefaultOrganization(), "customer1", 0);
insertGroup(db.getDefaultOrganization(), "customer2", 4);
insertGroup(db.getDefaultOrganization(), "customer3", 0);
insertDefaultGroup(5);
insertGroup("admins", 1);
insertGroup("customer1", 0);
insertGroup("customer2", 4);
insertGroup("customer3", 0);
loginAsAdmin(); loginAsAdmin();


SearchWsResponse response = call(ws.newRequest()); SearchWsResponse response = call(ws.newRequest());
tuple("customer1", "Customer1", 0), tuple("customer1", "Customer1", 0),
tuple("customer2", "Customer2", 4), tuple("customer2", "Customer2", 4),
tuple("customer3", "Customer3", 0), tuple("customer3", "Customer3", 0),
tuple("users", "Users", 5));
tuple("sonar-users", "Users", 5));
} }


@Test @Test
public void search_with_query() { public void search_with_query() {
insertDefaultGroup(db.getDefaultOrganization(), "users", 0);
insertGroup(db.getDefaultOrganization(), "admins", 0);
insertGroup(db.getDefaultOrganization(), "customer%_%/1", 0);
insertGroup(db.getDefaultOrganization(), "customer%_%/2", 0);
insertGroup(db.getDefaultOrganization(), "customer%_%/3", 0);
insertDefaultGroup(0);
insertGroup("admins", 0);
insertGroup("customer%_%/1", 0);
insertGroup("customer%_%/2", 0);
insertGroup("customer%_%/3", 0);
loginAsAdmin(); loginAsAdmin();


SearchWsResponse response = call(ws.newRequest().setParam(TEXT_QUERY, "tomer%_%/")); SearchWsResponse response = call(ws.newRequest().setParam(TEXT_QUERY, "tomer%_%/"));


@Test @Test
public void search_with_paging() { public void search_with_paging() {
insertDefaultGroup(db.getDefaultOrganization(), "users", 0);
insertGroup(db.getDefaultOrganization(), "admins", 0);
insertGroup(db.getDefaultOrganization(), "customer1", 0);
insertGroup(db.getDefaultOrganization(), "customer2", 0);
insertGroup(db.getDefaultOrganization(), "customer3", 0);
insertDefaultGroup(0);
insertGroup("admins", 0);
insertGroup("customer1", 0);
insertGroup("customer2", 0);
insertGroup("customer3", 0);
loginAsAdmin(); loginAsAdmin();


SearchWsResponse response = call(ws.newRequest().setParam(PAGE_SIZE, "3")); SearchWsResponse response = call(ws.newRequest().setParam(PAGE_SIZE, "3"));
assertThat(response.getPaging()).extracting(Paging::getPageIndex, Paging::getPageSize, Paging::getTotal).containsOnly(2, 3, 5); assertThat(response.getPaging()).extracting(Paging::getPageIndex, Paging::getPageSize, Paging::getTotal).containsOnly(2, 3, 5);
assertThat(response.getGroupsList()).extracting(Group::getName, Group::getDescription, Group::getMembersCount).containsOnly( assertThat(response.getGroupsList()).extracting(Group::getName, Group::getDescription, Group::getMembersCount).containsOnly(
tuple("customer3", "Customer3", 0), tuple("customer3", "Customer3", 0),
tuple("users", "Users", 0));
tuple("sonar-users", "Users", 0));


response = call(ws.newRequest().setParam(PAGE_SIZE, "3").setParam(PAGE, "3")); response = call(ws.newRequest().setParam(PAGE_SIZE, "3").setParam(PAGE, "3"));
assertThat(response.getPaging()).extracting(Paging::getPageIndex, Paging::getPageSize, Paging::getTotal).containsOnly(3, 3, 5); assertThat(response.getPaging()).extracting(Paging::getPageIndex, Paging::getPageSize, Paging::getTotal).containsOnly(3, 3, 5);


@Test @Test
public void search_with_fields() { public void search_with_fields() {
insertDefaultGroup(db.getDefaultOrganization(), "sonar-users", 0);
insertDefaultGroup(0);
loginAsAdmin(); loginAsAdmin();


assertThat(call(ws.newRequest()).getGroupsList()).extracting(Group::hasId, Group::hasName, Group::hasDescription, Group::hasMembersCount) assertThat(call(ws.newRequest()).getGroupsList()).extracting(Group::hasId, Group::hasName, Group::hasDescription, Group::hasMembersCount)


@Test @Test
public void return_default_group() { public void return_default_group() {
db.users().insertDefaultGroup("default");
db.users().insertDefaultGroup();
loginAsAdmin(); loginAsAdmin();


SearchWsResponse response = call(ws.newRequest()); SearchWsResponse response = call(ws.newRequest());


assertThat(response.getGroupsList()).extracting(Group::getName, Group::getDefault).containsOnly(tuple("default", true));
}

@Test
public void fail_when_no_default_group() {
db.users().insertGroup("users");
loginAsAdmin();

expectedException.expect(IllegalStateException.class);
expectedException.expectMessage("Default group cannot be found");

call(ws.newRequest());
assertThat(response.getGroupsList()).extracting(Group::getName, Group::getDefault).containsOnly(tuple("sonar-users", true));
} }


@Test @Test


@Test @Test
public void test_json_example() { public void test_json_example() {
insertDefaultGroup(db.getDefaultOrganization(), "users", 17);
insertGroup(db.getDefaultOrganization(), "administrators", 2);
insertDefaultGroup(17);
insertGroup("administrators", 2);
loginAsAdmin(); loginAsAdmin();


String response = ws.newRequest().setMediaType(MediaTypes.JSON).execute().getInput(); String response = ws.newRequest().setMediaType(MediaTypes.JSON).execute().getInput();
assertThat(action.isInternal()).isFalse(); assertThat(action.isInternal()).isFalse();
assertThat(action.responseExampleAsString()).isNotEmpty(); assertThat(action.responseExampleAsString()).isNotEmpty();


assertThat(action.params()).extracting(WebService.Param::key).containsOnly("p", "q", "ps", "f", "organization");
assertThat(action.params()).extracting(WebService.Param::key).containsOnly("p", "q", "ps", "f");


assertThat(action.param("f").possibleValues()).containsOnly("name", "description", "membersCount"); assertThat(action.param("f").possibleValues()).containsOnly("name", "description", "membersCount");
} }
return request.executeProtobuf(SearchWsResponse.class); return request.executeProtobuf(SearchWsResponse.class);
} }


private void insertDefaultGroup(OrganizationDto org, String name, int numberOfMembers) {
GroupDto group = newGroupDto().setName(name).setDescription(capitalize(name));
db.users().insertDefaultGroup(group);
private void insertDefaultGroup(int numberOfMembers) {
GroupDto group = db.users().insertDefaultGroup();
addMembers(group, numberOfMembers); addMembers(group, numberOfMembers);
} }


private void insertGroup(OrganizationDto org, String name, int numberOfMembers) {
private void insertGroup(String name, int numberOfMembers) {
GroupDto group = newGroupDto().setName(name).setDescription(capitalize(name)); GroupDto group = newGroupDto().setName(name).setDescription(capitalize(name));
db.users().insertGroup(group); db.users().insertGroup(group);
addMembers(group, numberOfMembers); addMembers(group, numberOfMembers);

+ 6
- 9
server/sonar-webserver-webapi/src/test/java/org/sonar/server/usergroups/ws/UpdateActionTest.java View File

import org.sonar.server.exceptions.ForbiddenException; import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.exceptions.NotFoundException; import org.sonar.server.exceptions.NotFoundException;
import org.sonar.server.exceptions.ServerException; import org.sonar.server.exceptions.ServerException;
import org.sonar.server.organization.TestDefaultOrganizationProvider;
import org.sonar.server.tester.UserSessionRule; import org.sonar.server.tester.UserSessionRule;
import org.sonar.server.usergroups.DefaultGroupFinder; import org.sonar.server.usergroups.DefaultGroupFinder;
import org.sonar.server.ws.TestRequest; import org.sonar.server.ws.TestRequest;
@Rule @Rule
public ExpectedException expectedException = ExpectedException.none(); public ExpectedException expectedException = ExpectedException.none();


private TestDefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(db);
private WsActionTester ws = new WsActionTester(
new UpdateAction(db.getDbClient(), userSession, new GroupWsSupport(db.getDbClient(), new DefaultGroupFinder(db.getDbClient(), defaultOrganizationProvider)),
defaultOrganizationProvider));
private final WsActionTester ws = new WsActionTester(
new UpdateAction(db.getDbClient(), userSession, new GroupWsSupport(db.getDbClient(), new DefaultGroupFinder(db.getDbClient()))));


@Test @Test
public void verify_definition() { public void verify_definition() {


@Test @Test
public void fail_to_update_default_group_name() { public void fail_to_update_default_group_name() {
GroupDto group = db.users().insertDefaultGroup("default");
GroupDto group = db.users().insertDefaultGroup();
loginAsAdmin(); loginAsAdmin();


expectedException.expect(IllegalArgumentException.class); expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Default group 'default' cannot be used to perform this action");
expectedException.expectMessage("Default group 'sonar-users' cannot be used to perform this action");


newRequest() newRequest()
.setParam("id", group.getUuid()) .setParam("id", group.getUuid())


@Test @Test
public void fail_to_update_default_group_description() { public void fail_to_update_default_group_description() {
GroupDto group = db.users().insertDefaultGroup("default");
GroupDto group = db.users().insertDefaultGroup();
loginAsAdmin(); loginAsAdmin();


expectedException.expect(IllegalArgumentException.class); expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Default group 'default' cannot be used to perform this action");
expectedException.expectMessage("Default group 'sonar-users' cannot be used to perform this action");


newRequest() newRequest()
.setParam("id", group.getUuid()) .setParam("id", group.getUuid())

+ 55
- 56
server/sonar-webserver-webapi/src/test/java/org/sonar/server/usergroups/ws/UsersActionTest.java View File

public DbTester db = DbTester.create(System2.INSTANCE); public DbTester db = DbTester.create(System2.INSTANCE);
@Rule @Rule
public UserSessionRule userSession = UserSessionRule.standalone(); public UserSessionRule userSession = UserSessionRule.standalone();
private TestDefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(db);
private WsActionTester ws = new WsActionTester(
new UsersAction(db.getDbClient(), userSession, new GroupWsSupport(db.getDbClient(), new DefaultGroupFinder(db.getDbClient(), defaultOrganizationProvider))));
private final WsActionTester ws = new WsActionTester(
new UsersAction(db.getDbClient(), userSession, new GroupWsSupport(db.getDbClient(), new DefaultGroupFinder(db.getDbClient()))));


@Test @Test
public void verify_definition() { public void verify_definition() {
Action wsDef = ws.getDef(); Action wsDef = ws.getDef();


assertThat(wsDef.isInternal()).isEqualTo(false);
assertThat(wsDef.isInternal()).isFalse();
assertThat(wsDef.since()).isEqualTo("5.2"); assertThat(wsDef.since()).isEqualTo("5.2");
assertThat(wsDef.isPost()).isEqualTo(false);
assertThat(wsDef.isPost()).isFalse();
assertThat(wsDef.changelog()).extracting(Change::getVersion, Change::getDescription).containsOnly( assertThat(wsDef.changelog()).extracting(Change::getVersion, Change::getDescription).containsOnly(
tuple("8.4", "Parameter 'id' is deprecated. Format changes from integer to string. Use 'name' instead.")); tuple("8.4", "Parameter 'id' is deprecated. Format changes from integer to string. Use 'name' instead."));
} }
.setParam("id", group.getUuid()) .setParam("id", group.getUuid())
.execute() .execute()
.getInput()).isSimilarTo("{\n" + .getInput()).isSimilarTo("{\n" +
" \"users\": [\n" +
" {\"login\": \"ada\", \"name\": \"Ada Lovelace\", \"selected\": true}\n" +
" ]\n" +
"}");
" \"users\": [\n" +
" {\"login\": \"ada\", \"name\": \"Ada Lovelace\", \"selected\": true}\n" +
" ]\n" +
"}");


assertJson(newUsersRequest() assertJson(newUsersRequest()
.setParam("id", group.getUuid()) .setParam("id", group.getUuid())
.setParam(Param.SELECTED, SelectionMode.SELECTED.value()) .setParam(Param.SELECTED, SelectionMode.SELECTED.value())
.execute() .execute()
.getInput()).isSimilarTo("{\n" + .getInput()).isSimilarTo("{\n" +
" \"users\": [\n" +
" {\"login\": \"ada\", \"name\": \"Ada Lovelace\", \"selected\": true}\n" +
" ]\n" +
"}");
" \"users\": [\n" +
" {\"login\": \"ada\", \"name\": \"Ada Lovelace\", \"selected\": true}\n" +
" ]\n" +
"}");
} }


@Test @Test
.setParam(Param.SELECTED, SelectionMode.ALL.value()) .setParam(Param.SELECTED, SelectionMode.ALL.value())
.execute() .execute()
.getInput()).isSimilarTo("{\n" + .getInput()).isSimilarTo("{\n" +
" \"p\": 1,\n" +
" \"ps\": 1,\n" +
" \"total\": 2,\n" +
" \"users\": [\n" +
" {\"login\": \"ada\", \"name\": \"Ada Lovelace\", \"selected\": true}\n" +
" ]\n" +
"}");
" \"p\": 1,\n" +
" \"ps\": 1,\n" +
" \"total\": 2,\n" +
" \"users\": [\n" +
" {\"login\": \"ada\", \"name\": \"Ada Lovelace\", \"selected\": true}\n" +
" ]\n" +
"}");


assertJson(newUsersRequest() assertJson(newUsersRequest()
.setParam("id", group.getUuid()) .setParam("id", group.getUuid())
.setParam(Param.SELECTED, SelectionMode.ALL.value()) .setParam(Param.SELECTED, SelectionMode.ALL.value())
.execute() .execute()
.getInput()).isSimilarTo("{\n" + .getInput()).isSimilarTo("{\n" +
" \"p\": 2,\n" +
" \"ps\": 1,\n" +
" \"total\": 2,\n" +
" \"users\": [\n" +
" {\"login\": \"grace\", \"name\": \"Grace Hopper\", \"selected\": false}\n" +
" ]\n" +
"}");
" \"p\": 2,\n" +
" \"ps\": 1,\n" +
" \"total\": 2,\n" +
" \"users\": [\n" +
" {\"login\": \"grace\", \"name\": \"Grace Hopper\", \"selected\": false}\n" +
" ]\n" +
"}");
} }


@Test @Test
.setParam(Param.SELECTED, SelectionMode.ALL.value()) .setParam(Param.SELECTED, SelectionMode.ALL.value())
.execute() .execute()
.getInput()).isSimilarTo("{\n" + .getInput()).isSimilarTo("{\n" +
" \"users\": [\n" +
" {\"login\": \"ada.login\", \"name\": \"Ada Lovelace\", \"selected\": true},\n" +
" {\"login\": \"grace\", \"name\": \"Grace Hopper\", \"selected\": false}\n" +
" ]\n" +
"}\n");
" \"users\": [\n" +
" {\"login\": \"ada.login\", \"name\": \"Ada Lovelace\", \"selected\": true},\n" +
" {\"login\": \"grace\", \"name\": \"Grace Hopper\", \"selected\": false}\n" +
" ]\n" +
"}\n");


assertJson(newUsersRequest().setParam("id", group.getUuid()) assertJson(newUsersRequest().setParam("id", group.getUuid())
.setParam("q", ".logi") .setParam("q", ".logi")
.execute() .execute()
.getInput()).isSimilarTo("{\n" + .getInput()).isSimilarTo("{\n" +
" \"users\": [\n" +
" {\n" +
" \"login\": \"ada.login\",\n" +
" \"name\": \"Ada Lovelace\",\n" +
" \"selected\": true\n" +
" }\n" +
" ]\n" +
"}\n");
" \"users\": [\n" +
" {\n" +
" \"login\": \"ada.login\",\n" +
" \"name\": \"Ada Lovelace\",\n" +
" \"selected\": true\n" +
" }\n" +
" ]\n" +
"}\n");


assertJson(newUsersRequest().setParam("id", group.getUuid()) assertJson(newUsersRequest().setParam("id", group.getUuid())
.setParam("q", "OvE") .setParam("q", "OvE")
.execute() .execute()
.getInput()).isSimilarTo("{\n" + .getInput()).isSimilarTo("{\n" +
" \"users\": [\n" +
" {\n" +
" \"login\": \"ada.login\",\n" +
" \"name\": \"Ada Lovelace\",\n" +
" \"selected\": true\n" +
" }\n" +
" ]\n" +
"}\n");
" \"users\": [\n" +
" {\n" +
" \"login\": \"ada.login\",\n" +
" \"name\": \"Ada Lovelace\",\n" +
" \"selected\": true\n" +
" }\n" +
" ]\n" +
"}\n");


assertJson(newUsersRequest().setParam("id", group.getUuid()) assertJson(newUsersRequest().setParam("id", group.getUuid())
.setParam("q", "mail") .setParam("q", "mail")
.execute() .execute()
.getInput()).isSimilarTo("{\n" + .getInput()).isSimilarTo("{\n" +
" \"users\": [\n" +
" {\n" +
" \"login\": \"ada.login\",\n" +
" \"name\": \"Ada Lovelace\",\n" +
" \"selected\": true\n" +
" }\n" +
" ]\n" +
"}\n");
" \"users\": [\n" +
" {\n" +
" \"login\": \"ada.login\",\n" +
" \"name\": \"Ada Lovelace\",\n" +
" \"selected\": true\n" +
" }\n" +
" ]\n" +
"}\n");
} }


@Test @Test

+ 1
- 1
sonar-ws/src/main/protobuf/ws-user_groups.proto View File

} }


message Group { message Group {
reserved 2;
optional string id = 1; optional string id = 1;
optional string organization = 2;
optional string name = 3; optional string name = 3;
optional string description = 4; optional string description = 4;
optional int32 membersCount = 5; optional int32 membersCount = 5;

Loading…
Cancel
Save