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
15 changed files with 119 additions and 23 deletions
Showing only changes of commit 7aa158e406 - Show all commits

View File

@ -264,7 +264,7 @@ public class RbacView {
} }
public void generateWithBaseFileName(final String baseFileName) { public void generateWithBaseFileName(final String baseFileName) {
new RbacViewMermaidFlowchart(this).generateToMarkdownFile(Path.of(OUTPUT_BASEDIR, baseFileName + ".md")); new RbacViewMermaidFlowchartGenerator(this).generateToMarkdownFile(Path.of(OUTPUT_BASEDIR, baseFileName + ".md"));
new RbacViewPostgresGenerator(this).generateToChangeLog(Path.of(OUTPUT_BASEDIR, baseFileName + ".sql")); new RbacViewPostgresGenerator(this).generateToChangeLog(Path.of(OUTPUT_BASEDIR, baseFileName + ".sql"));
} }

View File

@ -9,7 +9,7 @@ import java.time.LocalDateTime;
import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.joining;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacGrantDefinition.GrantType.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacGrantDefinition.GrantType.*;
public class RbacViewMermaidFlowchart { public class RbacViewMermaidFlowchartGenerator {
public static final String HOSTSHARING_DARK_ORANGE = "#dd4901"; public static final String HOSTSHARING_DARK_ORANGE = "#dd4901";
public static final String HOSTSHARING_LIGHT_ORANGE = "#feb28c"; public static final String HOSTSHARING_LIGHT_ORANGE = "#feb28c";
@ -18,7 +18,7 @@ public class RbacViewMermaidFlowchart {
private final RbacView rbacDef; private final RbacView rbacDef;
private final StringWriter flowchart = new StringWriter(); private final StringWriter flowchart = new StringWriter();
public RbacViewMermaidFlowchart(final RbacView rbacDef) { public RbacViewMermaidFlowchartGenerator(final RbacView rbacDef) {
this.rbacDef = rbacDef; this.rbacDef = rbacDef;
flowchart.writeLn(""" flowchart.writeLn("""
%%{init:{'flowchart':{'htmlLabels':false}}}%% %%{init:{'flowchart':{'htmlLabels':false}}}%%
@ -147,7 +147,9 @@ public class RbacViewMermaidFlowchart {
Files.writeString( Files.writeString(
path, path,
""" """
### rbac %{entityAlias} %{timestamp} ### rbac %{entityAlias}
This code generated was by RbacViewMermaidFlowchartGenerator at %{timestamp}.
```mermaid ```mermaid
%{flowchart} %{flowchart}

View File

@ -24,23 +24,26 @@ end; $$;
*/ */
create or replace procedure defineContext( create or replace procedure defineContext(
currentTask varchar(96), currentTask varchar(96),
currentRequest varchar(512) = null, currentRequest text = null,
currentUser varchar = null, currentUser varchar(63) = null,
assumedRoles varchar = null assumedRoles varchar(256) = null
) )
language plpgsql as $$ language plpgsql as $$
begin begin
assert length(currentTask) <= 96, 'currentTask must not be longer than 96 characters'; currentTask := coalesce(currentTask, '');
assert length(currentTask) > 8, 'currentTask must be at least 8 characters long'; assert length(currentTask) <= 96, FORMAT('currentTask must not be longer than 96 characters: "%s"', currentTask);
assert length(currentTask) > 8, FORMAT('currentTask must be at least 8 characters long: "%s""', currentTask);
execute format('set local hsadminng.currentTask to %L', currentTask); execute format('set local hsadminng.currentTask to %L', currentTask);
currentRequest := coalesce(currentRequest, ''); currentRequest := coalesce(currentRequest, '');
execute format('set local hsadminng.currentRequest to %L', currentRequest); execute format('set local hsadminng.currentRequest to %L', currentRequest);
currentUser := coalesce(currentUser, ''); currentUser := coalesce(currentUser, '');
assert length(currentUser) <= 63, FORMAT('currentUser must not be longer than 63 characters: "%s"', currentUser);
execute format('set local hsadminng.currentUser to %L', currentUser); execute format('set local hsadminng.currentUser to %L', currentUser);
assumedRoles := coalesce(assumedRoles, ''); assumedRoles := coalesce(assumedRoles, '');
assert length(assumedRoles) <= 256, FORMAT('assumedRoles must not be longer than 256 characters: "%s"', assumedRoles);
execute format('set local hsadminng.assumedRoles to %L', assumedRoles); execute format('set local hsadminng.assumedRoles to %L', assumedRoles);
call contextDefined(currentTask, currentRequest, currentUser, assumedRoles); call contextDefined(currentTask, currentRequest, currentUser, assumedRoles);

View File

@ -27,9 +27,9 @@ create table tx_context
txId bigint not null, txId bigint not null,
txTimestamp timestamp not null, txTimestamp timestamp not null,
currentUser varchar(63) not null, -- not the uuid, because users can be deleted currentUser varchar(63) not null, -- not the uuid, because users can be deleted
assumedRoles varchar not null, -- not the uuids, because roles can be deleted assumedRoles varchar(256) not null, -- not the uuids, because roles can be deleted
currentTask varchar(96) not null, currentTask varchar(96) not null,
currentRequest varchar(512) not null currentRequest text not null
); );
create index on tx_context using brin (txTimestamp); create index on tx_context using brin (txTimestamp);

View File

@ -119,8 +119,6 @@ end; $$;
create or replace procedure revokePermissionFromRole(permissionUuid uuid, superRoleUuid uuid) create or replace procedure revokePermissionFromRole(permissionUuid uuid, superRoleUuid uuid)
language plpgsql as $$ language plpgsql as $$
begin begin
-- TODO: call checkRevokeRoleFromUserPreconditions(grantedByRoleUuid, grantedRoleUuid, userUuid);
raise INFO 'delete from RbacGrants where ascendantUuid = % and descendantUuid = %', superRoleUuid, permissionUuid; raise INFO 'delete from RbacGrants where ascendantUuid = % and descendantUuid = %', superRoleUuid, permissionUuid;
delete from RbacGrants as g delete from RbacGrants as g
where g.ascendantUuid = superRoleUuid and g.descendantUuid = permissionUuid; where g.ascendantUuid = superRoleUuid and g.descendantUuid = permissionUuid;

View File

@ -1,4 +1,6 @@
### rbac customer 2024-03-11T09:06:04.484587070 ### rbac customer
This code generated was by RbacViewMermaidFlowchartGenerator at 2024-03-11T11:29:11.571772062.
```mermaid ```mermaid
%%{init:{'flowchart':{'htmlLabels':false}}}%% %%{init:{'flowchart':{'htmlLabels':false}}}%%

View File

@ -1,5 +1,5 @@
--liquibase formatted sql --liquibase formatted sql
-- This code generated was by RbacViewPostgresGenerator at 2024-03-11T09:06:04.497071201. -- This code generated was by RbacViewPostgresGenerator at 2024-03-11T11:29:11.584886824.
-- ============================================================================ -- ============================================================================
--changeset test-customer-rbac-OBJECT:1 endDelimiter:--// --changeset test-customer-rbac-OBJECT:1 endDelimiter:--//

View File

@ -1,4 +1,6 @@
### rbac package 2024-03-11T09:06:04.536081351 ### rbac package
This code generated was by RbacViewMermaidFlowchartGenerator at 2024-03-11T11:29:11.624847792.
```mermaid ```mermaid
%%{init:{'flowchart':{'htmlLabels':false}}}%% %%{init:{'flowchart':{'htmlLabels':false}}}%%

View File

@ -1,5 +1,5 @@
--liquibase formatted sql --liquibase formatted sql
-- This code generated was by RbacViewPostgresGenerator at 2024-03-11T09:06:04.536525766. -- This code generated was by RbacViewPostgresGenerator at 2024-03-11T11:29:11.625353859.
-- ============================================================================ -- ============================================================================
--changeset test-package-rbac-OBJECT:1 endDelimiter:--// --changeset test-package-rbac-OBJECT:1 endDelimiter:--//

View File

@ -0,0 +1,88 @@
### rbac domain
This code generated was by RbacViewMermaidFlowchartGenerator at 2024-03-11T11:29:11.644658132.
```mermaid
%%{init:{'flowchart':{'htmlLabels':false}}}%%
flowchart TB
subgraph package.customer["`**package.customer**`"]
direction TB
style package.customer fill:#99bcdb,stroke:#274d6e,stroke-width:8px
subgraph package.customer:roles[ ]
style package.customer:roles fill:#99bcdb,stroke:white
role:package.customer:owner[[package.customer:owner]]
role:package.customer:admin[[package.customer:admin]]
role:package.customer:tenant[[package.customer:tenant]]
end
end
subgraph package["`**package**`"]
direction TB
style package fill:#99bcdb,stroke:#274d6e,stroke-width:8px
subgraph package.customer["`**package.customer**`"]
direction TB
style package.customer fill:#99bcdb,stroke:#274d6e,stroke-width:8px
subgraph package.customer:roles[ ]
style package.customer:roles fill:#99bcdb,stroke:white
role:package.customer:owner[[package.customer:owner]]
role:package.customer:admin[[package.customer:admin]]
role:package.customer:tenant[[package.customer:tenant]]
end
end
subgraph package:roles[ ]
style package:roles fill:#99bcdb,stroke:white
role:package:owner[[package:owner]]
role:package:admin[[package:admin]]
role:package:tenant[[package:tenant]]
end
end
subgraph domain["`**domain**`"]
direction TB
style domain fill:#dd4901,stroke:#274d6e,stroke-width:8px
subgraph domain:roles[ ]
style domain:roles fill:#dd4901,stroke:white
role:domain:owner[[domain:owner]]
role:domain:admin[[domain:admin]]
end
subgraph domain:permissions[ ]
style domain:permissions fill:#dd4901,stroke:white
perm:domain:INSERT{{domain:INSERT}}
perm:domain:DELETE{{domain:DELETE}}
perm:domain:UPDATE{{domain:UPDATE}}
perm:domain:SELECT{{domain:SELECT}}
end
end
%% granting roles to roles
role:global:admin -.->|XX| role:package.customer:owner
role:package.customer:owner -.-> role:package.customer:admin
role:package.customer:admin -.-> role:package.customer:tenant
role:package.customer:admin -.-> role:package:owner
role:package:owner -.-> role:package:admin
role:package:admin -.-> role:package:tenant
role:package:tenant -.-> role:package.customer:tenant
role:package:admin ==> role:domain:owner
role:domain:owner ==> role:package:tenant
role:domain:owner ==> role:domain:admin
role:domain:admin ==> role:package:tenant
%% granting permissions to roles
role:package:admin ==> perm:domain:INSERT
role:domain:owner ==> perm:domain:DELETE
role:domain:owner ==> perm:domain:UPDATE
role:domain:admin ==> perm:domain:SELECT
```

View File

@ -1,5 +1,5 @@
--liquibase formatted sql --liquibase formatted sql
-- This code generated was by RbacViewPostgresGenerator at 2024-03-11T09:06:04.558752062. -- This code generated was by RbacViewPostgresGenerator at 2024-03-11T11:29:11.645391647.
-- ============================================================================ -- ============================================================================
--changeset test-domain-rbac-OBJECT:1 endDelimiter:--// --changeset test-domain-rbac-OBJECT:1 endDelimiter:--//

View File

@ -118,7 +118,6 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
}); });
// then // then
System.out.println("ok");
result.assertExceptionWithRootCauseMessage(org.hibernate.exception.ConstraintViolationException.class); result.assertExceptionWithRootCauseMessage(org.hibernate.exception.ConstraintViolationException.class);
} }

View File

@ -73,6 +73,7 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
.contentType("application/json") .contentType("application/json")
.body("", hasItem( .body("", hasItem(
allOf( allOf(
// TODO: should there be a grantedByRole or just a grantedByTrigger?
hasEntry("grantedByRoleIdName", "test_customer#xxx.owner"), hasEntry("grantedByRoleIdName", "test_customer#xxx.owner"),
hasEntry("grantedRoleIdName", "test_customer#xxx.admin"), hasEntry("grantedRoleIdName", "test_customer#xxx.admin"),
hasEntry("granteeUserName", "customer-admin@xxx.example.com") hasEntry("granteeUserName", "customer-admin@xxx.example.com")
@ -80,6 +81,7 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
)) ))
.body("", hasItem( .body("", hasItem(
allOf( allOf(
// TODO: should there be a grantedByRole or just a grantedByTrigger?
hasEntry("grantedByRoleIdName", "test_customer#yyy.owner"), hasEntry("grantedByRoleIdName", "test_customer#yyy.owner"),
hasEntry("grantedRoleIdName", "test_customer#yyy.admin"), hasEntry("grantedRoleIdName", "test_customer#yyy.admin"),
hasEntry("granteeUserName", "customer-admin@yyy.example.com") hasEntry("granteeUserName", "customer-admin@yyy.example.com")

View File

@ -1,6 +1,6 @@
package net.hostsharing.hsadminng.test.cust; package net.hostsharing.hsadminng.test.cust;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacViewMermaidFlowchart; import net.hostsharing.hsadminng.rbac.rbacdef.RbacViewMermaidFlowchartGenerator;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@ -9,7 +9,7 @@ class TestCustomerEntityUnitTest {
@Test @Test
void definesRbac() { void definesRbac() {
final var rbacFlowchart = new RbacViewMermaidFlowchart(TestCustomerEntity.rbac()).toString(); final var rbacFlowchart = new RbacViewMermaidFlowchartGenerator(TestCustomerEntity.rbac()).toString();
assertThat(rbacFlowchart).isEqualTo(""" assertThat(rbacFlowchart).isEqualTo("""
%%{init:{'flowchart':{'htmlLabels':false}}}%% %%{init:{'flowchart':{'htmlLabels':false}}}%%
flowchart TB flowchart TB

View File

@ -1,6 +1,6 @@
package net.hostsharing.hsadminng.test.pac; package net.hostsharing.hsadminng.test.pac;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacViewMermaidFlowchart; import net.hostsharing.hsadminng.rbac.rbacdef.RbacViewMermaidFlowchartGenerator;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@ -9,7 +9,7 @@ class TestPackageEntityUnitTest {
@Test @Test
void definesRbac() { void definesRbac() {
final var rbacFlowchart = new RbacViewMermaidFlowchart(TestPackageEntity.rbac()).toString(); final var rbacFlowchart = new RbacViewMermaidFlowchartGenerator(TestPackageEntity.rbac()).toString();
assertThat(rbacFlowchart).isEqualTo(""" assertThat(rbacFlowchart).isEqualTo("""
%%{init:{'flowchart':{'htmlLabels':false}}}%% %%{init:{'flowchart':{'htmlLabels':false}}}%%
flowchart TB flowchart TB