1
0

add RbacUser* tests and improved http status codes

This commit is contained in:
Michael Hoennig
2022-08-05 14:31:54 +02:00
parent f2bc42bd85
commit bef358eda6
16 changed files with 540 additions and 73 deletions

View File

@ -159,7 +159,7 @@ class CustomerRepositoryIntegrationTest {
// then
attempt.assertExceptionWithRootCauseMessage(
JpaSystemException.class,
"user admin@aaa.example.com .* has no permission to assume role package#aab00#admin");
"[403] user admin@aaa.example.com", "has no permission to assume role package#aab00#admin");
}
@Test

View File

@ -1,6 +1,7 @@
package net.hostsharing.hsadminng.rbac.rbacrole;
import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.test.Array;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
@ -29,7 +30,7 @@ class RbacRoleRepositoryIntegrationTest {
@Nested
class FindAllRbacRoles {
private static final String[] ALL_TEST_DATA_ROLES = new String[] {
private static final String[] ALL_TEST_DATA_ROLES = Array.of(
// @formatter:off
"global#hostsharing.admin",
"customer#aaa.admin", "customer#aaa.owner", "customer#aaa.tenant",
@ -45,7 +46,7 @@ class RbacRoleRepositoryIntegrationTest {
"package#aac01.admin", "package#aac01.owner", "package#aac01.tenant",
"package#aac02.admin", "package#aac02.owner", "package#aac02.tenant"
// @formatter:on
};
);
@Test
public void hostsharingAdmin_withoutAssumedRole_canViewAllRbacRoles() {
@ -116,7 +117,7 @@ class RbacRoleRepositoryIntegrationTest {
// then
attempt.assertExceptionWithRootCauseMessage(
JpaSystemException.class,
"user admin@aaa.example.com .* has no permission to assume role package#aab00#admin");
"[403] user admin@aaa.example.com", "has no permission to assume role package#aab00#admin");
}
@Test
@ -159,11 +160,10 @@ class RbacRoleRepositoryIntegrationTest {
assertThat(context.getAssumedRoles()).as("precondition").containsExactly(assumedRoles.split(";"));
}
void exactlyTheseRbacRolesAreReturned(final Iterable<RbacRoleEntity> actualResult, final String... rbacRoleNames) {
void exactlyTheseRbacRolesAreReturned(final Iterable<RbacRoleEntity> actualResult, final String... expectedRoleNames) {
assertThat(actualResult)
//.hasSize(rbacRoleNames.length)
.extracting(RbacRoleEntity::getRoleName)
.containsExactlyInAnyOrder(rbacRoleNames);
.containsExactlyInAnyOrder(expectedRoleNames);
}
}

View File

@ -0,0 +1,72 @@
package net.hostsharing.hsadminng.rbac.rbacuser;
import net.hostsharing.hsadminng.context.Context;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import java.util.List;
import static net.hostsharing.hsadminng.rbac.rbacrole.TestRbacRole.*;
import static net.hostsharing.hsadminng.rbac.rbacuser.TestRbacUser.userAaa;
import static net.hostsharing.hsadminng.rbac.rbacuser.TestRbacUser.userBbb;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@WebMvcTest(RbacUserController.class)
class RbacUserControllerRestTest {
@Autowired
MockMvc mockMvc;
@MockBean
Context contextMock;
@MockBean
RbacUserRepository rbacUserRepository;
@Test
void willListAllUsers() throws Exception {
// given
when(rbacUserRepository.findByOptionalNameLike(null)).thenReturn(
List.of(userAaa, userBbb));
// when
mockMvc.perform(MockMvcRequestBuilders
.get("/api/rbacusers")
.header("current-user", "mike@hostsharing.net")
.accept(MediaType.APPLICATION_JSON))
// then
.andExpect(status().isOk())
.andExpect(jsonPath("$", hasSize(2)))
.andExpect(jsonPath("$[0].name", is(userAaa.getName())))
.andExpect(jsonPath("$[1].name", is(userBbb.getName())));
}
@Test
void willListUsersByName() throws Exception {
// given
when(rbacUserRepository.findByOptionalNameLike("admin@aaa")).thenReturn(
List.of(userAaa));
// when
mockMvc.perform(MockMvcRequestBuilders
.get("/api/rbacusers")
.param("name", "admin@aaa")
.header("current-user", "mike@hostsharing.net")
.accept(MediaType.APPLICATION_JSON))
// then
.andExpect(status().isOk())
.andExpect(jsonPath("$", hasSize(1)))
.andExpect(jsonPath("$[0].name", is(userAaa.getName())));
}
}

View File

@ -0,0 +1,315 @@
package net.hostsharing.hsadminng.rbac.rbacuser;
import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.test.Array;
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 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, RbacUserRepository.class })
class RbacUserRepositoryIntegrationTest {
@Autowired
Context context;
@Autowired
RbacUserRepository rbacUserRepository;
@Autowired EntityManager em;
@Nested
class FindByOptionalNameLike {
private static final String[] ALL_TEST_DATA_USERS = Array.of(
// @formatter:off
"mike@hostsharing.net", "sven@hostsharing.net",
"admin@aaa.example.com",
"aaa00@aaa.example.com", "aaa01@aaa.example.com", "aaa02@aaa.example.com",
"admin@aab.example.com",
"aab00@aab.example.com", "aab01@aab.example.com", "aab02@aab.example.com",
"admin@aac.example.com",
"aac00@aac.example.com", "aac01@aac.example.com", "aac02@aac.example.com"
// @formatter:on
);
@Test
public void hostsharingAdmin_withoutAssumedRole_canViewAllRbacUsers() {
// given
currentUser("mike@hostsharing.net");
// when
final var result = rbacUserRepository.findByOptionalNameLike(null);
// then
exactlyTheseRbacUsersAreReturned(result, ALL_TEST_DATA_USERS);
}
@Test
public void hostsharingAdmin_withAssumedHostsharingAdminRole_canViewAllRbacUsers() {
given:
currentUser("mike@hostsharing.net");
assumedRoles("global#hostsharing.admin");
// when
final var result = rbacUserRepository.findByOptionalNameLike(null);
then:
exactlyTheseRbacUsersAreReturned(result, ALL_TEST_DATA_USERS);
}
@Test
public void hostsharingAdmin_withAssumedCustomerAdminRole_canViewOnlyUsersHavingRolesInThatCustomersRealm() {
given:
currentUser("mike@hostsharing.net");
assumedRoles("customer#aaa.admin");
// when
final var result = rbacUserRepository.findByOptionalNameLike(null);
then:
exactlyTheseRbacUsersAreReturned(
result,
"admin@aaa.example.com",
"aaa00@aaa.example.com", "aaa01@aaa.example.com", "aaa02@aaa.example.com"
);
}
@Test
public void customerAdmin_withoutAssumedRole_canViewOnlyUsersHavingRolesInThatCustomersRealm() {
// given:
currentUser("admin@aaa.example.com");
// when:
final var result = rbacUserRepository.findByOptionalNameLike(null);
// then:
exactlyTheseRbacUsersAreReturned(
result,
"admin@aaa.example.com",
"aaa00@aaa.example.com", "aaa01@aaa.example.com", "aaa02@aaa.example.com"
);
}
@Test
public void customerAdmin_withAssumedOwnedPackageAdminRole_canViewOnlyUsersHavingRolesInThatPackage() {
currentUser("admin@aaa.example.com");
assumedRoles("package#aaa00.admin");
final var result = rbacUserRepository.findByOptionalNameLike(null);
exactlyTheseRbacUsersAreReturned(result, "aaa00@aaa.example.com");
}
@Test
public void packageAdmin_withoutAssumedRole_canViewOnlyUsersHavingRolesInThatPackage() {
currentUser("aaa00@aaa.example.com");
final var result = rbacUserRepository.findByOptionalNameLike(null);
exactlyTheseRbacUsersAreReturned(result, "aaa00@aaa.example.com");
}
}
@Nested
class ListUserPermissions {
private static final String[] ALL_USER_PERMISSIONS = Array.of(
// @formatter:off
"global#hostsharing.admin -> global#hostsharing: add-customer",
"customer#aaa.admin -> customer#aaa: add-package",
"customer#aaa.admin -> customer#aaa: view",
"customer#aaa.owner -> customer#aaa: *",
"customer#aaa.tenant -> customer#aaa: view",
"package#aaa00.admin -> package#aaa00: add-domain",
"package#aaa00.admin -> package#aaa00: add-unixuser",
"package#aaa00.tenant -> package#aaa00: view",
"package#aaa01.admin -> package#aaa01: add-domain",
"package#aaa01.admin -> package#aaa01: add-unixuser",
"package#aaa01.tenant -> package#aaa01: view",
"package#aaa02.admin -> package#aaa02: add-domain",
"package#aaa02.admin -> package#aaa02: add-unixuser",
"package#aaa02.tenant -> package#aaa02: view",
"customer#aab.admin -> customer#aab: add-package",
"customer#aab.admin -> customer#aab: view",
"customer#aab.owner -> customer#aab: *",
"customer#aab.tenant -> customer#aab: view",
"package#aab00.admin -> package#aab00: add-domain",
"package#aab00.admin -> package#aab00: add-unixuser",
"package#aab00.tenant -> package#aab00: view",
"package#aab01.admin -> package#aab01: add-domain",
"package#aab01.admin -> package#aab01: add-unixuser",
"package#aab01.tenant -> package#aab01: view",
"package#aab02.admin -> package#aab02: add-domain",
"package#aab02.admin -> package#aab02: add-unixuser",
"package#aab02.tenant -> package#aab02: view",
"customer#aac.admin -> customer#aac: add-package",
"customer#aac.admin -> customer#aac: view",
"customer#aac.owner -> customer#aac: *",
"customer#aac.tenant -> customer#aac: view",
"package#aac00.admin -> package#aac00: add-domain",
"package#aac00.admin -> package#aac00: add-unixuser",
"package#aac00.tenant -> package#aac00: view",
"package#aac01.admin -> package#aac01: add-domain",
"package#aac01.admin -> package#aac01: add-unixuser",
"package#aac01.tenant -> package#aac01: view",
"package#aac02.admin -> package#aac02: add-domain",
"package#aac02.admin -> package#aac02: add-unixuser",
"package#aac02.tenant -> package#aac02: view"
// @formatter:on
);
@Test
public void hostsharingAdmin_withoutAssumedRole_canViewTheirOwnPermissions() {
// given
currentUser("mike@hostsharing.net");
// when
final var result = rbacUserRepository.findPermissionsOfUser("mike@hostsharing.net");
// then
exactlyTheseRbacPermissionsAreReturned(result, ALL_USER_PERMISSIONS);
}
@Test
public void hostsharingAdmin_withAssumedHostmastersRole_willThrowException() {
// given
currentUser("mike@hostsharing.net");
assumedRoles("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
currentUser("admin@aaa.example.com");
// when
final var result = rbacUserRepository.findPermissionsOfUser("admin@aaa.example.com");
// then
exactlyTheseRbacPermissionsAreReturned(result,
// @formatter:off
"customer#aaa.admin -> customer#aaa: add-package",
"customer#aaa.admin -> customer#aaa: view",
"customer#aaa.tenant -> customer#aaa: view",
"package#aaa00.admin -> package#aaa00: add-domain",
"package#aaa00.admin -> package#aaa00: add-unixuser",
"package#aaa00.tenant -> package#aaa00: view",
"package#aaa01.admin -> package#aaa01: add-domain",
"package#aaa01.admin -> package#aaa01: add-unixuser",
"package#aaa01.tenant -> package#aaa01: view",
"package#aaa02.admin -> package#aaa02: add-domain",
"package#aaa02.admin -> package#aaa02: add-unixuser",
"package#aaa02.tenant -> package#aaa02: view"
// @formatter:on
);
}
@Test
public void customerAdmin_withoutAssumedRole_isNotAllowedToViewGlobalAdminsPermissions() {
// given
currentUser("admin@aaa.example.com");
// when
final var result = attempt(em, () ->
rbacUserRepository.findPermissionsOfUser("mike@hostsharing.net")
);
// then
result.assertExceptionWithRootCauseMessage(
JpaSystemException.class,
"[403] permissions of user \"mike@hostsharing.net\" are not accessible to user \"admin@aaa.example.com\"");
}
@Test
public void customerAdmin_withoutAssumedRole_canViewAllPermissionsWithinThePacketsRealm() {
// given
currentUser("admin@aaa.example.com");
// when
final var result = rbacUserRepository.findPermissionsOfUser("aaa00@aaa.example.com");
// then
exactlyTheseRbacPermissionsAreReturned(result,
// @formatter:off
"customer#aaa.tenant -> customer#aaa: view",
// "customer#aaa.admin -> customer#aaa: view" - Not permissions through the customer admin!
"package#aaa00.admin -> package#aaa00: add-unixuser",
"package#aaa00.admin -> package#aaa00: add-domain",
"package#aaa00.tenant -> package#aaa00: view"
// @formatter:on
);
}
@Test
public void packetAdmin_withoutAssumedRole_canViewAllPermissionsWithinThePacketsRealm() {
// given
currentUser("aaa00@aaa.example.com");
// when
final var result = rbacUserRepository.findPermissionsOfUser("aaa00@aaa.example.com");
// then
exactlyTheseRbacPermissionsAreReturned(result,
// @formatter:off
"customer#aaa.tenant -> customer#aaa: view",
// "customer#aaa.admin -> customer#aaa: view" - Not permissions through the customer admin!
"package#aaa00.admin -> package#aaa00: add-unixuser",
"package#aaa00.admin -> package#aaa00: add-domain",
"package#aaa00.tenant -> package#aaa00: view"
// @formatter:on
);
}
}
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 exactlyTheseRbacUsersAreReturned(final List<RbacUserEntity> actualResult, final String... expectedUserNames) {
assertThat(actualResult)
.extracting(RbacUserEntity::getName)
.containsExactlyInAnyOrder(expectedUserNames);
}
void exactlyTheseRbacPermissionsAreReturned(
final List<RbacUserPermission> actualResult,
final String... expectedRoleNames) {
assertThat(actualResult)
.extracting(p -> p.getRoleName() + " -> " + p.getObjectTable() + "#" + p.getObjectIdName() + ": " + p.getOp())
.containsExactlyInAnyOrder(expectedRoleNames);
}
}

View File

@ -0,0 +1,14 @@
package net.hostsharing.hsadminng.rbac.rbacuser;
import static java.util.UUID.randomUUID;
public class TestRbacUser {
static final RbacUserEntity userAaa = rbacRole("admin@aaa.example.com");
static final RbacUserEntity userBbb = rbacRole("admin@bbb.example.com");
static public RbacUserEntity rbacRole(final String userName) {
return new RbacUserEntity(randomUUID(), userName);
}
}

View File

@ -0,0 +1,13 @@
package net.hostsharing.test;
/**
* Java has List.of(...), Set.of(...) and Map.of(...) all with varargs parameter,
* but no Array.of(...). Here it is.
*/
public class Array {
@SafeVarargs
public static <E> E[] of(E... elements) {
return elements;
}
}

View File

@ -71,9 +71,11 @@ public class JpaAttempt<T> {
public void assertExceptionWithRootCauseMessage(
final Class<? extends RuntimeException> expectedExceptionClass,
final String expectedRootCauseMessage) {
assertThat(
firstRootCauseMessageLineOf(caughtException(expectedExceptionClass)))
.matches(".*" + expectedRootCauseMessage + ".*");
final String... expectedRootCauseMessages) {
assertThat(wasSuccessful()).isFalse();
final String firstRootCauseMessageLine = firstRootCauseMessageLineOf(caughtException(expectedExceptionClass));
for ( String expectedRootCauseMessage: expectedRootCauseMessages ) {
assertThat(firstRootCauseMessageLine).contains(expectedRootCauseMessage);
}
}
}