RBAC Diagram+PostgreSQL Generator #21

Merged
hsh-michaelhoennig merged 54 commits from experimental-rbacview-generator into master 2024-03-11 12:30:44 +01:00
13 changed files with 64 additions and 126 deletions
Showing only changes of commit c2ad5a7e28 - Show all commits

View File

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

View File

@ -86,29 +86,6 @@ create or replace function findRbacUserId(userName varchar)
language sql as $$ language sql as $$
select uuid from RbacUser where name = userName 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; 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 uuid
returns null on null input
language plpgsql as $$ language plpgsql as $$
declare declare
roleUuid uuid; roleUuid uuid;
begin begin
assert roleDescriptor is not null, 'roleDescriptor must not be null';
roleUuid := findRoleId(roleDescriptor); roleUuid := findRoleId(roleDescriptor);
assert roleUuid is not null, 'roleUuid must not be null'; -- FIXME: remove
if (roleUuid is null) then if (roleUuid is null) then
if (whenNotExists = 'fail') then raise exception 'RbacRole "%#%.%" not found', roleDescriptor.objectTable, roleDescriptor.objectUuid, roleDescriptor.roleType;
raise exception 'RbacRole "%#%.%" not found', roleDescriptor.objectTable, roleDescriptor.objectUuid, roleDescriptor.roleType;
end if;
if (whenNotExists = 'create') then
roleUuid := createRole(roleDescriptor);
end if;
end if; end if;
assert roleUuid is not null, 'roleUuid must not be null'; -- FIXME: remove
return roleUuid; return roleUuid;
end; end;
$$; $$;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -119,7 +119,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
// then // then
System.out.println("ok"); System.out.println("ok");
// result.assertExceptionWithRootCauseMessage(org.hibernate.exception.ConstraintViolationException.class); result.assertExceptionWithRootCauseMessage(org.hibernate.exception.ConstraintViolationException.class);
} }
@Test @Test
@ -167,12 +167,12 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
.containsExactlyInAnyOrder(Array.fromFormatted( .containsExactlyInAnyOrder(Array.fromFormatted(
initialGrantNames, initialGrantNames,
// owner // 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 role global#global.admin by system and assume }",
"{ grant role debitor#1000422:FeG.owner to user superuser-alex by global#global.admin and assume }", "{ grant role debitor#1000422:FeG.owner to user superuser-alex by global#global.admin and assume }",
// admin // 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 }", "{ grant role debitor#1000422:FeG.admin to role debitor#1000422:FeG.owner by system and assume }",
// agent // 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 }", "{ grant role partner#10004:FeG.tenant to role debitor#1000422:FeG.tenant by system and assume }",
// guest // 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 }", "{ grant role debitor#1000422:FeG.guest to role debitor#1000422:FeG.tenant by system and assume }",
null)); 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.hs.office.test.ContextBasedTestWithCleanup;
import net.hostsharing.hsadminng.rbac.rbacgrant.RbacGrantsDiagramService.Include; import net.hostsharing.hsadminng.rbac.rbacgrant.RbacGrantsDiagramService.Include;
import net.hostsharing.test.JpaAttempt; 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.Test;
import org.junit.jupiter.api.TestInfo;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.boot.test.mock.mockito.MockBean;
@ -28,6 +31,27 @@ class RbacGrantsDiagramServiceIntegrationTest extends ContextBasedTestWithCleanu
@MockBean @MockBean
HttpServletRequest request; 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 @Test
void allGrantsToCurrentUser() { void allGrantsToCurrentUser() {
context("superuser-alex@hostsharing.net", "test_domain#xxx00-aaaa.owner"); context("superuser-alex@hostsharing.net", "test_domain#xxx00-aaaa.owner");
@ -36,27 +60,9 @@ class RbacGrantsDiagramServiceIntegrationTest extends ContextBasedTestWithCleanu
assertThat(graph).isEqualTo(""" assertThat(graph).isEqualTo("""
flowchart TB flowchart TB
role:test_package#xxx00.tenant[ role:test_domain#xxx00-aaaa.admin --> role:test_package#xxx00.tenant
test_package role:test_domain#xxx00-aaaa.owner --> role:test_domain#xxx00-aaaa.admin
xxx00.t role:test_package#xxx00.tenant --> role:test_customer#xxx.tenant
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]
""".trim()); """.trim());
} }
@ -68,60 +74,18 @@ class RbacGrantsDiagramServiceIntegrationTest extends ContextBasedTestWithCleanu
assertThat(graph).isEqualTo(""" assertThat(graph).isEqualTo("""
flowchart TB flowchart TB
role:test_domain#xxx00-aaaa.owner[ role:test_customer#xxx.tenant --> perm:SELECT:on:test_customer#xxx
test_domain role:test_domain#xxx00-aaaa.admin --> perm:UPDATE:on:test_domain#xxx00-aaaa
xxx00-aaaa.o role:test_domain#xxx00-aaaa.admin --> role:test_package#xxx00.tenant
owner] --> perm:*:on:test_domain#xxx00-aaaa{{ role:test_domain#xxx00-aaaa.owner --> perm:DELETE:on:test_domain#xxx00-aaaa
test_domain role:test_domain#xxx00-aaaa.owner --> role:test_domain#xxx00-aaaa.admin
xxx00-aaaa role:test_package#xxx00.tenant --> perm:SELECT:on:test_package#xxx00
*}} role:test_package#xxx00.tenant --> role:test_customer#xxx.tenant
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]
""".trim()); """.trim());
} }
@Test @Test
// @Disabled @Disabled // enable to generate from a real database
void print() throws IOException { void print() throws IOException {
//context("superuser-alex@hostsharing.net", "hs_office_person#FirbySusan.admin"); //context("superuser-alex@hostsharing.net", "hs_office_person#FirbySusan.admin");
context("superuser-alex@hostsharing.net"); 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; import static org.assertj.core.api.Assertions.assertThat;
class TestCustomerEntityTest { class TestCustomerEntityUnitTest {
@Test @Test
void definesRbac() { void definesRbac() {

View File

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