improve test code coverage
This commit is contained in:
parent
2531b9071f
commit
1a18ba4a3d
22
build.gradle
22
build.gradle
@ -156,11 +156,8 @@ jacocoTestReport {
|
||||
classDirectories.setFrom(files(classDirectories.files.collect {
|
||||
fileTree(dir: it, exclude: [
|
||||
"net/hostsharing/hsadminng/generated/**/*.class",
|
||||
|
||||
// TODO: improve test code coverage for these classes:
|
||||
"net/hostsharing/hsadminng/rbac/rbacuser/UserController.class",
|
||||
"net/hostsharing/hsadminng/rbac/rbacgrant/GrantController.class",
|
||||
"net/hostsharing/hsadminng/hs/hscustomer/CustomerController.class"
|
||||
"net/hostsharing/hsadminng/TestController.class",
|
||||
"net/hostsharing/hsadminng/hs/hscustomer/HsadminNgApplication.class"
|
||||
])
|
||||
}))
|
||||
}
|
||||
@ -174,7 +171,7 @@ jacocoTestCoverageVerification {
|
||||
rule {
|
||||
excludes = ['net.hostsharing.hsadminng.generated.**']
|
||||
limit {
|
||||
minimum = 0.7 // TODO: increase to 0.9
|
||||
minimum = 0.8 // TODO: increase to 0.9
|
||||
}
|
||||
}
|
||||
|
||||
@ -190,14 +187,13 @@ jacocoTestCoverageVerification {
|
||||
'net.hostsharing.hsadminng.TestController',
|
||||
|
||||
// TODO: improve test code coverage:
|
||||
'net.hostsharing.hsadminng.rbac.rbacuser.UserController',
|
||||
'net.hostsharing.hsadminng.hs.hscustomer.CustomerController'
|
||||
'net.hostsharing.hsadminng.Mapper',
|
||||
]
|
||||
|
||||
limit {
|
||||
counter = 'LINE'
|
||||
value = 'COVEREDRATIO'
|
||||
minimum = 0.7
|
||||
minimum = 0.95
|
||||
}
|
||||
}
|
||||
rule {
|
||||
@ -205,13 +201,7 @@ jacocoTestCoverageVerification {
|
||||
excludes = [
|
||||
'net.hostsharing.hsadminng.generated.**',
|
||||
'net.hostsharing.hsadminng.HsadminNgApplication.*',
|
||||
|
||||
// TODO: improve test code coverage:
|
||||
'net.hostsharing.hsadminng.rbac.rbacuser.RbacUserController.listUsers(*)',
|
||||
'net.hostsharing.hsadminng.rbac.rbacuser.RbacUserController.listUserPermissions(*)',
|
||||
'net.hostsharing.hsadminng.rbac.rbacgrant.RbacGrantController.listUserGrants(*)',
|
||||
'net.hostsharing.hsadminng.hs.hscustomer.CustomerController.addCustomer(java.lang.String, java.lang.String, net.hostsharing.hsadminng.generated.api.v1.model.CustomerResource)'
|
||||
]
|
||||
'net.hostsharing.hsadminng.TestController.*']
|
||||
|
||||
limit {
|
||||
counter = 'BRANCH'
|
||||
|
@ -1,3 +1,11 @@
|
||||
|
||||
# Spring BOM overrides
|
||||
postgresql.version = 42.4.1
|
||||
|
||||
# TODO: can be removed if all dependencies are JDK 16 compliant
|
||||
#org.gradle.jvmargs= \
|
||||
# --add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED \
|
||||
# --add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED \
|
||||
# --add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED \
|
||||
# --add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED \
|
||||
# --add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED
|
||||
|
@ -3,6 +3,7 @@ package net.hostsharing.hsadminng.hs.hscustomer;
|
||||
import net.hostsharing.hsadminng.context.Context;
|
||||
import net.hostsharing.hsadminng.generated.api.v1.api.CustomersApi;
|
||||
import net.hostsharing.hsadminng.generated.api.v1.model.CustomerResource;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@ -33,7 +34,7 @@ public class CustomerController implements CustomersApi {
|
||||
String prefix
|
||||
) {
|
||||
context.setCurrentUser(userName);
|
||||
if (assumedRoles != null && !assumedRoles.isBlank()) {
|
||||
if (!StringUtils.isBlank(assumedRoles)) {
|
||||
context.assumeRoles(assumedRoles);
|
||||
}
|
||||
|
||||
@ -51,7 +52,7 @@ public class CustomerController implements CustomersApi {
|
||||
|
||||
context.setCurrentTask("create new customer: #" + customer.getReference() + " / " + customer.getPrefix());
|
||||
context.setCurrentUser(currentUser);
|
||||
if (assumedRoles != null && !assumedRoles.isBlank()) {
|
||||
if (!StringUtils.isBlank(assumedRoles)) {
|
||||
context.assumeRoles(assumedRoles);
|
||||
}
|
||||
if (customer.getUuid() == null) {
|
||||
@ -62,7 +63,7 @@ public class CustomerController implements CustomersApi {
|
||||
|
||||
final var uri =
|
||||
MvcUriComponentsBuilder.fromController(getClass())
|
||||
.path("/api/rbac-users/{id}")
|
||||
.path("/api/customers/{id}")
|
||||
.buildAndExpand(customer.getUuid())
|
||||
.toUri();
|
||||
return ResponseEntity.created(uri).body(map(saved, CustomerResource.class));
|
||||
|
@ -5,6 +5,7 @@ import net.hostsharing.hsadminng.context.Context;
|
||||
import net.hostsharing.hsadminng.generated.api.v1.api.PackagesApi;
|
||||
import net.hostsharing.hsadminng.generated.api.v1.model.PackageResource;
|
||||
import net.hostsharing.hsadminng.generated.api.v1.model.PackageUpdateResource;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@ -33,7 +34,7 @@ public class PackageController implements PackagesApi {
|
||||
String name
|
||||
) {
|
||||
context.setCurrentUser(userName);
|
||||
if (assumedRoles != null && !assumedRoles.isBlank()) {
|
||||
if (!StringUtils.isBlank(assumedRoles)) {
|
||||
context.assumeRoles(assumedRoles);
|
||||
}
|
||||
final var result = packageRepository.findAllByOptionalNameLike(name);
|
||||
@ -49,7 +50,7 @@ public class PackageController implements PackagesApi {
|
||||
final PackageUpdateResource body) {
|
||||
|
||||
context.setCurrentUser(currentUser);
|
||||
if (assumedRoles != null && !assumedRoles.isBlank()) {
|
||||
if (!StringUtils.isBlank(assumedRoles)) {
|
||||
context.assumeRoles(assumedRoles);
|
||||
}
|
||||
final var current = packageRepository.findByUuid(packageUuid);
|
||||
|
@ -3,6 +3,7 @@ package net.hostsharing.hsadminng.rbac.rbacgrant;
|
||||
import net.hostsharing.hsadminng.context.Context;
|
||||
import net.hostsharing.hsadminng.generated.api.v1.api.RbacgrantsApi;
|
||||
import net.hostsharing.hsadminng.generated.api.v1.model.RbacGrantResource;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@ -38,7 +39,7 @@ public class RbacGrantController implements RbacgrantsApi {
|
||||
final UUID granteeUserUuid) {
|
||||
|
||||
context.setCurrentUser(currentUser);
|
||||
if (assumedRoles != null && !assumedRoles.isBlank()) {
|
||||
if (!StringUtils.isBlank(assumedRoles)) {
|
||||
context.assumeRoles(assumedRoles);
|
||||
}
|
||||
|
||||
@ -57,7 +58,7 @@ public class RbacGrantController implements RbacgrantsApi {
|
||||
final String assumedRoles) {
|
||||
|
||||
context.setCurrentUser(currentUser);
|
||||
if (assumedRoles != null && !assumedRoles.isBlank()) {
|
||||
if (!StringUtils.isBlank(assumedRoles)) {
|
||||
context.assumeRoles(assumedRoles);
|
||||
}
|
||||
return ResponseEntity.ok(mapList(rbacGrantRepository.findAll(), RbacGrantResource.class));
|
||||
@ -72,7 +73,7 @@ public class RbacGrantController implements RbacgrantsApi {
|
||||
|
||||
context.setCurrentTask("granting role to user");
|
||||
context.setCurrentUser(currentUser);
|
||||
if (assumedRoles != null && !assumedRoles.isBlank()) {
|
||||
if (!StringUtils.isBlank(assumedRoles)) {
|
||||
context.assumeRoles(assumedRoles);
|
||||
}
|
||||
|
||||
@ -98,7 +99,7 @@ public class RbacGrantController implements RbacgrantsApi {
|
||||
|
||||
context.setCurrentTask("revoking role from user");
|
||||
context.setCurrentUser(currentUser);
|
||||
if (assumedRoles != null && !assumedRoles.isBlank()) {
|
||||
if (!StringUtils.isBlank(assumedRoles)) {
|
||||
context.assumeRoles(assumedRoles);
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,7 @@ package net.hostsharing.hsadminng.rbac.rbacrole;
|
||||
import net.hostsharing.hsadminng.context.Context;
|
||||
import net.hostsharing.hsadminng.generated.api.v1.api.RbacrolesApi;
|
||||
import net.hostsharing.hsadminng.generated.api.v1.model.RbacRoleResource;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@ -29,7 +30,7 @@ public class RbacRoleController implements RbacrolesApi {
|
||||
final String assumedRoles) {
|
||||
|
||||
context.setCurrentUser(currentUser);
|
||||
if (assumedRoles != null && !assumedRoles.isBlank()) {
|
||||
if (!StringUtils.isBlank(assumedRoles)) {
|
||||
context.assumeRoles(assumedRoles);
|
||||
}
|
||||
return ResponseEntity.ok(mapList(rbacRoleRepository.findAll(), RbacRoleResource.class));
|
||||
|
@ -2,8 +2,11 @@ package net.hostsharing.hsadminng.rbac.rbacuser;
|
||||
|
||||
import net.hostsharing.hsadminng.context.Context;
|
||||
import net.hostsharing.hsadminng.generated.api.v1.api.RbacusersApi;
|
||||
import net.hostsharing.hsadminng.generated.api.v1.model.RbacGrantResource;
|
||||
import net.hostsharing.hsadminng.generated.api.v1.model.RbacUserPermissionResource;
|
||||
import net.hostsharing.hsadminng.generated.api.v1.model.RbacUserResource;
|
||||
import net.hostsharing.hsadminng.rbac.rbacgrant.RbacGrantId;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@ -28,7 +31,7 @@ public class RbacUserController implements RbacusersApi {
|
||||
@Override
|
||||
@Transactional
|
||||
public ResponseEntity<RbacUserResource> createUser(
|
||||
@RequestBody final RbacUserResource body
|
||||
final RbacUserResource body
|
||||
) {
|
||||
context.setCurrentTask("creating new user: " + body.getName());
|
||||
context.setCurrentUser(body.getName());
|
||||
@ -47,22 +50,33 @@ public class RbacUserController implements RbacusersApi {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<List<RbacUserPermissionResource>> getUserById(
|
||||
@Transactional(readOnly = true)
|
||||
public ResponseEntity<RbacUserResource> getUserById(
|
||||
final String currentUser,
|
||||
final String assumedRoles,
|
||||
final String userName) {
|
||||
return null; // TODO implement getUserById
|
||||
final UUID userUuid) {
|
||||
|
||||
context.setCurrentUser(currentUser);
|
||||
if (!StringUtils.isBlank(assumedRoles)) {
|
||||
context.assumeRoles(assumedRoles);
|
||||
}
|
||||
|
||||
final var result = rbacUserRepository.findByUuid(userUuid);
|
||||
if (result == null) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
return ResponseEntity.ok(map(result, RbacUserResource.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public ResponseEntity<List<RbacUserResource>> listUsers(
|
||||
@RequestHeader(name = "current-user") final String currentUserName,
|
||||
@RequestHeader(name = "assumed-roles", required = false) final String assumedRoles,
|
||||
@RequestParam(name = "name", required = false) final String userName
|
||||
final String currentUserName,
|
||||
final String assumedRoles,
|
||||
final String userName
|
||||
) {
|
||||
context.setCurrentUser(currentUserName);
|
||||
if (assumedRoles != null && !assumedRoles.isBlank()) {
|
||||
if (!StringUtils.isBlank(assumedRoles)) {
|
||||
context.assumeRoles(assumedRoles);
|
||||
}
|
||||
return ResponseEntity.ok(mapList(rbacUserRepository.findByOptionalNameLike(userName), RbacUserResource.class));
|
||||
@ -71,14 +85,14 @@ public class RbacUserController implements RbacusersApi {
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public ResponseEntity<List<RbacUserPermissionResource>> listUserPermissions(
|
||||
@RequestHeader(name = "current-user") final String currentUserName,
|
||||
@RequestHeader(name = "assumed-roles", required = false) final String assumedRoles,
|
||||
@PathVariable(name = "userName") final String userName
|
||||
final String currentUserName,
|
||||
final String assumedRoles,
|
||||
final UUID userUuid
|
||||
) {
|
||||
context.setCurrentUser(currentUserName);
|
||||
if (assumedRoles != null && !assumedRoles.isBlank()) {
|
||||
if (!StringUtils.isBlank(assumedRoles)) {
|
||||
context.assumeRoles(assumedRoles);
|
||||
}
|
||||
return ResponseEntity.ok(mapList(rbacUserRepository.findPermissionsOfUser(userName), RbacUserPermissionResource.class));
|
||||
return ResponseEntity.ok(mapList(rbacUserRepository.findPermissionsOfUserByUuid(userUuid), RbacUserPermissionResource.class));
|
||||
}
|
||||
}
|
||||
|
@ -22,8 +22,8 @@ public interface RbacUserRepository extends Repository<RbacUserEntity, UUID> {
|
||||
|
||||
RbacUserEntity findByUuid(UUID uuid);
|
||||
|
||||
@Query(value = "select * from grantedPermissions(:userName)", nativeQuery = true)
|
||||
List<RbacUserPermission> findPermissionsOfUser(String userName);
|
||||
@Query(value = "select * from grantedPermissions(:userUuid)", nativeQuery = true)
|
||||
List<RbacUserPermission> findPermissionsOfUserByUuid(UUID userUuid);
|
||||
|
||||
/*
|
||||
Can't use save/saveAndFlush from SpringData because the uuid is not generated on the entity level,
|
||||
|
@ -13,12 +13,12 @@ paths:
|
||||
/api/rbac-users:
|
||||
$ref: "./api-definition/rbac-users.yaml"
|
||||
|
||||
/api/rbac-users/{userUuid}:
|
||||
$ref: "./api-definition/rbac-users-with-id.yaml"
|
||||
|
||||
/api/rbac-users/{userUuid}/permissions:
|
||||
$ref: "./api-definition/rbac-users-with-id-permissions.yaml"
|
||||
|
||||
/api/rbac-users/{userUuid}:
|
||||
$ref: "./api-definition/rbac-users-with-id.yaml"
|
||||
|
||||
/api/rbac-roles:
|
||||
$ref: "./api-definition/rbac-roles.yaml"
|
||||
|
||||
|
@ -6,11 +6,12 @@ get:
|
||||
parameters:
|
||||
- $ref: './api-definition/auth.yaml#/components/parameters/currentUser'
|
||||
- $ref: './api-definition/auth.yaml#/components/parameters/assumedRoles'
|
||||
- name: userName
|
||||
- name: userUuid
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
|
@ -6,20 +6,19 @@ get:
|
||||
parameters:
|
||||
- $ref: './api-definition/auth.yaml#/components/parameters/currentUser'
|
||||
- $ref: './api-definition/auth.yaml#/components/parameters/assumedRoles'
|
||||
- name: userName
|
||||
- name: userUuid
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
content:
|
||||
'application/json':
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: './api-definition/rbac-user-schemas.yaml#/components/schemas/RbacUserPermission'
|
||||
$ref: './api-definition/rbac-user-schemas.yaml#/components/schemas/RbacUser'
|
||||
|
||||
"401":
|
||||
$ref: './api-definition/error-responses.yaml#/components/responses/Unauthorized'
|
||||
|
@ -205,24 +205,18 @@ grant all privileges on RbacOwnGrantedPermissions_rv to restricted;
|
||||
|
||||
|
||||
*/
|
||||
create or replace function grantedPermissions(userName varchar)
|
||||
create or replace function grantedPermissions(targetUserUuid uuid)
|
||||
returns table(roleUuid uuid, roleName text, permissionUuid uuid, op RbacOp, objectTable varchar, objectIdName varchar, objectUuid uuid)
|
||||
returns null on null input
|
||||
language plpgsql as $$
|
||||
declare
|
||||
targetUserId uuid;
|
||||
currentUserId uuid;
|
||||
begin
|
||||
-- @formatter:off
|
||||
if cardinality(assumedRoles()) > 0 then
|
||||
raise exception '[400] grantedPermissions(...) does not support assumed roles';
|
||||
end if;
|
||||
|
||||
targetUserId := findRbacUserId(userName);
|
||||
currentUserId := currentUserId();
|
||||
|
||||
if hasGlobalRoleGranted(targetUserId) and not hasGlobalRoleGranted(currentUserId) then
|
||||
raise exception '[403] permissions of user "%" are not accessible to user "%"', userName, currentUser();
|
||||
if hasGlobalRoleGranted(targetUserUuid) and not hasGlobalRoleGranted(currentUserId) then
|
||||
raise exception '[403] permissions of user "%" are not accessible to user "%"', targetUserUuid, currentUser();
|
||||
end if;
|
||||
|
||||
return query select
|
||||
@ -235,12 +229,12 @@ begin
|
||||
p.uuid as permissionUuid, p.op, po.objecttable as permissionObjectTable,
|
||||
findIdNameByObjectUuid(po.objectTable, po.uuid) as permissionObjectIdName,
|
||||
po.uuid as permissionObjectUuid
|
||||
from queryPermissionsGrantedToSubjectId( targetUserId) as p
|
||||
from queryPermissionsGrantedToSubjectId( targetUserUuid) as p
|
||||
join rbacgrants as g on g.descendantUuid = p.uuid
|
||||
join rbacobject as po on po.uuid = p.objectUuid
|
||||
join rbacrole_rv as r on r.uuid = g.ascendantUuid
|
||||
join rbacobject as ro on ro.uuid = r.objectUuid
|
||||
where isGranted(targetUserId, r.uuid)
|
||||
where isGranted(targetUserUuid, r.uuid)
|
||||
) xp;
|
||||
-- @formatter:on
|
||||
end; $$;
|
||||
|
@ -141,6 +141,36 @@ class CustomerControllerAcceptanceTest {
|
||||
.hasValueSatisfying(c -> assertThat(c.getPrefix()).isEqualTo("vvv"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void hostsharingAdmin_withAssumedCustomerAdminRole_canNotCreateCustomer() throws Exception {
|
||||
|
||||
RestAssured // @formatter:off
|
||||
.given()
|
||||
.header("current-user", "mike@hostsharing.net")
|
||||
.header("assumed-roles", "customer#xxx.admin")
|
||||
.contentType(ContentType.JSON)
|
||||
.body("""
|
||||
{
|
||||
"reference": 90010,
|
||||
"prefix": "uuu",
|
||||
"adminUserName": "customer-admin@uuu.example.com"
|
||||
}
|
||||
""")
|
||||
.port(port)
|
||||
.when()
|
||||
.post("http://localhost/api/customers")
|
||||
.then().assertThat()
|
||||
.statusCode(403)
|
||||
.contentType(ContentType.JSON)
|
||||
.statusCode(403)
|
||||
.body("message", containsString("add-customer not permitted for customer#xxx.admin"));
|
||||
// @formatter:on
|
||||
|
||||
// finally, the new customer was not created
|
||||
context.setCurrentUser("sven@hostsharing.net");
|
||||
assertThat(customerRepository.findCustomerByOptionalPrefixLike("uuu")).hasSize(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void customerAdmin_withoutAssumedRole_canNotCreateCustomer() throws Exception {
|
||||
|
||||
|
@ -28,6 +28,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assumptions.assumeThat;
|
||||
import static org.hamcrest.CoreMatchers.containsString;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
|
||||
@SpringBootTest(
|
||||
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
@ -55,6 +56,108 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
|
||||
@Autowired
|
||||
JpaAttempt jpaAttempt;
|
||||
|
||||
@Nested
|
||||
class ListGrants {
|
||||
|
||||
@Test
|
||||
@Accepts("GRT:L(List)")
|
||||
void hostsharingAdmin_withoutAssumedRole_canViewAllGrants() {
|
||||
RestAssured // @formatter:off
|
||||
.given()
|
||||
.header("current-user", "mike@hostsharing.net")
|
||||
.port(port)
|
||||
.when()
|
||||
.get("http://localhost/api/rbac-grants")
|
||||
.then().log().all().assertThat()
|
||||
.statusCode(200)
|
||||
.contentType("application/json")
|
||||
.body("", hasItem(
|
||||
allOf(
|
||||
hasEntry("grantedByRoleIdName", "global#hostsharing.admin"),
|
||||
hasEntry("grantedRoleIdName", "customer#xxx.admin"),
|
||||
hasEntry("granteeUserName", "customer-admin@xxx.example.com")
|
||||
)
|
||||
))
|
||||
.body("", hasItem(
|
||||
allOf(
|
||||
hasEntry("grantedByRoleIdName", "global#hostsharing.admin"),
|
||||
hasEntry("grantedRoleIdName", "customer#yyy.admin"),
|
||||
hasEntry("granteeUserName", "customer-admin@yyy.example.com")
|
||||
)
|
||||
))
|
||||
.body("", hasItem(
|
||||
allOf(
|
||||
hasEntry("grantedByRoleIdName", "global#hostsharing.admin"),
|
||||
hasEntry("grantedRoleIdName", "global#hostsharing.admin"),
|
||||
hasEntry("granteeUserName", "sven@hostsharing.net")
|
||||
)
|
||||
))
|
||||
.body("", hasItem(
|
||||
allOf(
|
||||
hasEntry("grantedByRoleIdName", "customer#xxx.admin"),
|
||||
hasEntry("grantedRoleIdName", "package#xxx00.admin"),
|
||||
hasEntry("granteeUserName", "pac-admin-xxx00@xxx.example.com")
|
||||
)
|
||||
))
|
||||
.body("", hasItem(
|
||||
allOf(
|
||||
hasEntry("grantedByRoleIdName", "customer#zzz.admin"),
|
||||
hasEntry("grantedRoleIdName", "package#zzz02.admin"),
|
||||
hasEntry("granteeUserName", "pac-admin-zzz02@zzz.example.com")
|
||||
)
|
||||
))
|
||||
.body("size()", greaterThanOrEqualTo(14));
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Test
|
||||
@Accepts({ "GRT:L(List)", "GRT:X(Access Control)" })
|
||||
void hostsharingAdmin_withAssumedPackageAdminRole_canViewPacketRelatedGrants() {
|
||||
RestAssured // @formatter:off
|
||||
.given()
|
||||
.header("current-user", "mike@hostsharing.net")
|
||||
.header("assumed-roles", "package#yyy00.admin")
|
||||
.port(port)
|
||||
.when()
|
||||
.get("http://localhost/api/rbac-grants")
|
||||
.then().log().all().assertThat()
|
||||
.statusCode(200)
|
||||
.contentType("application/json")
|
||||
.body("", hasItem(
|
||||
allOf(
|
||||
hasEntry("grantedByRoleIdName", "customer#yyy.admin"),
|
||||
hasEntry("grantedRoleIdName", "package#yyy00.admin"),
|
||||
hasEntry("granteeUserName", "pac-admin-yyy00@yyy.example.com")
|
||||
)
|
||||
))
|
||||
.body("size()", is(1));
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Test
|
||||
@Accepts({ "GRT:L(List)", "GRT:X(Access Control)" })
|
||||
void packageAdmin_withoutAssumedRole_canViewPacketRelatedGrants() {
|
||||
RestAssured // @formatter:off
|
||||
.given()
|
||||
.header("current-user", "pac-admin-yyy00@yyy.example.com")
|
||||
.port(port)
|
||||
.when()
|
||||
.get("http://localhost/api/rbac-grants")
|
||||
.then().log().all().assertThat()
|
||||
.statusCode(200)
|
||||
.contentType("application/json")
|
||||
.body("", hasItem(
|
||||
allOf(
|
||||
hasEntry("grantedByRoleIdName", "customer#yyy.admin"),
|
||||
hasEntry("grantedRoleIdName", "package#yyy00.admin"),
|
||||
hasEntry("granteeUserName", "pac-admin-yyy00@yyy.example.com")
|
||||
)
|
||||
))
|
||||
.body("size()", is(1));
|
||||
// @formatter:on
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class GetGrantById {
|
||||
|
||||
|
@ -2,8 +2,10 @@ package net.hostsharing.hsadminng.rbac.rbacuser;
|
||||
|
||||
import io.restassured.RestAssured;
|
||||
import io.restassured.http.ContentType;
|
||||
import net.hostsharing.hsadminng.Accepts;
|
||||
import net.hostsharing.hsadminng.HsadminNgApplication;
|
||||
import net.hostsharing.hsadminng.context.Context;
|
||||
import net.hostsharing.test.JpaAttempt;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@ -19,7 +21,7 @@ import static org.hamcrest.Matchers.*;
|
||||
|
||||
@SpringBootTest(
|
||||
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
classes = HsadminNgApplication.class
|
||||
classes = { HsadminNgApplication.class, JpaAttempt.class }
|
||||
)
|
||||
@Transactional
|
||||
class RbacUserControllerAcceptanceTest {
|
||||
@ -30,6 +32,9 @@ class RbacUserControllerAcceptanceTest {
|
||||
@Autowired
|
||||
EntityManager em;
|
||||
|
||||
@Autowired
|
||||
JpaAttempt jpaAttempt;
|
||||
|
||||
@Autowired
|
||||
Context context;
|
||||
|
||||
@ -37,9 +42,125 @@ class RbacUserControllerAcceptanceTest {
|
||||
RbacUserRepository rbacUserRepository;
|
||||
|
||||
@Nested
|
||||
class ApiRbacUsersGet {
|
||||
class CreateRbacUser {
|
||||
|
||||
@Test
|
||||
@Accepts({ "USR:C(Create)", "USR:X(Access Control)" })
|
||||
void anybody_canCreateANewUser() {
|
||||
|
||||
// @formatter:off
|
||||
final var location = RestAssured
|
||||
.given()
|
||||
.contentType(ContentType.JSON)
|
||||
.body("""
|
||||
{
|
||||
"name": "new-user@example.com"
|
||||
}
|
||||
""")
|
||||
.port(port)
|
||||
.when()
|
||||
.post("http://localhost/api/rbac-users")
|
||||
.then().assertThat()
|
||||
.statusCode(201)
|
||||
.contentType(ContentType.JSON)
|
||||
.body("name", is("new-user@example.com"))
|
||||
.header("Location", startsWith("http://localhost"))
|
||||
.extract().header("Location");
|
||||
// @formatter:on
|
||||
|
||||
// finally, the user can view its own record
|
||||
final var newUserUuid = UUID.fromString(
|
||||
location.substring(location.lastIndexOf('/') + 1));
|
||||
context.setCurrentUser("new-user@example.com");
|
||||
assertThat(rbacUserRepository.findByUuid(newUserUuid))
|
||||
.extracting(RbacUserEntity::getName).isEqualTo("new-user@example.com");
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class GetRbacUser {
|
||||
|
||||
@Test
|
||||
@Accepts({ "USR:R(Read)" })
|
||||
void hostsharingAdmin_withoutAssumedRole_canGetArbitraryUser() {
|
||||
final var givenUser = findRbacUserByName("pac-admin-xxx00@xxx.example.com");
|
||||
|
||||
// @formatter:off
|
||||
RestAssured
|
||||
.given()
|
||||
.header("current-user", "mike@hostsharing.net")
|
||||
.port(port)
|
||||
.when()
|
||||
.get("http://localhost/api/rbac-users/" + givenUser.getUuid())
|
||||
.then().log().body().assertThat()
|
||||
.statusCode(200)
|
||||
.contentType("application/json")
|
||||
.body("name", is("pac-admin-xxx00@xxx.example.com"));
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Test
|
||||
@Accepts({ "USR:R(Read)", "USR:X(Access Control)" })
|
||||
void hostsharingAdmin_withAssumedCustomerAdminRole_canGetUserWithinInItsRealm() {
|
||||
final var givenUser = findRbacUserByName("pac-admin-yyy00@yyy.example.com");
|
||||
|
||||
// @formatter:off
|
||||
RestAssured
|
||||
.given()
|
||||
.header("current-user", "mike@hostsharing.net")
|
||||
.header("assumed-roles", "customer#yyy.admin")
|
||||
.port(port)
|
||||
.when()
|
||||
.get("http://localhost/api/rbac-users/" + givenUser.getUuid())
|
||||
.then().log().body().assertThat()
|
||||
.statusCode(200)
|
||||
.contentType("application/json")
|
||||
.body("name", is("pac-admin-yyy00@yyy.example.com"));
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Test
|
||||
@Accepts({ "USR:R(Read)", "USR:X(Access Control)" })
|
||||
void customerAdmin_withoutAssumedRole_canGetUserWithinInItsRealm() {
|
||||
final var givenUser = findRbacUserByName("pac-admin-yyy00@yyy.example.com");
|
||||
|
||||
// @formatter:off
|
||||
RestAssured
|
||||
.given()
|
||||
.header("current-user", "customer-admin@yyy.example.com")
|
||||
.port(port)
|
||||
.when()
|
||||
.get("http://localhost/api/rbac-users/" + givenUser.getUuid())
|
||||
.then().log().body().assertThat()
|
||||
.statusCode(200)
|
||||
.contentType("application/json")
|
||||
.body("name", is("pac-admin-yyy00@yyy.example.com"));
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Test
|
||||
@Accepts({ "USR:R(Read)", "USR:X(Access Control)" })
|
||||
void customerAdmin_withoutAssumedRole_canNotGetUserOutsideOfItsRealm() {
|
||||
final var givenUser = findRbacUserByName("pac-admin-yyy00@yyy.example.com");
|
||||
|
||||
// @formatter:off
|
||||
RestAssured
|
||||
.given()
|
||||
.header("current-user", "customer-admin@xxx.example.com")
|
||||
.port(port)
|
||||
.when()
|
||||
.get("http://localhost/api/rbac-users/" + givenUser.getUuid())
|
||||
.then().log().body().assertThat()
|
||||
.statusCode(404);
|
||||
// @formatter:on
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class ListRbacUsers {
|
||||
|
||||
@Test
|
||||
@Accepts({ "USR:L(List)" })
|
||||
void hostsharingAdmin_withoutAssumedRole_canViewAllUsers() {
|
||||
|
||||
// @formatter:off
|
||||
@ -65,6 +186,7 @@ class RbacUserControllerAcceptanceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@Accepts({ "USR:F(Filter)" })
|
||||
void hostsharingAdmin_withoutAssumedRole_canViewAllUsersByName() {
|
||||
|
||||
// @formatter:off
|
||||
@ -85,6 +207,30 @@ class RbacUserControllerAcceptanceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@Accepts({ "USR:L(List)", "USR:X(Access Control)" })
|
||||
void hostsharingAdmin_withAssumedCustomerAdminRole_canViewUsersInItsRealm() {
|
||||
|
||||
// @formatter:off
|
||||
RestAssured
|
||||
.given()
|
||||
.header("current-user", "mike@hostsharing.net")
|
||||
.header("assumed-roles", "customer#yyy.admin")
|
||||
.port(port)
|
||||
.when()
|
||||
.get("http://localhost/api/rbac-users")
|
||||
.then().assertThat()
|
||||
.statusCode(200)
|
||||
.contentType("application/json")
|
||||
.body("[0].name", is("customer-admin@yyy.example.com"))
|
||||
.body("[1].name", is("pac-admin-yyy00@yyy.example.com"))
|
||||
.body("[2].name", is("pac-admin-yyy01@yyy.example.com"))
|
||||
.body("[3].name", is("pac-admin-yyy02@yyy.example.com"))
|
||||
.body("size()", is(4));
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Test
|
||||
@Accepts({ "USR:L(List)", "USR:X(Access Control)" })
|
||||
void customerAdmin_withoutAssumedRole_canViewUsersInItsRealm() {
|
||||
|
||||
// @formatter:off
|
||||
@ -106,6 +252,7 @@ class RbacUserControllerAcceptanceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@Accepts({ "USR:L(List)", "USR:X(Access Control)" })
|
||||
void packetAdmin_withoutAssumedRole_canViewAllUsersOfItsPackage() {
|
||||
|
||||
// @formatter:off
|
||||
@ -125,37 +272,135 @@ class RbacUserControllerAcceptanceTest {
|
||||
}
|
||||
|
||||
@Nested
|
||||
class ApiRbacUsersPost {
|
||||
class ListRbacUserPermissions {
|
||||
|
||||
@Test
|
||||
void anybody_canCreateANewUser() {
|
||||
@Accepts({ "PRM:L(List)" })
|
||||
void hostsharingAdmin_withoutAssumedRole_canViewArbitraryUsersPermissions() {
|
||||
final var givenUser = findRbacUserByName("pac-admin-yyy00@yyy.example.com");
|
||||
|
||||
// @formatter:off
|
||||
final var location = RestAssured
|
||||
RestAssured
|
||||
.given()
|
||||
.contentType(ContentType.JSON)
|
||||
.body("""
|
||||
{
|
||||
"name": "new-user@example.com"
|
||||
}
|
||||
""")
|
||||
.header("current-user", "mike@hostsharing.net")
|
||||
.port(port)
|
||||
.when()
|
||||
.post("http://localhost/api/rbac-users")
|
||||
.then().assertThat()
|
||||
.statusCode(201)
|
||||
.contentType(ContentType.JSON)
|
||||
.body("name", is("new-user@example.com"))
|
||||
.header("Location", startsWith("http://localhost"))
|
||||
.extract().header("Location");
|
||||
.get("http://localhost/api/rbac-users/" + givenUser.getUuid() + "/permissions")
|
||||
.then().log().body().assertThat()
|
||||
.statusCode(200)
|
||||
.contentType("application/json")
|
||||
.body("", hasItem(
|
||||
allOf(
|
||||
hasEntry("roleName", "customer#yyy.tenant"),
|
||||
hasEntry("op", "view"))
|
||||
))
|
||||
.body("", hasItem(
|
||||
allOf(
|
||||
hasEntry("roleName", "package#yyy00.admin"),
|
||||
hasEntry("op", "add-unixuser"))
|
||||
))
|
||||
.body("", hasItem(
|
||||
allOf(
|
||||
hasEntry("roleName", "unixuser#yyy00-aaaa.owner"),
|
||||
hasEntry("op", "*"))
|
||||
))
|
||||
.body("size()", is(8));
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
// finally, the user can view its own record
|
||||
final var newUserUuid = UUID.fromString(
|
||||
location.substring(location.lastIndexOf('/') + 1));
|
||||
context.setCurrentUser("new-user@example.com");
|
||||
assertThat(rbacUserRepository.findByUuid(newUserUuid))
|
||||
.extracting(RbacUserEntity::getName).isEqualTo("new-user@example.com");
|
||||
@Test
|
||||
@Accepts({ "PRM:L(List)" })
|
||||
void hostsharingAdmin_withAssumedCustomerAdminRole_canViewArbitraryUsersPermissions() {
|
||||
final var givenUser = findRbacUserByName("pac-admin-yyy00@yyy.example.com");
|
||||
|
||||
// @formatter:off
|
||||
RestAssured
|
||||
.given()
|
||||
.header("current-user", "mike@hostsharing.net")
|
||||
.header("assumed-roles", "package#yyy00.admin")
|
||||
.port(port)
|
||||
.when()
|
||||
.get("http://localhost/api/rbac-users/" + givenUser.getUuid() + "/permissions")
|
||||
.then().log().body().assertThat()
|
||||
.statusCode(200)
|
||||
.contentType("application/json")
|
||||
.body("", hasItem(
|
||||
allOf(
|
||||
hasEntry("roleName", "customer#yyy.tenant"),
|
||||
hasEntry("op", "view"))
|
||||
))
|
||||
.body("", hasItem(
|
||||
allOf(
|
||||
hasEntry("roleName", "package#yyy00.admin"),
|
||||
hasEntry("op", "add-unixuser"))
|
||||
))
|
||||
.body("", hasItem(
|
||||
allOf(
|
||||
hasEntry("roleName", "unixuser#yyy00-aaaa.owner"),
|
||||
hasEntry("op", "*"))
|
||||
))
|
||||
.body("size()", is(8));
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Test
|
||||
@Accepts({ "PRM:L(List)" })
|
||||
void packageAdmin_withoutAssumedRole_canViewPermissionsOfUsersInItsRealm() {
|
||||
final var givenUser = findRbacUserByName("pac-admin-yyy00@yyy.example.com");
|
||||
|
||||
// @formatter:off
|
||||
RestAssured
|
||||
.given()
|
||||
.header("current-user", "pac-admin-yyy00@yyy.example.com")
|
||||
.port(port)
|
||||
.when()
|
||||
.get("http://localhost/api/rbac-users/" + givenUser.getUuid() + "/permissions")
|
||||
.then().log().body().assertThat()
|
||||
.statusCode(200)
|
||||
.contentType("application/json")
|
||||
.body("", hasItem(
|
||||
allOf(
|
||||
hasEntry("roleName", "customer#yyy.tenant"),
|
||||
hasEntry("op", "view"))
|
||||
))
|
||||
.body("", hasItem(
|
||||
allOf(
|
||||
hasEntry("roleName", "package#yyy00.admin"),
|
||||
hasEntry("op", "add-unixuser"))
|
||||
))
|
||||
.body("", hasItem(
|
||||
allOf(
|
||||
hasEntry("roleName", "unixuser#yyy00-aaaa.owner"),
|
||||
hasEntry("op", "*"))
|
||||
))
|
||||
.body("size()", is(8));
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Test
|
||||
@Accepts({ "PRM:L(List)" })
|
||||
void packageAdmin_canViewPermissionsOfUsersOutsideOfItsRealm() {
|
||||
final var givenUser = findRbacUserByName("pac-admin-xxx00@xxx.example.com");
|
||||
|
||||
// @formatter:off
|
||||
RestAssured
|
||||
.given()
|
||||
.header("current-user", "pac-admin-yyy00@yyy.example.com")
|
||||
.port(port)
|
||||
.when()
|
||||
.get("http://localhost/api/rbac-users/" + givenUser.getUuid() + "/permissions")
|
||||
.then().log().body().assertThat()
|
||||
.statusCode(200)
|
||||
.contentType("application/json")
|
||||
.body("size()", is(0));
|
||||
// @formatter:on
|
||||
}
|
||||
}
|
||||
|
||||
RbacUserEntity findRbacUserByName(final String userName) {
|
||||
return jpaAttempt.transacted(() -> {
|
||||
context.setCurrentUser("mike@hostsharing.net");
|
||||
return rbacUserRepository.findByName(userName);
|
||||
}).returnedValue();
|
||||
}
|
||||
}
|
||||
|
@ -231,35 +231,19 @@ class RbacUserRepositoryIntegrationTest extends ContextBasedTest {
|
||||
context("mike@hostsharing.net");
|
||||
|
||||
// when
|
||||
final var result = rbacUserRepository.findPermissionsOfUser("mike@hostsharing.net");
|
||||
final var result = rbacUserRepository.findPermissionsOfUserByUuid(userUUID("mike@hostsharing.net"));
|
||||
|
||||
// then
|
||||
allTheseRbacPermissionsAreReturned(result, ALL_USER_PERMISSIONS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hostsharingAdmin_withAssumedHostmastersRole_willThrowException() {
|
||||
// given
|
||||
context("mike@hostsharing.net", "global#hostsharing.admin");
|
||||
|
||||
// when
|
||||
final var result = attempt(em, () ->
|
||||
rbacUserRepository.findPermissionsOfUser("mike@hostsharing.net")
|
||||
);
|
||||
|
||||
// then
|
||||
result.assertExceptionWithRootCauseMessage(
|
||||
JpaSystemException.class,
|
||||
"[400] grantedPermissions(...) does not support assumed roles");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customerAdmin_withoutAssumedRole_canViewTheirOwnPermissions() {
|
||||
// given
|
||||
context("customer-admin@xxx.example.com");
|
||||
|
||||
// when
|
||||
final var result = rbacUserRepository.findPermissionsOfUser("customer-admin@xxx.example.com");
|
||||
final var result = rbacUserRepository.findPermissionsOfUserByUuid(userUUID("customer-admin@xxx.example.com"));
|
||||
|
||||
// then
|
||||
allTheseRbacPermissionsAreReturned(
|
||||
@ -299,16 +283,18 @@ class RbacUserRepositoryIntegrationTest extends ContextBasedTest {
|
||||
public void customerAdmin_withoutAssumedRole_isNotAllowedToViewGlobalAdminsPermissions() {
|
||||
// given
|
||||
context("customer-admin@xxx.example.com");
|
||||
final UUID userUuid = userUUID("mike@hostsharing.net");
|
||||
|
||||
// when
|
||||
final var result = attempt(em, () ->
|
||||
rbacUserRepository.findPermissionsOfUser("mike@hostsharing.net")
|
||||
rbacUserRepository.findPermissionsOfUserByUuid(userUuid)
|
||||
);
|
||||
|
||||
// then
|
||||
result.assertExceptionWithRootCauseMessage(
|
||||
JpaSystemException.class,
|
||||
"[403] permissions of user \"mike@hostsharing.net\" are not accessible to user \"customer-admin@xxx.example.com\"");
|
||||
"[403] permissions of user \"" + userUuid
|
||||
+ "\" are not accessible to user \"customer-admin@xxx.example.com\"");
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -317,7 +303,7 @@ class RbacUserRepositoryIntegrationTest extends ContextBasedTest {
|
||||
context("customer-admin@xxx.example.com");
|
||||
|
||||
// when
|
||||
final var result = rbacUserRepository.findPermissionsOfUser("pac-admin-xxx00@xxx.example.com");
|
||||
final var result = rbacUserRepository.findPermissionsOfUserByUuid(userUUID("pac-admin-xxx00@xxx.example.com"));
|
||||
|
||||
// then
|
||||
allTheseRbacPermissionsAreReturned(
|
||||
@ -353,7 +339,7 @@ class RbacUserRepositoryIntegrationTest extends ContextBasedTest {
|
||||
context("customer-admin@xxx.example.com");
|
||||
|
||||
// when
|
||||
final var result = rbacUserRepository.findPermissionsOfUser("pac-admin-yyy00@yyy.example.com");
|
||||
final var result = rbacUserRepository.findPermissionsOfUserByUuid(userUUID("pac-admin-yyy00@yyy.example.com"));
|
||||
|
||||
// then
|
||||
noRbacPermissionsAreReturned(result);
|
||||
@ -365,7 +351,7 @@ class RbacUserRepositoryIntegrationTest extends ContextBasedTest {
|
||||
context("pac-admin-xxx00@xxx.example.com");
|
||||
|
||||
// when
|
||||
final var result = rbacUserRepository.findPermissionsOfUser("pac-admin-xxx00@xxx.example.com");
|
||||
final var result = rbacUserRepository.findPermissionsOfUserByUuid(userUUID("pac-admin-xxx00@xxx.example.com"));
|
||||
|
||||
// then
|
||||
allTheseRbacPermissionsAreReturned(
|
||||
@ -397,6 +383,10 @@ class RbacUserRepositoryIntegrationTest extends ContextBasedTest {
|
||||
}
|
||||
}
|
||||
|
||||
UUID userUUID(final String userName) {
|
||||
return rbacUserRepository.findByName(userName).getUuid();
|
||||
}
|
||||
|
||||
void exactlyTheseRbacUsersAreReturned(final List<RbacUserEntity> actualResult, final String... expectedUserNames) {
|
||||
assertThat(actualResult)
|
||||
.extracting(RbacUserEntity::getName)
|
||||
|
Loading…
Reference in New Issue
Block a user