fix Debitor RBAC system

This commit is contained in:
Michael Hoennig 2024-03-08 16:04:58 +01:00
parent 7fab1186ed
commit c2ad5a7e28
13 changed files with 64 additions and 126 deletions

View File

@ -62,7 +62,6 @@ public class HsOfficeDebitorController implements HsOfficeDebitorsApi {
final var entityToSave = mapper.map(body, HsOfficeDebitorEntity.class);
final var saved = debitorRepo.save(entityToSave);
em.flush(); // FIXME: remove
final var uri =
MvcUriComponentsBuilder.fromController(getClass())

View File

@ -86,29 +86,6 @@ create or replace function findRbacUserId(userName varchar)
language sql as $$
select uuid from RbacUser where name = userName
$$;
create type RbacWhenNotExists as enum ('fail', 'create');
create or replace function getRbacUserId(userName varchar, whenNotExists RbacWhenNotExists)
returns uuid
returns null on null input
language plpgsql as $$
declare
userUuid uuid;
begin
userUuid = findRbacUserId(userName);
if (userUuid is null) then
if (whenNotExists = 'fail') then
raise exception 'RbacUser with name="%" not found', userName;
end if;
if (whenNotExists = 'create') then
userUuid = createRbacUser(userName);
end if;
end if;
return userUuid;
end;
$$;
--//
-- ============================================================================
@ -293,24 +270,18 @@ create or replace function findRoleId(roleDescriptor RbacRoleDescriptor)
select uuid from RbacRole where objectUuid = roleDescriptor.objectUuid and roleType = roleDescriptor.roleType;
$$;
create or replace function getRoleId(roleDescriptor RbacRoleDescriptor, whenNotExists RbacWhenNotExists)
create or replace function getRoleId(roleDescriptor RbacRoleDescriptor)
returns uuid
returns null on null input
language plpgsql as $$
declare
roleUuid uuid;
begin
assert roleDescriptor is not null, 'roleDescriptor must not be null';
roleUuid := findRoleId(roleDescriptor);
assert roleUuid is not null, 'roleUuid must not be null'; -- FIXME: remove
if (roleUuid is null) then
if (whenNotExists = 'fail') then
raise exception 'RbacRole "%#%.%" not found', roleDescriptor.objectTable, roleDescriptor.objectUuid, roleDescriptor.roleType;
end if;
if (whenNotExists = 'create') then
roleUuid := createRole(roleDescriptor);
end if;
raise exception 'RbacRole "%#%.%" not found', roleDescriptor.objectTable, roleDescriptor.objectUuid, roleDescriptor.roleType;
end if;
assert roleUuid is not null, 'roleUuid must not be null'; -- FIXME: remove
return roleUuid;
end;
$$;

View File

@ -45,16 +45,15 @@ begin
call grantPermissionsToRole(roleUuid, toPermissionUuids(roleDescriptor.objectuuid, permissions));
end if;
foreach superRoleDesc in array incomingSuperRoles
foreach superRoleDesc in array array_remove(incomingSuperRoles, null)
loop
superRoleUuid := getRoleId(superRoleDesc, 'fail');
superRoleUuid := getRoleId(superRoleDesc);
call grantRoleToRole(roleUuid, superRoleUuid, superRoleDesc.assumed);
end loop;
foreach subRoleDesc in array outgoingSubRoles
foreach subRoleDesc in array array_remove(outgoingSubRoles, null)
loop
subRoleUuid := getRoleId(subRoleDesc, 'fail');
assert subRoleUuid is not null, 'subRoleUuid must not be null'; -- FIXME: remove
subRoleUuid := getRoleId(subRoleDesc);
call grantRoleToRole(subRoleUuid, roleUuid, subRoleDesc.assumed);
end loop;
@ -62,7 +61,7 @@ begin
if grantedByRole is null then
grantedByRoleUuid := roleUuid;
else
grantedByRoleUuid := getRoleId(grantedByRole, 'fail');
grantedByRoleUuid := getRoleId(grantedByRole);
end if;
foreach userUuid in array userUuids
loop

View File

@ -46,8 +46,8 @@ begin
select * into newCust
from test_customer where reference=custReference;
call grantRoleToUser(
getRoleId(testCustomerOwner(newCust), 'fail'),
getRoleId(testCustomerAdmin(newCust), 'fail'),
getRoleId(testCustomerOwner(newCust)),
getRoleId(testCustomerAdmin(newCust)),
custAdminUuid,
true);
end; $$;

View File

@ -35,7 +35,7 @@ begin
returning * into pac;
call grantRoleToUser(
getRoleId(testCustomerAdmin(cust), 'fail'),
getRoleId(testCustomerAdmin(cust)),
findRoleId(testPackageAdmin(pac)),
createRbacUser('pac-admin-' || pacName || '@' || cust.prefix || '.example.com'),
true);

View File

@ -98,12 +98,12 @@ begin
--Attention: Cannot be in partner-details because of insert order (partner is not in database yet)
call grantPermissionsToRole(
getRoleId(hsOfficePartnerOwner(NEW), 'fail'),
getRoleId(hsOfficePartnerOwner(NEW)),
createPermissions(NEW.detailsUuid, array ['DELETE'])
);
call grantPermissionsToRole(
getRoleId(hsOfficePartnerAdmin(NEW), 'fail'),
getRoleId(hsOfficePartnerAdmin(NEW)),
createPermissions(NEW.detailsUuid, array ['UPDATE'])
);
@ -111,7 +111,7 @@ begin
-- Yes, here hsOfficePartnerAGENT is used, not hsOfficePartnerTENANT.
-- Do NOT grant view permission on partner-details to hsOfficePartnerTENANT!
-- Otherwise package-admins etc. would be able to read the data.
getRoleId(hsOfficePartnerAgent(NEW), 'fail'),
getRoleId(hsOfficePartnerAgent(NEW)),
createPermissions(NEW.detailsUuid, array ['SELECT'])
);

View File

@ -42,7 +42,7 @@ begin
-- coopsharestransactions cannot be edited nor deleted, just created+viewed
call grantPermissionsToRole(
getRoleId(hsOfficeMembershipTenant(newHsOfficeMembership), 'fail'),
getRoleId(hsOfficeMembershipTenant(newHsOfficeMembership)),
createPermissions(NEW.uuid, array ['SELECT'])
);

View File

@ -42,7 +42,7 @@ begin
-- coopassetstransactions cannot be edited nor deleted, just created+viewed
call grantPermissionsToRole(
getRoleId(hsOfficeMembershipTenant(newHsOfficeMembership), 'fail'),
getRoleId(hsOfficeMembershipTenant(newHsOfficeMembership)),
createPermissions(NEW.uuid, array ['SELECT'])
);

View File

@ -49,6 +49,8 @@ public class ArchitectureTest {
"..rbac.rbacuser",
"..rbac.rbacgrant",
"..rbac.rbacrole",
"..rbac.rbacobject",
"..rbac.rbacdef",
"..stringify"
// ATTENTION: Don't simply add packages here, also add arch rules for the new package!
);
@ -116,7 +118,10 @@ public class ArchitectureTest {
public static final ArchRule hsAdminPackagesRule = classes()
.that().resideInAPackage("..hs.office.(*)..")
.should().onlyBeAccessed().byClassesThat()
.resideInAnyPackage("..hs.office.(*)..");
.resideInAnyPackage(
"..hs.office.(*)..",
"..rbac.rbacgrant" // TODO: just because of RbacGrantsDiagramServiceIntegrationTest
);
@ArchTest
@SuppressWarnings("unused")

View File

@ -119,7 +119,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
// then
System.out.println("ok");
// result.assertExceptionWithRootCauseMessage(org.hibernate.exception.ConstraintViolationException.class);
result.assertExceptionWithRootCauseMessage(org.hibernate.exception.ConstraintViolationException.class);
}
@Test
@ -167,12 +167,12 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
.containsExactlyInAnyOrder(Array.fromFormatted(
initialGrantNames,
// owner
"{ grant perm * on debitor#1000422:FeG to role debitor#1000422:FeG.owner by system and assume }",
"{ grant perm DELETE on debitor#1000422:FeG to role debitor#1000422:FeG.owner by system and assume }",
"{ grant role debitor#1000422:FeG.owner to role global#global.admin by system and assume }",
"{ grant role debitor#1000422:FeG.owner to user superuser-alex by global#global.admin and assume }",
// admin
"{ grant perm edit on debitor#1000422:FeG to role debitor#1000422:FeG.admin by system and assume }",
"{ grant perm UPDATE on debitor#1000422:FeG to role debitor#1000422:FeG.admin by system and assume }",
"{ grant role debitor#1000422:FeG.admin to role debitor#1000422:FeG.owner by system and assume }",
// agent
@ -187,7 +187,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
"{ grant role partner#10004:FeG.tenant to role debitor#1000422:FeG.tenant by system and assume }",
// guest
"{ grant perm view on debitor#1000422:FeG to role debitor#1000422:FeG.guest by system and assume }",
"{ grant perm SELECT on debitor#1000422:FeG to role debitor#1000422:FeG.guest by system and assume }",
"{ grant role debitor#1000422:FeG.guest to role debitor#1000422:FeG.tenant by system and assume }",
null));

View File

@ -4,7 +4,10 @@ import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.hs.office.test.ContextBasedTestWithCleanup;
import net.hostsharing.hsadminng.rbac.rbacgrant.RbacGrantsDiagramService.Include;
import net.hostsharing.test.JpaAttempt;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.mock.mockito.MockBean;
@ -28,6 +31,27 @@ class RbacGrantsDiagramServiceIntegrationTest extends ContextBasedTestWithCleanu
@MockBean
HttpServletRequest request;
@Autowired
Context context;
@Autowired
RbacGrantsDiagramService diagramService;
TestInfo test;
@BeforeEach
void init(TestInfo testInfo) {
this.test = testInfo;
}
protected void context(final String currentUser, final String assumedRoles) {
context.define(test.getDisplayName(), null, currentUser, assumedRoles);
}
protected void context(final String currentUser) {
context(currentUser, null);
}
@Test
void allGrantsToCurrentUser() {
context("superuser-alex@hostsharing.net", "test_domain#xxx00-aaaa.owner");
@ -36,27 +60,9 @@ class RbacGrantsDiagramServiceIntegrationTest extends ContextBasedTestWithCleanu
assertThat(graph).isEqualTo("""
flowchart TB
role:test_package#xxx00.tenant[
test_package
xxx00.t
tenant] --> role:test_customer#xxx.tenant[
test_customer
xxx.t
tenant]
role:test_domain#xxx00-aaaa.owner[
test_domain
xxx00-aaaa.o
owner] --> role:test_domain#xxx00-aaaa.admin[
test_domain
xxx00-aaaa.a
admin]
role:test_domain#xxx00-aaaa.admin[
test_domain
xxx00-aaaa.a
admin] --> role:test_package#xxx00.tenant[
test_package
xxx00.t
tenant]
role:test_domain#xxx00-aaaa.admin --> role:test_package#xxx00.tenant
role:test_domain#xxx00-aaaa.owner --> role:test_domain#xxx00-aaaa.admin
role:test_package#xxx00.tenant --> role:test_customer#xxx.tenant
""".trim());
}
@ -68,60 +74,18 @@ class RbacGrantsDiagramServiceIntegrationTest extends ContextBasedTestWithCleanu
assertThat(graph).isEqualTo("""
flowchart TB
role:test_domain#xxx00-aaaa.owner[
test_domain
xxx00-aaaa.o
owner] --> perm:*:on:test_domain#xxx00-aaaa{{
test_domain
xxx00-aaaa
*}}
role:test_customer#xxx.tenant[
test_customer
xxx.t
tenant] --> perm:view:on:test_customer#xxx{{
test_customer
xxx
view}}
role:test_domain#xxx00-aaaa.admin[
test_domain
xxx00-aaaa.a
admin] --> perm:edit:on:test_domain#xxx00-aaaa{{
test_domain
xxx00-aaaa
edit}}
role:test_package#xxx00.tenant[
test_package
xxx00.t
tenant] --> role:test_customer#xxx.tenant[
test_customer
xxx.t
tenant]
role:test_domain#xxx00-aaaa.owner[
test_domain
xxx00-aaaa.o
owner] --> role:test_domain#xxx00-aaaa.admin[
test_domain
xxx00-aaaa.a
admin]
role:test_package#xxx00.tenant[
test_package
xxx00.t
tenant] --> perm:view:on:test_package#xxx00{{
test_package
xxx00
view}}
role:test_domain#xxx00-aaaa.admin[
test_domain
xxx00-aaaa.a
admin] --> role:test_package#xxx00.tenant[
test_package
xxx00.t
tenant]
role:test_customer#xxx.tenant --> perm:SELECT:on:test_customer#xxx
role:test_domain#xxx00-aaaa.admin --> perm:UPDATE:on:test_domain#xxx00-aaaa
role:test_domain#xxx00-aaaa.admin --> role:test_package#xxx00.tenant
role:test_domain#xxx00-aaaa.owner --> perm:DELETE:on:test_domain#xxx00-aaaa
role:test_domain#xxx00-aaaa.owner --> role:test_domain#xxx00-aaaa.admin
role:test_package#xxx00.tenant --> perm:SELECT:on:test_package#xxx00
role:test_package#xxx00.tenant --> role:test_customer#xxx.tenant
""".trim());
}
@Test
// @Disabled
@Disabled // enable to generate from a real database
void print() throws IOException {
//context("superuser-alex@hostsharing.net", "hs_office_person#FirbySusan.admin");
context("superuser-alex@hostsharing.net");

View File

@ -5,7 +5,7 @@ import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
class TestCustomerEntityTest {
class TestCustomerEntityUnitTest {
@Test
void definesRbac() {

View File

@ -5,7 +5,7 @@ import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
class TestPackageEntityTest {
class TestPackageEntityUnitTest {
@Test
void definesRbac() {