improve test code coverage and generic array return from native queries
This commit is contained in:
parent
a1c3e95032
commit
8af93603d5
@ -1,16 +0,0 @@
|
|||||||
package net.hostsharing.hsadminng.config;
|
|
||||||
|
|
||||||
import com.vladmihalcea.hibernate.type.array.UUIDArrayType;
|
|
||||||
import org.hibernate.dialect.PostgreSQL95Dialect;
|
|
||||||
|
|
||||||
import java.sql.Types;
|
|
||||||
|
|
||||||
@SuppressWarnings("unused") // configured in application.yml
|
|
||||||
public class PostgreSQL95CustomDialect extends PostgreSQL95Dialect {
|
|
||||||
|
|
||||||
public PostgreSQL95CustomDialect() {
|
|
||||||
this.registerHibernateType(Types.OTHER, "pg-uuid");
|
|
||||||
this.registerHibernateType(Types.ARRAY, UUIDArrayType.class.getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -0,0 +1,15 @@
|
|||||||
|
package net.hostsharing.hsadminng.config;
|
||||||
|
|
||||||
|
import org.hibernate.dialect.PostgreSQL95Dialect;
|
||||||
|
|
||||||
|
import java.sql.Types;
|
||||||
|
|
||||||
|
@SuppressWarnings("unused") // configured in application.yml
|
||||||
|
public class PostgresCustomDialect extends PostgreSQL95Dialect {
|
||||||
|
|
||||||
|
public PostgresCustomDialect() {
|
||||||
|
this.registerHibernateType(Types.OTHER, "pg-uuid");
|
||||||
|
this.registerHibernateType(Types.ARRAY, "array");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,5 +1,7 @@
|
|||||||
package net.hostsharing.hsadminng.context;
|
package net.hostsharing.hsadminng.context;
|
||||||
|
|
||||||
|
import com.vladmihalcea.hibernate.type.array.StringArrayType;
|
||||||
|
import com.vladmihalcea.hibernate.type.array.UUIDArrayType;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
@ -54,12 +56,12 @@ public class Context {
|
|||||||
final String assumedRoles) {
|
final String assumedRoles) {
|
||||||
final var query = em.createNativeQuery(
|
final var query = em.createNativeQuery(
|
||||||
"""
|
"""
|
||||||
call defineContext(
|
call defineContext(
|
||||||
cast(:currentTask as varchar),
|
cast(:currentTask as varchar),
|
||||||
cast(:currentRequest as varchar),
|
cast(:currentRequest as varchar),
|
||||||
cast(:currentUser as varchar),
|
cast(:currentUser as varchar),
|
||||||
cast(:assumedRoles as varchar));
|
cast(:assumedRoles as varchar));
|
||||||
""");
|
""");
|
||||||
query.setParameter("currentTask", shortenToMaxLength(currentTask, 96));
|
query.setParameter("currentTask", shortenToMaxLength(currentTask, 96));
|
||||||
query.setParameter("currentRequest", shortenToMaxLength(currentRequest, 512)); // TODO.SPEC: length?
|
query.setParameter("currentRequest", shortenToMaxLength(currentRequest, 512)); // TODO.SPEC: length?
|
||||||
query.setParameter("currentUser", currentUser);
|
query.setParameter("currentUser", currentUser);
|
||||||
@ -80,11 +82,17 @@ public class Context {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String[] getAssumedRoles() {
|
public String[] getAssumedRoles() {
|
||||||
return (String[]) em.createNativeQuery("select assumedRoles()").getSingleResult();
|
return (String[]) em.createNativeQuery("select assumedRoles() as roles")
|
||||||
|
.unwrap(org.hibernate.query.NativeQuery.class)
|
||||||
|
.addScalar("roles", StringArrayType.INSTANCE)
|
||||||
|
.getSingleResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
public UUID[] currentSubjectsUuids() {
|
public UUID[] currentSubjectsUuids() {
|
||||||
return (UUID[]) em.createNativeQuery("select currentSubjectsUuids()").getSingleResult();
|
return (UUID[]) em.createNativeQuery("select currentSubjectsUuids() as uuids")
|
||||||
|
.unwrap(org.hibernate.query.NativeQuery.class)
|
||||||
|
.addScalar("uuids", UUIDArrayType.INSTANCE) // TODO.BLOG
|
||||||
|
.getSingleResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String getCallerMethodNameFromStack() {
|
private static String getCallerMethodNameFromStack() {
|
||||||
|
@ -16,7 +16,7 @@ spring:
|
|||||||
jpa:
|
jpa:
|
||||||
properties:
|
properties:
|
||||||
hibernate:
|
hibernate:
|
||||||
dialect: net.hostsharing.hsadminng.config.PostgreSQL95CustomDialect
|
dialect: net.hostsharing.hsadminng.config.PostgresCustomDialect
|
||||||
|
|
||||||
liquibase:
|
liquibase:
|
||||||
contexts: dev
|
contexts: dev
|
||||||
|
@ -28,6 +28,7 @@ create or replace function determineCurrentSubjectsUuids(currentUserUuid uuid, a
|
|||||||
language plpgsql as $$
|
language plpgsql as $$
|
||||||
declare
|
declare
|
||||||
roleName varchar(63);
|
roleName varchar(63);
|
||||||
|
roleNameParts varchar(63);
|
||||||
objectTableToAssume varchar(63);
|
objectTableToAssume varchar(63);
|
||||||
objectNameToAssume varchar(63);
|
objectNameToAssume varchar(63);
|
||||||
objectUuidToAssume uuid;
|
objectUuidToAssume uuid;
|
||||||
@ -48,10 +49,10 @@ begin
|
|||||||
|
|
||||||
foreach roleName in array string_to_array(assumedRoles, ';')
|
foreach roleName in array string_to_array(assumedRoles, ';')
|
||||||
loop
|
loop
|
||||||
roleName = overlay(roleName placing '#' from length(roleName) + 1 - strpos(reverse(roleName), '.'));
|
roleNameParts = overlay(roleName placing '#' from length(roleName) + 1 - strpos(reverse(roleName), '.'));
|
||||||
objectTableToAssume = split_part(roleName, '#', 1);
|
objectTableToAssume = split_part(roleNameParts, '#', 1);
|
||||||
objectNameToAssume = split_part(roleName, '#', 2);
|
objectNameToAssume = split_part(roleNameParts, '#', 2);
|
||||||
roleTypeToAssume = split_part(roleName, '#', 3);
|
roleTypeToAssume = split_part(roleNameParts, '#', 3);
|
||||||
|
|
||||||
objectUuidToAssume = findObjectUuidByIdName(objectTableToAssume, objectNameToAssume);
|
objectUuidToAssume = findObjectUuidByIdName(objectTableToAssume, objectNameToAssume);
|
||||||
|
|
||||||
@ -60,7 +61,10 @@ begin
|
|||||||
where r.objectUuid = objectUuidToAssume
|
where r.objectUuid = objectUuidToAssume
|
||||||
and r.roleType = roleTypeToAssume
|
and r.roleType = roleTypeToAssume
|
||||||
into roleUuidToAssume;
|
into roleUuidToAssume;
|
||||||
if (not isGranted(currentUserUuid, roleUuidToAssume)) then
|
if roleUuidToAssume is null then
|
||||||
|
raise exception '[403] role % not accessible for user %', roleName, currentUser();
|
||||||
|
end if;
|
||||||
|
if not isGranted(currentUserUuid, roleUuidToAssume) then
|
||||||
raise exception '[403] user % has no permission to assume role %', currentUser(), roleName;
|
raise exception '[403] user % has no permission to assume role %', currentUser(), roleName;
|
||||||
end if;
|
end if;
|
||||||
roleIdsToAssume := roleIdsToAssume || roleUuidToAssume;
|
roleIdsToAssume := roleIdsToAssume || roleUuidToAssume;
|
||||||
@ -160,7 +164,7 @@ begin
|
|||||||
if (currentSubjectsUuids is null or length(currentSubjectsUuids) = 0 ) then
|
if (currentSubjectsUuids is null or length(currentSubjectsUuids) = 0 ) then
|
||||||
currentUserName := currentUser();
|
currentUserName := currentUser();
|
||||||
if (length(currentUserName) > 0) then
|
if (length(currentUserName) > 0) then
|
||||||
raise exception '[401] currentUserUuid cannot be determined, unknown user name "%"', currentUserName;
|
raise exception '[401] currentSubjectsUuids (%) cannot be determined, unknown user name "%"', currentSubjectsUuids, currentUserName;
|
||||||
else
|
else
|
||||||
raise exception '[401] currentSubjectsUuids cannot be determined, please call `defineContext(...)` first;"';
|
raise exception '[401] currentSubjectsUuids cannot be determined, please call `defineContext(...)` first;"';
|
||||||
end if;
|
end if;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package net.hostsharing.hsadminng.context;
|
package net.hostsharing.hsadminng.context;
|
||||||
|
|
||||||
|
import net.hostsharing.test.Array;
|
||||||
import net.hostsharing.test.JpaAttempt;
|
import net.hostsharing.test.JpaAttempt;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@ -32,23 +33,26 @@ class ContextIntegrationTests {
|
|||||||
|
|
||||||
context.define("mike@hostsharing.net", null);
|
context.define("mike@hostsharing.net", null);
|
||||||
|
|
||||||
final var currentTask = context.getCurrentTask();
|
assertThat(context.getCurrentTask())
|
||||||
assertThat(currentTask).isEqualTo("ContextIntegrationTests.defineWithoutHttpServletRequestUsesCallStack");
|
.isEqualTo("ContextIntegrationTests.defineWithoutHttpServletRequestUsesCallStack");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Transactional
|
@Transactional
|
||||||
void defineWithCurrentUserButWithoutAssumedRoles() {
|
void defineWithCurrentUserButWithoutAssumedRoles() {
|
||||||
|
// when
|
||||||
context.define("mike@hostsharing.net");
|
context.define("mike@hostsharing.net");
|
||||||
|
|
||||||
final var currentUser = context.getCurrentUser();
|
// then
|
||||||
assertThat(currentUser).isEqualTo("mike@hostsharing.net");
|
assertThat(context.getCurrentUser()).
|
||||||
|
isEqualTo("mike@hostsharing.net");
|
||||||
|
|
||||||
final var currentUserUuid = context.getCurrentUserUUid();
|
assertThat(context.getCurrentUserUUid()).isNotNull();
|
||||||
assertThat(currentUserUuid).isNotNull();
|
|
||||||
|
|
||||||
final var assumedRoleUuids = context.currentSubjectsUuids();
|
assertThat(context.getAssumedRoles()).isEmpty();
|
||||||
assertThat(assumedRoleUuids).containsExactly(currentUserUuid);
|
|
||||||
|
assertThat(context.currentSubjectsUuids())
|
||||||
|
.containsExactly(context.getCurrentUserUUid());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -80,13 +84,17 @@ class ContextIntegrationTests {
|
|||||||
@Test
|
@Test
|
||||||
@Transactional
|
@Transactional
|
||||||
void defineWithCurrentUserAndAssumedRoles() {
|
void defineWithCurrentUserAndAssumedRoles() {
|
||||||
|
// given
|
||||||
context.define("mike@hostsharing.net", "customer#xxx.owner;customer#yyy.owner");
|
context.define("mike@hostsharing.net", "customer#xxx.owner;customer#yyy.owner");
|
||||||
|
|
||||||
|
// when
|
||||||
final var currentUser = context.getCurrentUser();
|
final var currentUser = context.getCurrentUser();
|
||||||
assertThat(currentUser).isEqualTo("mike@hostsharing.net");
|
assertThat(currentUser).isEqualTo("mike@hostsharing.net");
|
||||||
|
|
||||||
final var assumedRoles = context.currentSubjectsUuids();
|
// then
|
||||||
assertThat(assumedRoles).hasSize(2);
|
assertThat(context.getAssumedRoles())
|
||||||
|
.isEqualTo(Array.of("customer#xxx.owner", "customer#yyy.owner"));
|
||||||
|
assertThat(context.currentSubjectsUuids()).hasSize(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -99,6 +107,6 @@ class ContextIntegrationTests {
|
|||||||
// then
|
// then
|
||||||
result.assertExceptionWithRootCauseMessage(
|
result.assertExceptionWithRootCauseMessage(
|
||||||
javax.persistence.PersistenceException.class,
|
javax.persistence.PersistenceException.class,
|
||||||
"ERROR: [403] user customer-admin@xxx.example.com has no permission to assume role package#yyy00#admin");
|
"ERROR: [403] user customer-admin@xxx.example.com has no permission to assume role package#yyy00.admin");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import io.restassured.http.ContentType;
|
|||||||
import io.restassured.response.ValidatableResponse;
|
import io.restassured.response.ValidatableResponse;
|
||||||
import net.hostsharing.hsadminng.Accepts;
|
import net.hostsharing.hsadminng.Accepts;
|
||||||
import net.hostsharing.hsadminng.HsadminNgApplication;
|
import net.hostsharing.hsadminng.HsadminNgApplication;
|
||||||
|
import net.hostsharing.hsadminng.context.Context;
|
||||||
import net.hostsharing.hsadminng.context.ContextBasedTest;
|
import net.hostsharing.hsadminng.context.ContextBasedTest;
|
||||||
import net.hostsharing.hsadminng.rbac.rbacrole.RbacRoleEntity;
|
import net.hostsharing.hsadminng.rbac.rbacrole.RbacRoleEntity;
|
||||||
import net.hostsharing.hsadminng.rbac.rbacrole.RbacRoleRepository;
|
import net.hostsharing.hsadminng.rbac.rbacrole.RbacRoleRepository;
|
||||||
@ -203,7 +204,7 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Accepts({ "GRT:R(Read)", "GRT:X(Access Control)" })
|
@Accepts({ "GRT:R(Read)", "GRT:X(Access Control)" })
|
||||||
void packageAdmin_withAssumedPackageAdmin_canNotReadItsOwnGrantByIdAnymore() {
|
void packageAdmin_withAssumedPackageAdmin_canStillReadItsOwnGrantById() {
|
||||||
// given
|
// given
|
||||||
final var givenCurrentUserAsPackageAdmin = new Subject(
|
final var givenCurrentUserAsPackageAdmin = new Subject(
|
||||||
"pac-admin-xxx00@xxx.example.com",
|
"pac-admin-xxx00@xxx.example.com",
|
||||||
@ -225,21 +226,20 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Accepts({ "GRT:R(Read)", "GRT:X(Access Control)" })
|
@Accepts({ "GRT:R(Read)", "GRT:X(Access Control)" })
|
||||||
void packageAdmin_withAssumedUnixUserAdmin_canNotReadItsOwnGrantByIdAnymore() {
|
void packageAdmin_withAssumedPackageTenantRole_canNotReadItsOwnGrantByIdAnymore() {
|
||||||
|
|
||||||
// given
|
// given
|
||||||
final var givenCurrentUserAsPackageAdmin = new Subject(
|
final var givenCurrentUserAsPackageAdmin = new Subject(
|
||||||
"pac-admin-xxx00@xxx.example.com",
|
"pac-admin-xxx00@xxx.example.com",
|
||||||
"unixuser#xxx00-xxxa.admin");
|
"package#xxx00.tenant");
|
||||||
final var givenGranteeUser = findRbacUserByName("pac-admin-xxx00@xxx.example.com");
|
final var givenGranteeUser = findRbacUserByName("pac-admin-xxx00@xxx.example.com");
|
||||||
final var givenGrantedRole = findRbacRoleByName("package#xxx00.admin");
|
final var givenGrantedRole = findRbacRoleByName("package#xxx00.admin");
|
||||||
|
|
||||||
// when
|
|
||||||
final var grant = givenCurrentUserAsPackageAdmin.getGrantById()
|
final var grant = givenCurrentUserAsPackageAdmin.getGrantById()
|
||||||
.forGrantedRole(givenGrantedRole).toGranteeUser(givenGranteeUser);
|
.forGrantedRole(givenGrantedRole).toGranteeUser(givenGranteeUser);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
grant.assertThat()
|
grant.assertThat()
|
||||||
.statusCode(401);
|
.statusCode(404);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -453,8 +453,6 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
|
|||||||
|
|
||||||
private Subject currentSubject = Subject.this;
|
private Subject currentSubject = Subject.this;
|
||||||
private RbacRoleEntity grantedRole;
|
private RbacRoleEntity grantedRole;
|
||||||
private boolean assumed;
|
|
||||||
private RbacUserEntity granteeUser;
|
|
||||||
|
|
||||||
GetGrantByIdFixture forGrantedRole(final RbacRoleEntity grantedRole) {
|
GetGrantByIdFixture forGrantedRole(final RbacRoleEntity grantedRole) {
|
||||||
this.grantedRole = grantedRole;
|
this.grantedRole = grantedRole;
|
||||||
@ -462,7 +460,6 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ValidatableResponse toGranteeUser(final RbacUserEntity granteeUser) {
|
ValidatableResponse toGranteeUser(final RbacUserEntity granteeUser) {
|
||||||
this.granteeUser = granteeUser;
|
|
||||||
|
|
||||||
return RestAssured // @formatter:ff
|
return RestAssured // @formatter:ff
|
||||||
.given()
|
.given()
|
||||||
@ -473,7 +470,8 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
|
|||||||
.get("http://localhost/api/rbac-grants/%s/%s".formatted(
|
.get("http://localhost/api/rbac-grants/%s/%s".formatted(
|
||||||
grantedRole.getUuid(), granteeUser.getUuid()
|
grantedRole.getUuid(), granteeUser.getUuid()
|
||||||
))
|
))
|
||||||
.then().log().all(); // @formatter:on
|
.then().log().all();
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -147,7 +147,7 @@ class RbacRoleRepositoryIntegrationTest {
|
|||||||
|
|
||||||
result.assertExceptionWithRootCauseMessage(
|
result.assertExceptionWithRootCauseMessage(
|
||||||
JpaSystemException.class,
|
JpaSystemException.class,
|
||||||
"[401] currentUserUuid cannot be determined, unknown user name \"unknown@example.org\"");
|
"[401] currentSubjectsUuids () cannot be determined, unknown user name \"unknown@example.org\"");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ spring:
|
|||||||
properties:
|
properties:
|
||||||
hibernate:
|
hibernate:
|
||||||
default_schema: public
|
default_schema: public
|
||||||
dialect: net.hostsharing.hsadminng.config.PostgreSQL95CustomDialect
|
dialect: net.hostsharing.hsadminng.config.PostgresCustomDialect
|
||||||
format_sql: false
|
format_sql: false
|
||||||
hibernate:
|
hibernate:
|
||||||
ddl-auto: none
|
ddl-auto: none
|
||||||
|
Loading…
Reference in New Issue
Block a user