RBAC Diagram+PostgreSQL Generator #21
@ -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"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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}
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
@ -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}}}%%
|
||||||
|
@ -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:--//
|
||||||
|
@ -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}}}%%
|
||||||
|
@ -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:--//
|
||||||
|
88
src/main/resources/db/changelog/133-test-domain-rbac.md
Normal file
88
src/main/resources/db/changelog/133-test-domain-rbac.md
Normal 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
|
||||||
|
|
||||||
|
```
|
@ -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:--//
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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")
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user