creating and viewing grants
This commit is contained in:
13
src/test/java/net/hostsharing/hsadminng/Accepts.java
Normal file
13
src/test/java/net/hostsharing/hsadminng/Accepts.java
Normal file
@ -0,0 +1,13 @@
|
||||
package net.hostsharing.hsadminng;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
public @interface Accepts {
|
||||
|
||||
String[] value();
|
||||
}
|
@ -32,7 +32,7 @@ class PackageRepositoryIntegrationTest {
|
||||
class FindAllByOptionalNameLike {
|
||||
|
||||
@Test
|
||||
public void hostsharingAdmin_withoutAssumedRole_canNotViewAnyPackages_becauseThoseGrantsAreNotFollowed() {
|
||||
public void hostsharingAdmin_withoutAssumedRole_canNotViewAnyPackages_becauseThoseGrantsAreNotassumedd() {
|
||||
// given
|
||||
currentUser("mike@hostsharing.net");
|
||||
|
||||
@ -44,7 +44,7 @@ class PackageRepositoryIntegrationTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hostsharingAdmin_withAssumedHostsharingAdminRole__canNotViewAnyPackages_becauseThoseGrantsAreNotFollowed() {
|
||||
public void hostsharingAdmin_withAssumedHostsharingAdminRole__canNotViewAnyPackages_becauseThoseGrantsAreNotassumedd() {
|
||||
given:
|
||||
currentUser("mike@hostsharing.net");
|
||||
assumedRoles("global#hostsharing.admin");
|
||||
|
@ -0,0 +1,168 @@
|
||||
package net.hostsharing.hsadminng.rbac.rbacgrant;
|
||||
|
||||
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.hsadminng.rbac.rbacrole.RbacRoleEntity;
|
||||
import net.hostsharing.hsadminng.rbac.rbacrole.RbacRoleRepository;
|
||||
import net.hostsharing.hsadminng.rbac.rbacuser.RbacUserEntity;
|
||||
import net.hostsharing.hsadminng.rbac.rbacuser.RbacUserRepository;
|
||||
import net.hostsharing.test.JpaAttempt;
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.web.server.LocalServerPort;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
||||
@SpringBootTest(
|
||||
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
classes = { HsadminNgApplication.class, JpaAttempt.class }
|
||||
)
|
||||
@Accepts({ "ROL:S(Schema)" })
|
||||
class RbacGrantControllerAcceptanceTest {
|
||||
|
||||
@LocalServerPort
|
||||
Integer port;
|
||||
|
||||
@Autowired
|
||||
EntityManager em;
|
||||
|
||||
@Autowired
|
||||
Context context;
|
||||
|
||||
@Autowired
|
||||
RbacUserRepository rbacUserRepository;
|
||||
|
||||
@Autowired
|
||||
RbacRoleRepository rbacRoleRepository;
|
||||
|
||||
@Autowired
|
||||
RbacGrantRepository rbacGrantRepository;
|
||||
|
||||
@Autowired
|
||||
JpaAttempt jpaAttempt;
|
||||
|
||||
@Test
|
||||
@Accepts({ "ROL:L(List)" })
|
||||
void returnsRbacGrantsForPackageAdmin() {
|
||||
|
||||
RestAssured // @formatter:off
|
||||
.given()
|
||||
.header("current-user", "aaa00@aaa.example.com")
|
||||
.port(port)
|
||||
.when()
|
||||
.get("http://localhost/api/rbac-roles")
|
||||
.then().assertThat()
|
||||
.statusCode(200)
|
||||
.contentType("application/json")
|
||||
.body("[0].roleName", is("customer#aaa.tenant"))
|
||||
.body("[1].roleName", is("package#aaa00.admin"))
|
||||
.body("[2].roleName", is("package#aaa00.tenant"));
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Test
|
||||
@Accepts({ "ROL:C(Create)" })
|
||||
void packageAdmin_canGrantOwnPackageAdminRole_toArbitraryUser() {
|
||||
|
||||
// given
|
||||
final var givenNewUserName = "test-user-" + RandomStringUtils.randomAlphabetic(8) + "@example.com";
|
||||
final String givenPackageAdmin = "aaa00@aaa.example.com";
|
||||
final var givenOwnPackageAdminRole = "package#aaa00.admin";
|
||||
// when
|
||||
RestAssured // @formatter:off
|
||||
.given()
|
||||
.header("current-user", givenPackageAdmin)
|
||||
.contentType(ContentType.JSON)
|
||||
.body("""
|
||||
{
|
||||
"userUuid": "%s",
|
||||
"roleUuid": "%s",
|
||||
"assumed": true,
|
||||
"empowered": false
|
||||
}
|
||||
""".formatted(
|
||||
createRBacUser(givenNewUserName).getUuid().toString(),
|
||||
findRbacRoleByName(givenOwnPackageAdminRole).getUuid().toString())
|
||||
)
|
||||
.port(port)
|
||||
.when()
|
||||
.post("http://localhost/api/rbac-grants")
|
||||
.then().assertThat()
|
||||
.statusCode(201);
|
||||
// @formatter:on
|
||||
|
||||
// then
|
||||
assertThat(findAllGrantsOfUser(givenPackageAdmin))
|
||||
.extracting(RbacGrantEntity::toDisplay)
|
||||
.contains("grant( " + givenNewUserName + " -> " + givenOwnPackageAdminRole + ": assumed )");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Accepts({ "ROL:C(Create)", "ROL:X(Access Control)" })
|
||||
void packageAdmin_canNotGrantAlienPackageAdminRole_toArbitraryUser() {
|
||||
|
||||
// given
|
||||
final var givenNewUserName = "test-user-" + RandomStringUtils.randomAlphabetic(8) + "@example.com";
|
||||
final String givenPackageAdmin = "aaa00@aaa.example.com";
|
||||
final var givenAlienPackageAdminRole = "package#aab00.admin";
|
||||
|
||||
// when
|
||||
RestAssured // @formatter:off
|
||||
.given()
|
||||
.header("current-user", givenPackageAdmin)
|
||||
.contentType(ContentType.JSON)
|
||||
.body("""
|
||||
{
|
||||
"userUuid": "%s",
|
||||
"roleUuid": "%s",
|
||||
"assumed": true,
|
||||
"empowered": false
|
||||
}
|
||||
""".formatted(
|
||||
createRBacUser(givenNewUserName).getUuid().toString(),
|
||||
findRbacRoleByName(givenAlienPackageAdminRole).getUuid().toString())
|
||||
)
|
||||
.port(port)
|
||||
.when()
|
||||
.post("http://localhost/api/rbac-grants")
|
||||
.then().assertThat()
|
||||
.statusCode(403);
|
||||
// @formatter:on
|
||||
|
||||
// then
|
||||
assertThat(findAllGrantsOfUser(givenPackageAdmin))
|
||||
.extracting(RbacGrantEntity::getUserName)
|
||||
.doesNotContain(givenNewUserName);
|
||||
}
|
||||
|
||||
List<RbacGrantEntity> findAllGrantsOfUser(final String userName) {
|
||||
return jpaAttempt.transacted(() -> {
|
||||
context.setCurrentUser(userName);
|
||||
return rbacGrantRepository.findAll();
|
||||
}).returnedValue();
|
||||
}
|
||||
|
||||
RbacUserEntity createRBacUser(final String userName) {
|
||||
return jpaAttempt.transacted(() -> {
|
||||
return rbacUserRepository.create(new RbacUserEntity(UUID.randomUUID(), userName));
|
||||
}).returnedValue();
|
||||
}
|
||||
|
||||
RbacRoleEntity findRbacRoleByName(final String roleName) {
|
||||
return jpaAttempt.transacted(() -> {
|
||||
context.setCurrentUser("mike@hostsharing.net");
|
||||
return rbacRoleRepository.findByRoleName(roleName);
|
||||
}).returnedValue();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,142 @@
|
||||
package net.hostsharing.hsadminng.rbac.rbacgrant;
|
||||
|
||||
import net.hostsharing.hsadminng.Accepts;
|
||||
import net.hostsharing.hsadminng.context.Context;
|
||||
import net.hostsharing.hsadminng.rbac.rbacrole.RbacRoleRepository;
|
||||
import net.hostsharing.hsadminng.rbac.rbacuser.RbacUserRepository;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.orm.jpa.JpaSystemException;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import java.util.List;
|
||||
|
||||
import static net.hostsharing.test.JpaAttempt.attempt;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@DataJpaTest
|
||||
@ComponentScan(basePackageClasses = { Context.class, RbacGrantRepository.class })
|
||||
@DirtiesContext
|
||||
@Accepts({ "GRT:S(Schema)" })
|
||||
class RbacGrantRepositoryIntegrationTest {
|
||||
|
||||
@Autowired
|
||||
Context context;
|
||||
|
||||
@Autowired
|
||||
RbacGrantRepository rbacGrantRepository;
|
||||
|
||||
@Autowired
|
||||
RbacUserRepository rbacUserRepository;
|
||||
|
||||
@Autowired
|
||||
RbacRoleRepository rbacRoleRepository;
|
||||
|
||||
@Autowired
|
||||
EntityManager em;
|
||||
|
||||
@Nested
|
||||
class FindAllRbacGrants {
|
||||
|
||||
@Test
|
||||
@Accepts({ "GRT:L(List)" })
|
||||
public void packageAdmin_canViewItsRbacGrants() {
|
||||
// given
|
||||
currentUser("aaa00@aaa.example.com");
|
||||
|
||||
// when
|
||||
final var result = rbacGrantRepository.findAll();
|
||||
|
||||
// then
|
||||
exactlyTheseRbacGrantsAreReturned(
|
||||
result,
|
||||
"grant( aaa00@aaa.example.com -> package#aaa00.admin: managed assumed empowered )");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Accepts({ "GRT:L(List)" })
|
||||
public void customerAdmin_canViewItsRbacGrants() {
|
||||
// given
|
||||
currentUser("admin@aaa.example.com");
|
||||
|
||||
// when
|
||||
final var result = rbacGrantRepository.findAll();
|
||||
|
||||
// then
|
||||
exactlyTheseRbacGrantsAreReturned(
|
||||
result,
|
||||
"grant( admin@aaa.example.com -> customer#aaa.admin: managed assumed empowered )",
|
||||
"grant( aaa00@aaa.example.com -> package#aaa00.admin: managed assumed empowered )",
|
||||
"grant( aaa01@aaa.example.com -> package#aaa01.admin: managed assumed empowered )",
|
||||
"grant( aaa02@aaa.example.com -> package#aaa02.admin: managed assumed empowered )");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Accepts({ "GRT:L(List)" })
|
||||
public void customerAdmin_withAssumedRole_cannotViewRbacGrants() {
|
||||
// given:
|
||||
currentUser("admin@aaa.example.com");
|
||||
assumedRoles("package#aab00.admin");
|
||||
|
||||
// when
|
||||
final var result = attempt(
|
||||
em,
|
||||
() -> rbacGrantRepository.findAll());
|
||||
|
||||
// then
|
||||
result.assertExceptionWithRootCauseMessage(
|
||||
JpaSystemException.class,
|
||||
"[403] user admin@aaa.example.com", "has no permission to assume role package#aab00#admin");
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class CreateRbacGrant {
|
||||
|
||||
@Test
|
||||
@Accepts({ "GRT:C(Create)" })
|
||||
public void customerAdmin_canGrantOwnPackageAdminRole_toArbitraryUser() {
|
||||
// given
|
||||
currentUser("admin@aaa.example.com");
|
||||
final var userUuid = rbacUserRepository.findUuidByName("aac00@aac.example.com");
|
||||
final var roleUuid = rbacRoleRepository.findByRoleName("package#aaa00.admin").getUuid();
|
||||
|
||||
// when
|
||||
final var grant = RbacGrantEntity.builder()
|
||||
.userUuid(userUuid).roleUuid(roleUuid)
|
||||
.assumed(true).empowered(false)
|
||||
.build();
|
||||
final var attempt = attempt(em, () ->
|
||||
rbacGrantRepository.save(grant)
|
||||
);
|
||||
|
||||
// then
|
||||
assertThat(attempt.wasSuccessful()).isTrue();
|
||||
assertThat(rbacGrantRepository.findAll())
|
||||
.extracting(RbacGrantEntity::toDisplay)
|
||||
.contains("grant( aac00@aac.example.com -> package#aaa00.admin: assumed )");
|
||||
}
|
||||
}
|
||||
|
||||
void currentUser(final String currentUser) {
|
||||
context.setCurrentUser(currentUser);
|
||||
assertThat(context.getCurrentUser()).as("precondition").isEqualTo(currentUser);
|
||||
}
|
||||
|
||||
void assumedRoles(final String assumedRoles) {
|
||||
context.assumeRoles(assumedRoles);
|
||||
assertThat(context.getAssumedRoles()).as("precondition").containsExactly(assumedRoles.split(";"));
|
||||
}
|
||||
|
||||
void exactlyTheseRbacGrantsAreReturned(final List<RbacGrantEntity> actualResult, final String... expectedGrant) {
|
||||
assertThat(actualResult)
|
||||
.filteredOn(g -> !g.getUserName().startsWith("test-user-")) // ignore test-users created by other tests
|
||||
.extracting(RbacGrantEntity::toDisplay)
|
||||
.containsExactlyInAnyOrder(expectedGrant);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
package net.hostsharing.hsadminng.rbac.rbacrole;
|
||||
|
||||
import io.restassured.RestAssured;
|
||||
import net.hostsharing.hsadminng.Accepts;
|
||||
import net.hostsharing.hsadminng.HsadminNgApplication;
|
||||
import net.hostsharing.hsadminng.context.Context;
|
||||
import net.hostsharing.hsadminng.rbac.rbacuser.RbacUserRepository;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.web.server.LocalServerPort;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
||||
@SpringBootTest(
|
||||
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
classes = HsadminNgApplication.class
|
||||
)
|
||||
@Accepts({ "ROL:*:S:Schema" })
|
||||
class RbacRoleControllerAcceptanceTest {
|
||||
|
||||
@LocalServerPort
|
||||
private Integer port;
|
||||
|
||||
@Autowired
|
||||
EntityManager em;
|
||||
|
||||
@Autowired
|
||||
Context context;
|
||||
|
||||
@Autowired
|
||||
RbacUserRepository rbacUserRepository;
|
||||
|
||||
@Autowired
|
||||
RbacRoleRepository rbacRoleRepository;
|
||||
|
||||
@Test
|
||||
@Accepts({ "ROL:*:L:List" })
|
||||
void returnsRbacRolesForAssumedPackageAdmin() {
|
||||
|
||||
// @formatter:off
|
||||
RestAssured
|
||||
.given()
|
||||
.header("current-user", "mike@hostsharing.net")
|
||||
.header("assumed-roles", "package#aaa00.admin")
|
||||
.port(port)
|
||||
.when()
|
||||
.get("http://localhost/api/rbac-roles")
|
||||
.then().assertThat()
|
||||
.statusCode(200)
|
||||
.contentType("application/json")
|
||||
.body("[0].roleName", is("customer#aaa.tenant"))
|
||||
.body("[1].roleName", is("package#aaa00.admin"))
|
||||
.body("[2].roleName", is("package#aaa00.tenant"));
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
}
|
@ -15,8 +15,7 @@ import javax.transaction.Transactional;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
|
||||
@SpringBootTest(
|
||||
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
@ -56,7 +55,12 @@ class RbacUserControllerAcceptanceTest {
|
||||
.body("[0].name", is("aaa00@aaa.example.com"))
|
||||
.body("[1].name", is("aaa01@aaa.example.com"))
|
||||
.body("[2].name", is("aaa02@aaa.example.com"))
|
||||
.body("size()", is(14));
|
||||
.body("[3].name", is("aab00@aab.example.com"))
|
||||
// ...
|
||||
.body("[11].name", is("admin@aac.example.com"))
|
||||
.body("[12].name", is("mike@hostsharing.net"))
|
||||
.body("[13].name", is("sven@hostsharing.net"))
|
||||
.body("size()", greaterThanOrEqualTo(14));
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
|
@ -371,6 +371,7 @@ class RbacUserRepositoryIntegrationTest {
|
||||
|
||||
void exactlyTheseRbacUsersAreReturned(final List<RbacUserEntity> actualResult, final String... expectedUserNames) {
|
||||
assertThat(actualResult)
|
||||
.filteredOn(u -> !u.getName().startsWith("test-user-"))
|
||||
.extracting(RbacUserEntity::getName)
|
||||
.containsExactlyInAnyOrder(expectedUserNames);
|
||||
}
|
||||
|
@ -49,6 +49,13 @@ public class JpaAttempt {
|
||||
}
|
||||
}
|
||||
|
||||
public static JpaResult<Void> attempt(final EntityManager em, final Runnable code) {
|
||||
return attempt(em, () -> {
|
||||
code.run();
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||
public <T> JpaResult<T> transacted(final Supplier<T> code) {
|
||||
return attempt(em, code);
|
||||
|
Reference in New Issue
Block a user