implements REST API DELETE to /api/rbac-grants/{grantedRoleUuid}/{granteeUserUuid}:

This commit is contained in:
Michael Hoennig 2022-08-17 17:34:10 +02:00
parent 787400c089
commit 8a62d9802e
9 changed files with 150 additions and 5 deletions

View File

@ -60,4 +60,22 @@ public class RbacGrantController implements RbacgrantsApi {
return ResponseEntity.created(uri).build(); return ResponseEntity.created(uri).build();
} }
@Override
@Transactional
public ResponseEntity<Void> revokeRoleFromUser(
final String currentUser,
final String assumedRoles,
final UUID grantedRoleUuid,
final UUID granteeUserUuid) {
context.setCurrentUser(currentUser);
if (assumedRoles != null && !assumedRoles.isBlank()) {
context.assumeRoles(assumedRoles);
}
rbacGrantRepository.deleteByRbacGrantId(new RbacGrantId(granteeUserUuid, grantedRoleUuid));
return ResponseEntity.noContent().build();
}
} }

View File

@ -53,6 +53,10 @@ public class RbacGrantEntity {
@Enumerated(EnumType.STRING) @Enumerated(EnumType.STRING)
private RbacRoleType grantedRoleType; private RbacRoleType grantedRoleType;
RbacGrantId getRbacGrantId() {
return new RbacGrantId(granteeUserUuid, grantedRoleUuid);
}
public String toDisplay() { public String toDisplay() {
return "{ grant " + (assumed ? "assumed " : "") + return "{ grant " + (assumed ? "assumed " : "") +
"role " + grantedRoleIdName + " to user " + granteeUserName + " by role " + grantedByRoleIdName + " }"; "role " + grantedRoleIdName + " to user " + granteeUserName + " by role " + grantedByRoleIdName + " }";

View File

@ -1,5 +1,6 @@
package net.hostsharing.hsadminng.rbac.rbacgrant; package net.hostsharing.hsadminng.rbac.rbacgrant;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
@ -10,6 +11,7 @@ import java.util.UUID;
@Getter @Getter
@EqualsAndHashCode @EqualsAndHashCode
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor
public class RbacGrantId implements Serializable { public class RbacGrantId implements Serializable {
private UUID granteeUserUuid; private UUID granteeUserUuid;

View File

@ -1,5 +1,7 @@
package net.hostsharing.hsadminng.rbac.rbacgrant; package net.hostsharing.hsadminng.rbac.rbacgrant;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.Repository; import org.springframework.data.repository.Repository;
import java.util.List; import java.util.List;
@ -10,5 +12,11 @@ public interface RbacGrantRepository extends Repository<RbacGrantEntity, RbacGra
void save(final RbacGrantEntity grant); void save(final RbacGrantEntity grant);
void delete(final RbacGrantEntity grant); @Modifying
@Query(value = """
delete from RbacGrantEntity as g
where g.grantedRoleUuid=:#{#rbacGrantId.grantedRoleUuid}
and g.granteeUserUuid=:#{#rbacGrantId.granteeUserUuid}
""")
void deleteByRbacGrantId(RbacGrantId rbacGrantId);
} }

View File

@ -22,6 +22,9 @@ paths:
/api/rbac-grants: /api/rbac-grants:
$ref: "./api-definition/rbac-grants.yaml" $ref: "./api-definition/rbac-grants.yaml"
/api/rbac-grants/{grantedRoleUuid}/{granteeUserUuid}:
$ref: "./api-definition/rbac-grants-id.yaml"
# HS # HS
/api/customers: /api/customers:

View File

@ -0,0 +1,30 @@
delete:
tags:
- rbacgrants
operationId: revokeRoleFromUser
parameters:
- $ref: './api-definition/auth.yaml#/components/parameters/currentUser'
- $ref: './api-definition/auth.yaml#/components/parameters/assumedRoles'
- name: grantedRoleUuid
in: path
required: true
schema:
type: string
format: uuid
description: UUID of the granted role.
- name: granteeUserUuid
in: path
required: true
schema:
type: string
format: uuid
description: UUID of the user to whom the role was granted.
responses:
"204":
description: No Content
"401":
$ref: './api-definition/error-responses.yaml#/components/responses/Unauthorized'
"403":
$ref: './api-definition/error-responses.yaml#/components/responses/Forbidden'
"404":
$ref: './api-definition/error-responses.yaml#/components/responses/NotFound'

View File

@ -31,6 +31,7 @@ post:
responses: responses:
"201": "201":
description: OK description: OK
content:
"401": "401":
$ref: './api-definition/error-responses.yaml#/components/responses/Unauthorized' $ref: './api-definition/error-responses.yaml#/components/responses/Unauthorized'
"403": "403":

View File

@ -142,6 +142,83 @@ class RbacGrantControllerAcceptanceTest {
} }
} }
@Nested
class RevokeRoleFromUser {
@Test
@Accepts({ "GRT:D(Delete)" })
@Transactional(propagation = Propagation.NEVER)
void packageAdmin_canRevokePackageAdminRole_grantedByPackageAdmin_toArbitraryUser() {
// given
final var givenNewUserName = "test-user-" + RandomStringUtils.randomAlphabetic(8) + "@example.com";
final var givenNewUserNameUuid = createRBacUser(givenNewUserName).getUuid();
final var givenCurrentUserPackageAdmin = "aaa00@aaa.example.com";
final var givenAssumedRole = "package#aaa00.admin";
final var givenOwnPackageAdminRole = "package#aaa00.admin";
final var givenOwnPackageAdminRoleUuid = findRbacRoleByName(givenOwnPackageAdminRole).getUuid();
final var expectedGrant = "{ grant assumed role " + givenOwnPackageAdminRole +
" to user " + givenNewUserName +
" by role " + givenAssumedRole + " }";
// and given a grant
RestAssured // @formatter:off
.given()
.header("current-user", givenCurrentUserPackageAdmin)
.header("assumed-roles", givenAssumedRole)
.contentType(ContentType.JSON)
.body("""
{
"assumed": true,
"grantedRoleUuid": "%s",
"granteeUserUuid": "%s"
}
""".formatted(
givenOwnPackageAdminRoleUuid.toString(),
givenNewUserNameUuid.toString())
)
.port(port)
.when()
.post("http://localhost/api/rbac-grants")
.then().assertThat()
.statusCode(201); // @formatter:on
assumeThat(findAllGrantsOfUser(givenCurrentUserPackageAdmin))
.extracting(RbacGrantEntity::toDisplay)
.contains(expectedGrant);
// when
RestAssured // @formatter:off
.given()
.header("current-user", givenCurrentUserPackageAdmin)
.header("assumed-roles", givenAssumedRole)
.contentType(ContentType.JSON)
.body("""
{
"assumed": true,
"grantedRoleUuid": "%s",
"granteeUserUuid": "%s"
}
""".formatted(
givenOwnPackageAdminRoleUuid.toString(),
givenNewUserNameUuid.toString())
)
.port(port)
.when()
.delete("http://localhost/api/rbac-grants/%s/%s".formatted(
givenOwnPackageAdminRoleUuid, givenNewUserNameUuid
) )
.then().assertThat()
.statusCode(204); // @formatter:on
// then
assertThat(findAllGrantsOfUser(givenCurrentUserPackageAdmin))
.extracting(RbacGrantEntity::toDisplay)
.doesNotContain("{ grant assumed role " + givenOwnPackageAdminRole +
" to user " + givenNewUserName +
" by role " + givenAssumedRole + " }");
}
}
List<RbacGrantEntity> findAllGrantsOfUser(final String userName) { List<RbacGrantEntity> findAllGrantsOfUser(final String userName) {
return jpaAttempt.transacted(() -> { return jpaAttempt.transacted(() -> {
context.setCurrentUser(userName); context.setCurrentUser(userName);

View File

@ -132,7 +132,9 @@ class RbacGrantRepositoryIntegrationTest {
@Transactional(propagation = Propagation.NEVER) @Transactional(propagation = Propagation.NEVER)
public void packageAdmin_canNotGrantPackageOwnerRole() { public void packageAdmin_canNotGrantPackageOwnerRole() {
// given // given
record Given(RbacUserEntity arbitraryUser, UUID packageOwnerRoleUuid) {} record Given(RbacUserEntity arbitraryUser, UUID packageOwnerRoleUuid) {
}
final var given = jpaAttempt.transacted(() -> { final var given = jpaAttempt.transacted(() -> {
// to find the uuids of we need to have access rights to these // to find the uuids of we need to have access rights to these
currentUser("admin@aaa.example.com"); currentUser("admin@aaa.example.com");
@ -184,7 +186,7 @@ class RbacGrantRepositoryIntegrationTest {
currentUser("admin@aaa.example.com"); currentUser("admin@aaa.example.com");
assumedRoles("customer#aaa.admin"); assumedRoles("customer#aaa.admin");
final var revokeAttempt = attempt(em, () -> { final var revokeAttempt = attempt(em, () -> {
rbacGrantRepository.delete(grant); rbacGrantRepository.deleteByRbacGrantId(grant.getRbacGrantId());
}); });
// then // then
@ -207,7 +209,7 @@ class RbacGrantRepositoryIntegrationTest {
currentUser("aaa00@aaa.example.com"); currentUser("aaa00@aaa.example.com");
assumedRoles("package#aaa00.admin"); assumedRoles("package#aaa00.admin");
final var revokeAttempt = attempt(em, () -> { final var revokeAttempt = attempt(em, () -> {
rbacGrantRepository.delete(grant); rbacGrantRepository.deleteByRbacGrantId(grant.getRbacGrantId());
}); });
// then // then
@ -231,7 +233,7 @@ class RbacGrantRepositoryIntegrationTest {
currentUser("aaa00@aaa.example.com"); currentUser("aaa00@aaa.example.com");
assumedRoles("package#aaa00.admin"); assumedRoles("package#aaa00.admin");
final var revokeAttempt = attempt(em, () -> { final var revokeAttempt = attempt(em, () -> {
rbacGrantRepository.delete(grant); rbacGrantRepository.deleteByRbacGrantId(grant.getRbacGrantId());
}); });
// then // then