This commit is contained in:
Michael Hoennig 2024-03-12 08:31:50 +01:00
parent e422db9081
commit 9788205724
26 changed files with 867 additions and 1008 deletions

View File

@ -59,8 +59,11 @@ public class HsOfficeBankAccountEntity implements HasUuid, Stringifyable {
public static RbacView rbac() {
return rbacViewFor("bankAccount", HsOfficeBankAccountEntity.class)
.withIdentityView(SQL.projection("iban || ':' || holder"))
.withIdentityView(SQL.projection("concat(iban, ':', holder)"))
.withUpdatableColumns("holder", "iban", "bic")
.toRole("global", GUEST).grantPermission("bankAccount", INSERT)
.createRole(OWNER, (with) -> {
with.owningUser(CREATOR);
with.incomingSuperRole(GLOBAL, ADMIN);
@ -75,6 +78,6 @@ public class HsOfficeBankAccountEntity implements HasUuid, Stringifyable {
}
public static void main(String[] args) throws IOException {
rbac().generateWithBaseFileName("243-hs-office-bankaccount-rbac-generated");
rbac().generateWithBaseFileName("243-hs-office-bankaccount-rbac");
}
}

View File

@ -78,6 +78,8 @@ public class HsOfficePersonEntity implements HasUuid, Stringifyable {
return rbacViewFor("person", HsOfficePersonEntity.class)
.withIdentityView(SQL.projection("concat(tradeName, familyName, givenName)"))
.withUpdatableColumns("personType", "tradeName", "givenName", "familyName")
.toRole("global", GUEST).grantPermission("person", INSERT)
.createRole(OWNER, (with) -> {
with.permission(DELETE);
with.owningUser(CREATOR);
@ -93,6 +95,6 @@ public class HsOfficePersonEntity implements HasUuid, Stringifyable {
public static void main(String[] args) throws IOException {
rbac().generateWithBaseFileName("213-hs-office-person-rbac-generated");
rbac().generateWithBaseFileName("213-hs-office-person-rbac");
}
}

View File

@ -95,7 +95,12 @@ public class HsOfficeSepaMandateEntity implements Stringifyable, HasUuid {
public static RbacView rbac() {
return rbacViewFor("sepaMandate", HsOfficeSepaMandateEntity.class)
.withIdentityView(projection("concat(tradeName, familyName, givenName)"))
.withIdentityView(query("""
select sm.uuid as uuid, ba.iban || '-' || sm.validity as idName
from hs_office_sepamandate sm
join hs_office_bankaccount ba on ba.uuid = sm.bankAccountUuid
"""))
.withRestrictedViewOrderBy(expression("validity"))
.withUpdatableColumns("reference", "agreement", "validity")
.importEntityAlias("debitorRel", HsOfficeRelationshipEntity.class, dependsOnColumn("debitorRelUuid"))
@ -111,17 +116,17 @@ public class HsOfficeSepaMandateEntity implements Stringifyable, HasUuid {
})
.createSubRole(AGENT, (with) -> {
with.outgoingSubRole("bankAccount", REFERRER);
with.outgoingSubRole("debitorRel", AGENT);
with.outgoingSubRole("debitor", AGENT);
})
.createSubRole(REFERRER, (with) -> {
with.incomingSuperRole("bankAccount", ADMIN);
with.incomingSuperRole("debitorRel", AGENT);
with.incomingSuperRole("debitor", AGENT);
with.outgoingSubRole("debitorRel", TENANT);
with.permission(SELECT);
});
}
public static void main(String[] args) throws IOException {
rbac().generateWithBaseFileName("253-hs-office-sepamandate-rbac-generated");
rbac().generateWithBaseFileName("253-hs-office-sepamandate-rbac");
}
}

View File

@ -53,7 +53,7 @@ public class InsertTriggerGenerator {
FOR row IN SELECT * FROM ${rawSuperTableName}
LOOP
roleUuid := findRoleId(${rawSuperRoleDescriptor}(row));
roleUuid := findRoleId(${rawSuperRoleDescriptor});
permissionUuid := createPermission(row.uuid, 'INSERT', '${rawSubTableName}');
call grantPermissionToRole(roleUuid, permissionUuid);
END LOOP;
@ -62,7 +62,7 @@ public class InsertTriggerGenerator {
""",
with("rawSubTableName", rbacDef.getRootEntityAlias().getRawTableName()),
with("rawSuperTableName", superRoleDef.getEntityAlias().getRawTableName()),
with("rawSuperRoleDescriptor", toVar(superRoleDef))
with("rawSuperRoleDescriptor", toRoleDescriptor(superRoleDef, "row"))
);
});
}
@ -79,7 +79,7 @@ public class InsertTriggerGenerator {
strict as $$
begin
call grantPermissionToRole(
${rawSuperRoleDescriptor}(NEW),
${rawSuperRoleDescriptor},
createPermission(NEW.uuid, 'INSERT', '${rawSubTableName}'));
return NEW;
end; $$;
@ -91,7 +91,7 @@ public class InsertTriggerGenerator {
""",
with("rawSubTableName", rbacDef.getRootEntityAlias().getRawTableName()),
with("rawSuperTableName", superRoleDef.getEntityAlias().getRawTableName()),
with("rawSuperRoleDescriptor", toVar(superRoleDef))
with("rawSuperRoleDescriptor", toRoleDescriptor(superRoleDef, PostgresTriggerReference.NEW.name()))
);
});
}
@ -111,15 +111,39 @@ public class InsertTriggerGenerator {
""",
with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableName()));
getOptionalInsertGrant().ifPresentOrElse(g -> {
plPgSql.writeLn("""
create trigger ${rawSubTable}_insert_permission_check_tg
before insert on ${rawSubTable}
for each row
when ( not hasInsertPermission(NEW.${referenceColumn}, 'INSERT', '${rawSubTable}') )
execute procedure ${rawSubTable}_insert_permission_missing_tf();
""",
with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableName()),
with("referenceColumn", g.getSuperRoleDef().getEntityAlias().dependsOnColumName() ));
if (!g.getSuperRoleDef().getEntityAlias().isGlobal()) {
plPgSql.writeLn(
"""
create trigger ${rawSubTable}_insert_permission_check_tg
before insert on ${rawSubTable}
for each row
when ( not hasInsertPermission(NEW.${referenceColumn}, 'INSERT', '${rawSubTable}') )
execute procedure ${rawSubTable}_insert_permission_missing_tf();
""",
with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableName()),
with("referenceColumn", g.getSuperRoleDef().getEntityAlias().dependsOnColumName()));
} else {
switch (g.getSuperRoleDef().getRole()) {
case ADMIN -> {
plPgSql.writeLn(
"""
create trigger ${rawSubTable}_insert_permission_check_tg
before insert on ${rawSubTable}
for each row
when ( not isGlobalAdmin() )
execute procedure ${rawSubTable}_insert_permission_missing_tf();
""",
with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableName()));
}
case GUEST -> {
// no permission check trigger generated, as anybody can insert rows into this table
}
default -> {
throw new IllegalArgumentException(
"invalid global role for INSERT permission: " + g.getSuperRoleDef().getRole());
}
}
}
},
() -> {
plPgSql.writeLn("""
@ -162,4 +186,12 @@ public class InsertTriggerGenerator {
return uncapitalize(roleDef.getEntityAlias().simpleName()) + capitalize(roleDef.getRole().roleName());
}
private String toRoleDescriptor(final RbacView.RbacRoleDefinition roleDef, final String ref) {
final var functionName = toVar(roleDef);
if (roleDef.getEntityAlias().isGlobal()) {
return functionName + "()";
}
return functionName + "(" + ref + ")";
}
}

View File

@ -31,7 +31,7 @@ public class RbacIdentityViewGenerator {
$idName$);
""";
case SQL_QUERY -> """
call generateRbacIdentityViewFromProjection('${rawTableName}', $idName$
call generateRbacIdentityViewFromQuery('${rawTableName}', $idName$
${identityViewSqlPart}
$idName$);
""";

View File

@ -24,9 +24,11 @@ public class RbacRestrictedViewGenerator {
--changeset ${liquibaseTagPrefix}-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
call generateRbacRestrictedView('${rawTableName}',
'${orderBy}',
$orderBy$
${orderBy}
$orderBy$,
$updates$
${updates}
${updates}
$updates$);
--//

View File

@ -626,22 +626,23 @@ public class RbacView {
return tableName.substring(0, tableName.length() - "_rv".length());
}
public record Role(String roleName) {
public enum Role {
public static final Role OWNER = new Role("owner");
public static final Role ADMIN = new Role("admin");
public static final Role AGENT = new Role("agent");
public static final Role TENANT = new Role("tenant");
public static final Role REFERRER = new Role("referrer");
OWNER,
ADMIN,
AGENT,
TENANT,
REFERRER,
GUEST;
@Override
public String toString() {
return ":" + roleName;
return ":" + roleName();
}
@Override
public boolean equals(final Object obj) {
return ((obj instanceof Role) && ((Role) obj).roleName.equals(this.roleName));
String roleName() {
return name().toLowerCase();
}
}

View File

@ -103,7 +103,7 @@ public class RbacGrantController implements RbacGrantsApi {
// public ResponseEntity<String> allGrantsOfUserAsMermaid(
// @RequestHeader(name = "current-user") String currentUser,
// @RequestHeader(name = "assumed-roles", required = false) String assumedRoles) {
// final var graph = RbacGrantsMermaidService.allGrantsToUser(currentUser);
// final var graph = RbacGrantsDiagramService.allGrantsToUser(currentUser);
// return ResponseEntity.ok(graph);
// }

View File

@ -1,204 +0,0 @@
package net.hostsharing.hsadminng.rbac.rbacgrant;
import net.hostsharing.hsadminng.context.Context;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import jakarta.validation.constraints.NotNull;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.*;
import java.util.stream.Stream;
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.joining;
import static net.hostsharing.hsadminng.rbac.rbacgrant.RbacGrantsMermaidService.Include.*;
// TODO: cleanup - this code was 'hacked' to quickly fix a specific problem, needs refactoring
@Service
public class RbacGrantsMermaidService {
public static void writeToFile(final String title, final String graph, final String fileName) {
try (BufferedWriter writer = new BufferedWriter(new FileWriter(fileName))) {
writer.write("""
### all grants to %s
```mermaid
%s
```
""".formatted(title, graph));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public enum Include {
DETAILS,
USERS,
PERMISSIONS,
NOT_ASSUMED,
TEST_ENTITIES,
NON_TEST_ENTITIES
}
@Autowired
private Context context;
@Autowired
private RawRbacGrantRepository rawGrantRepo;
@PersistenceContext
private EntityManager em;
public String allGrantsToCurrentUser(final EnumSet<Include> includes) {
final var graph = new HashSet<RawRbacGrantEntity>();
for ( UUID subjectUuid: context.currentSubjectsUuids() ) {
traverseGrantsTo(graph, subjectUuid, includes);
}
return toMermaidFlowchart(graph, includes);
}
private void traverseGrantsTo(final Set<RawRbacGrantEntity> graph, final UUID refUuid, final EnumSet<Include> includes) {
final var grants = rawGrantRepo.findByAscendingUuid(refUuid);
grants.forEach(g -> {
if (!includes.contains(PERMISSIONS) && g.getDescendantIdName().startsWith("perm ")) {
return;
}
if (!includes.contains(TEST_ENTITIES) && g.getDescendantIdName().contains(" test_")) {
return;
}
if (!includes.contains(NON_TEST_ENTITIES) && !g.getDescendantIdName().contains(" test_")) {
return;
}
graph.add(g);
if (includes.contains(NOT_ASSUMED) || g.isAssumed()) {
traverseGrantsTo(graph, g.getDescendantUuid(), includes);
}
});
}
public String allGrantsFrom(final UUID targetObject, final String op, final EnumSet<Include> includes) {
final var refUuid = (UUID) em.createNativeQuery("SELECT uuid FROM rbacpermission WHERE objectuuid=:targetObject AND op=:op")
.setParameter("targetObject", targetObject)
.setParameter("op", op)
.getSingleResult();
final var graph = new HashSet<RawRbacGrantEntity>();
traverseGrantsFrom(graph, refUuid, includes);
return toMermaidFlowchart(graph, includes);
}
private void traverseGrantsFrom(final Set<RawRbacGrantEntity> graph, final UUID refUuid, final EnumSet<Include> option) {
final var grants = rawGrantRepo.findByDescendantUuid(refUuid);
grants.forEach(g -> {
if (!option.contains(USERS) && g.getAscendantIdName().startsWith("user ")) {
return;
}
graph.add(g);
if (option.contains(NOT_ASSUMED) || g.isAssumed()) {
traverseGrantsFrom(graph, g.getAscendingUuid(), option);
}
});
}
private String toMermaidFlowchart(final HashSet<RawRbacGrantEntity> graph, final EnumSet<Include> includes) {
final var entities =
includes.contains(DETAILS)
? graph.stream()
.flatMap(g -> Stream.of(
new Node(g.getAscendantIdName(), g.getAscendingUuid()),
new Node(g.getDescendantIdName(), g.getDescendantUuid()))
)
.collect(groupingBy(RbacGrantsMermaidService::renderEntityIdName))
.entrySet().stream()
.map(entity -> "subgraph " + quoted(entity.getKey()) + renderSubgraph(entity.getKey()) + "\n\n "
+ entity.getValue().stream()
.map(n -> renderNode(n.idName(), n.uuid()).replace("\n", "\n "))
.sorted()
.distinct()
.collect(joining("\n\n ")))
.collect(joining("\n\nend\n\n"))
+ "\n\nend\n\n"
: "";
final var grants = graph.stream()
.map(g -> quoted(g.getAscendantIdName()) +
(g.isAssumed() ? " --> " : " -.-> ") +
quoted(g.getDescendantIdName()))
.sorted()
.collect(joining("\n"));
final var avoidCroppedNodeLabels = "%%{init:{'flowchart':{'htmlLabels':false}}}%%\n\n";
return (includes.contains(DETAILS) ? avoidCroppedNodeLabels : "")
+ "flowchart TB\n\n"
+ entities
+ grants;
}
private String renderSubgraph(final String entityId) {
// this does not work according to Mermaid bug https://github.com/mermaid-js/mermaid/issues/3806
// if (entityId.contains("#")) {
// final var parts = entityId.split("#");
// final var table = parts[0];
// final var entity = parts[1];
// if (table.equals("entity")) {
// return "[" + entity "]";
// }
// return "[" + table + "\n" + entity + "]";
// }
return "[" + entityId + "]";
}
private static String renderEntityIdName(final Node node) {
final var refType = refType(node.idName());
if (refType.equals("user")) {
return "users";
}
if (refType.equals("perm")) {
return node.idName().split(" ", 4)[3];
}
if (refType.equals("role")) {
final var withoutRolePrefix = node.idName().substring("role:".length());
return withoutRolePrefix.substring(0, withoutRolePrefix.lastIndexOf('.'));
}
throw new IllegalArgumentException("unknown refType '" + refType + "' in '" + node.idName() + "'");
}
private String renderNode(final String idName, final UUID uuid) {
return quoted(idName) + renderNodeContent(idName, uuid);
}
private String renderNodeContent(final String idName, final UUID uuid) {
final var refType = refType(idName);
if (refType.equals("user")) {
final var displayName = idName.substring(refType.length()+1);
return "(" + displayName + "\nref:" + uuid + ")";
}
if (refType.equals("role")) {
final var roleType = idName.substring(idName.lastIndexOf('.') + 1);
return "[" + roleType + "\nref:" + uuid + "]";
}
if (refType.equals("perm")) {
final var roleType = idName.split(" ")[1];
return "{{" + roleType + "\nref:" + uuid + "}}";
}
return "";
}
private static String refType(final String idName) {
return idName.split(" ", 2)[0];
}
@NotNull
private static String quoted(final String idName) {
return idName.replace(" ", ":").replaceAll("@.*", "");
}
}
record Node(String idName, UUID uuid) {
}

View File

@ -41,8 +41,7 @@ public class TestCustomerEntity implements HasUuid {
.withIdentityView(SQL.projection("prefix"))
.withRestrictedViewOrderBy(SQL.expression("reference"))
.withUpdatableColumns("reference", "prefix", "adminUserName")
// TODO: do we want explicit specification of parent-independent insert permissions?
// .toRole("global", ADMIN).grantPermission("customer", INSERT)
.toRole("global", ADMIN).grantPermission("customer", INSERT)
.createRole(OWNER, (with) -> {
with.owningUser(CREATOR).unassumed();

View File

@ -373,10 +373,12 @@ create table RbacPermission
uuid uuid primary key references RbacReference (uuid) on delete cascade,
objectUuid uuid not null references RbacObject,
op RbacOp not null,
opTableName varchar(60),
unique (objectUuid, op)
opTableName varchar(60)
);
ALTER TABLE RbacPermission
ADD CONSTRAINT RbacPermission_uc UNIQUE NULLS NOT DISTINCT (objectUuid, op, opTableName);
call create_journal('RbacPermission');
create or replace function createPermission(forObjectUuid uuid, forOp RbacOp, forOpTableName text = null)
@ -469,23 +471,6 @@ $$;
--//
-- ============================================================================
--changeset rbac-base-duplicate-role-grant-exception:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
create or replace procedure raiseDuplicateRoleGrantException(subRoleId uuid, superRoleId uuid)
language plpgsql as $$
declare
subRoleIdName text;
superRoleIdName text;
begin
select roleIdName from rbacRole_ev where uuid=subRoleId into subRoleIdName;
select roleIdName from rbacRole_ev where uuid=superRoleId into superRoleIdName;
raise exception '[400] Duplicate role grant detected: role % (%) already granted to % (%)', subRoleId, subRoleIdName, superRoleId, superRoleIdName;
end;
$$;
--//
-- ============================================================================
--changeset rbac-base-duplicate-role-grant-exception:1 endDelimiter:--//
-- ----------------------------------------------------------------------------

View File

@ -118,9 +118,32 @@ select 'global', (select uuid from RbacObject where objectTable = 'global'), 'ad
$$;
begin transaction;
call defineContext('creating global admin role', null, null, null);
select createRole(globalAdmin());
call defineContext('creating global admin role', null, null, null);
select createRole(globalAdmin());
commit;
--//
-- ============================================================================
--changeset rbac-global-GUEST-ROLE:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
/*
A global guest role.
*/
create or replace function globalGuest(assumed boolean = true)
returns RbacRoleDescriptor
returns null on null input
stable -- leakproof
language sql as $$
select 'global', (select uuid from RbacObject where objectTable = 'global'), 'guest'::RbacRoleType, assumed;
$$;
begin transaction;
call defineContext('creating global guest role', null, null, null);
select createRole(globalGuest());
commit;
--//
-- ============================================================================
--changeset rbac-global-ADMIN-USERS:1 context:dev,tc endDelimiter:--//

View File

@ -1,4 +1,5 @@
--liquibase formatted sql
-- This code generated was by RbacViewPostgresGenerator at 2024-03-11T15:13:04.479330676.
-- ============================================================================
--changeset hs-office-person-rbac-OBJECT:1 endDelimiter:--//
@ -15,69 +16,134 @@ call generateRbacRoleDescriptors('hsOfficePerson', 'hs_office_person');
-- ============================================================================
--changeset hs-office-person-rbac-ROLES-CREATION:1 endDelimiter:--//
--changeset hs-office-person-rbac-insert-trigger:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
/*
Creates the roles and their assignments for a new person for the AFTER INSERT TRIGGER.
Creates the roles, grants and permission for the AFTER INSERT TRIGGER.
*/
create or replace function createRbacRolesForHsOfficePerson()
create or replace procedure buildRbacSystemForHsOfficePerson(
NEW hs_office_person
)
language plpgsql as $$
declare
begin
call enterTriggerForObjectUuid(NEW.uuid);
perform createRoleWithGrants(
hsOfficePersonOwner(NEW),
permissions => array['DELETE'],
userUuids => array[currentUserUuid()],
incomingSuperRoles => array[globalAdmin()]
);
perform createRoleWithGrants(
hsOfficePersonAdmin(NEW),
permissions => array['UPDATE'],
incomingSuperRoles => array[hsOfficePersonOwner(NEW)]
);
perform createRoleWithGrants(
hsOfficePersonReferrer(NEW),
permissions => array['SELECT'],
incomingSuperRoles => array[hsOfficePersonAdmin(NEW)]
);
call leaveTriggerForObjectUuid(NEW.uuid);
end; $$;
/*
AFTER INSERT TRIGGER to create the role+grant structure for a new hs_office_person row.
*/
create or replace function insertTriggerForHsOfficePerson_tf()
returns trigger
language plpgsql
strict as $$
begin
if TG_OP <> 'INSERT' then
raise exception 'invalid usage of TRIGGER AFTER INSERT';
end if;
perform createRoleWithGrants(
hsOfficePersonOwner(NEW),
permissions => array['DELETE'],
incomingSuperRoles => array[globalAdmin()],
userUuids => array[currentUserUuid()],
grantedByRole => globalAdmin()
);
-- TODO: who is admin? the person itself? is it allowed for the person itself or a representative to update the data?
perform createRoleWithGrants(
hsOfficePersonAdmin(NEW),
permissions => array['UPDATE'],
incomingSuperRoles => array[hsOfficePersonOwner(NEW)]
);
perform createRoleWithGrants(
hsOfficePersonReferrer(NEW),
permissions => array['SELECT'],
incomingSuperRoles => array[hsOfficePersonAdmin(NEW)]
);
call buildRbacSystemForHsOfficePerson(NEW);
return NEW;
end; $$;
/*
An AFTER INSERT TRIGGER which creates the role structure for a new customer.
*/
create trigger createRbacRolesForHsOfficePerson_Trigger
after insert
on hs_office_person
create trigger insertTriggerForHsOfficePerson_tg
after insert on hs_office_person
for each row
execute procedure createRbacRolesForHsOfficePerson();
execute procedure insertTriggerForHsOfficePerson_tf();
--//
-- ============================================================================
--changeset hs-office-person-rbac-INSERT:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
/*
Creates INSERT INTO hs_office_person permissions for the related global rows.
*/
do language plpgsql $$
declare
row global;
permissionUuid uuid;
roleUuid uuid;
begin
call defineContext('create INSERT INTO hs_office_person permissions for the related global rows');
FOR row IN SELECT * FROM global
LOOP
roleUuid := findRoleId(globalGuest());
permissionUuid := createPermission(row.uuid, 'INSERT', 'hs_office_person');
call grantPermissionToRole(roleUuid, permissionUuid);
END LOOP;
END;
$$;
/**
Adds hs_office_person INSERT permission to specified role of new global rows.
*/
create or replace function hs_office_person_global_insert_tf()
returns trigger
language plpgsql
strict as $$
begin
call grantPermissionToRole(
globalGuest(),
createPermission(NEW.uuid, 'INSERT', 'hs_office_person'));
return NEW;
end; $$;
create trigger hs_office_person_global_insert_tg
after insert on global
for each row
execute procedure hs_office_person_global_insert_tf();
/**
Checks if the user or assumed roles are allowed to insert a row to hs_office_person.
*/
create or replace function hs_office_person_insert_permission_missing_tf()
returns trigger
language plpgsql as $$
begin
raise exception '[403] insert into hs_office_person not allowed for current subjects % (%)',
currentSubjects(), currentSubjectsUuids();
end; $$;
--//
-- ============================================================================
--changeset hs-office-person-rbac-IDENTITY-VIEW:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
call generateRbacIdentityViewFromProjection('hs_office_person', $idName$
concat(target.tradeName, target.familyName, target.givenName)
concat(tradeName, familyName, givenName)
$idName$);
--//
-- ============================================================================
--changeset hs-office-person-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
call generateRbacRestrictedView('hs_office_person', 'concat(target.tradeName, target.familyName, target.givenName)',
call generateRbacRestrictedView('hs_office_person',
'concat(tradeName, familyName, givenName)',
$updates$
personType = new.personType,
tradeName = new.tradeName,
@ -87,48 +153,3 @@ call generateRbacRestrictedView('hs_office_person', 'concat(target.tradeName, ta
--//
-- ============================================================================
--changeset hs-office-person-rbac-NEW-PERSON:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
/*
Creates a global permission for new-person and assigns it to the hostsharing admins role.
*/
do language plpgsql $$
declare
addCustomerPermissions uuid[];
globalObjectUuid uuid;
globalAdminRoleUuid uuid ;
begin
call defineContext('granting global new-person permission to global admin role', null, null, null);
globalAdminRoleUuid := findRoleId(globalAdmin());
globalObjectUuid := (select uuid from global);
addCustomerPermissions := createPermissions(globalObjectUuid, array ['new-person']);
call grantPermissionsToRole(globalAdminRoleUuid, addCustomerPermissions);
end;
$$;
/**
Used by the trigger to prevent the add-customer to current user respectively assumed roles.
*/
create or replace function addHsOfficePersonNotAllowedForCurrentSubjects()
returns trigger
language PLPGSQL
as $$
begin
raise exception '[403] new-person not permitted for %',
array_to_string(currentSubjects(), ';', 'null');
end; $$;
/**
Checks if the user or assumed roles are allowed to create a new customer.
*/
create trigger hs_office_person_insert_trigger
before insert
on hs_office_person
for each row
-- TODO.spec: who is allowed to create new persons
when ( not hasAssumedRole() )
execute procedure addHsOfficePersonNotAllowedForCurrentSubjects();
--//

View File

@ -27,71 +27,77 @@ create or replace function hsOfficeRelationshipRbacRolesTrigger()
language plpgsql
strict as $$
declare
hsOfficeRelationshipTenant RbacRoleDescriptor;
newRelAnchor hs_office_person;
newRelHolder hs_office_person;
newAnchorPerson hs_office_person;
newHolderPerson hs_office_person;
oldContact hs_office_contact;
newContact hs_office_contact;
begin
call enterTriggerForObjectUuid(NEW.uuid);
hsOfficeRelationshipTenant := hsOfficeRelationshipTenant(NEW);
select * from hs_office_person as p where p.uuid = NEW.relAnchorUuid into newRelAnchor;
select * from hs_office_person as p where p.uuid = NEW.relHolderUuid into newRelHolder;
select * from hs_office_person as p where p.uuid = NEW.relAnchorUuid into newAnchorPerson;
select * from hs_office_person as p where p.uuid = NEW.relHolderUuid into newHolderPerson;
select * from hs_office_contact as c where c.uuid = NEW.contactUuid into newContact;
if TG_OP = 'INSERT' then
-- cannot be generated using `tools/generate` because there are multiple grants to the same entity type
perform createRoleWithGrants(
hsOfficeRelationshipOwner(NEW),
permissions => array['DELETE'],
incomingSuperRoles => array[
globalAdmin(),
hsOfficePersonAdmin(newRelAnchor)]
);
globalAdmin()
]
);
perform createRoleWithGrants(
hsOfficeRelationshipAdmin(NEW),
permissions => array['UPDATE'],
incomingSuperRoles => array[hsOfficeRelationshipOwner(NEW)]
);
incomingSuperRoles => array[
hsOfficeRelationshipOwner(NEW),
hsOfficePersonAdmin(newAnchorPerson)
]
);
-- the tenant role for those related users who can view the data
perform createRoleWithGrants(
hsOfficeRelationshipTenant,
permissions => array['SELECT'],
hsOfficeRelationshipAgent(NEW),
incomingSuperRoles => array[
hsOfficeRelationshipAdmin(NEW),
hsOfficePersonAdmin(newRelAnchor),
hsOfficePersonAdmin(newRelHolder),
hsOfficeContactAdmin(newContact)],
outgoingSubRoles => array[
hsOfficePersonTenant(newRelAnchor),
hsOfficePersonTenant(newRelHolder),
hsOfficeContactTenant(newContact)]
);
hsOfficePersonAdmin(newHolderPerson),
hsOfficeContactAdmin(newContact)
]
);
-- anchor and holder admin roles need each others tenant role
-- to be able to see the joined relationship
-- TODO: this can probably be avoided through agent+guest roles
call grantRoleToRole(hsOfficePersonTenant(newRelAnchor), hsOfficePersonAdmin(newRelHolder));
call grantRoleToRole(hsOfficePersonTenant(newRelHolder), hsOfficePersonAdmin(newRelAnchor));
call grantRoleToRoleIfNotNull(hsOfficePersonTenant(newRelHolder), hsOfficeContactAdmin(newContact));
perform createRoleWithGrants(
hsOfficeRelationshipTenant(NEW),
permissions => array['SELECT'],
incomingSuperRoles => array[
hsOfficeRelationshipAgent(NEW)
],
outgoingSubRoles => array[
hsOfficePersonReferrer(newAnchorPerson),
hsOfficePersonReferrer(newHolderPerson),
hsOfficeContactReferrer(newContact)
]
);
if ( NEW.relType = 'REPRESENTATIVE' ) then
call grantRoleToRole(hsOfficePersonAdmin(newHolderPerson), hsOfficePersonAdmin(newAnchorPerson));
end if;
elsif TG_OP = 'UPDATE' then
if OLD.contactUuid <> NEW.contactUuid then
-- nothing but the contact can be updated,
-- only the contact can be updated,
-- in other cases, a new relationship needs to be created and the old updated
select * from hs_office_contact as c where c.uuid = OLD.contactUuid into oldContact;
call revokeRoleFromRole( hsOfficeRelationshipTenant, hsOfficeContactAdmin(oldContact) );
call grantRoleToRole( hsOfficeRelationshipTenant, hsOfficeContactAdmin(newContact) );
call revokeRoleFromRole( hsOfficeContactReferrer(oldContact), hsOfficeRelationshipTenant(NEW) );
call grantRoleToRole( hsOfficeContactReferrer(newContact), hsOfficeRelationshipTenant(NEW) );
call revokeRoleFromRole( hsOfficeContactTenant(oldContact), hsOfficeRelationshipTenant );
call grantRoleToRole( hsOfficeContactTenant(newContact), hsOfficeRelationshipTenant );
call revokeRoleFromRole( hsOfficeRelationshipAgent(NEW), hsOfficeContactAdmin(oldContact) );
call grantRoleToRole( hsOfficeRelationshipAgent(NEW), hsOfficeContactAdmin(newContact) );
end if;
else
raise exception 'invalid usage of TRIGGER';
@ -136,8 +142,8 @@ call generateRbacIdentityViewFromProjection('hs_office_relationship', $idName$
--changeset hs-office-relationship-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
call generateRbacRestrictedView('hs_office_relationship',
'(select idName from hs_office_person_iv p where p.uuid = target.relHolderUuid)',
$updates$
'(select idName from hs_office_person_iv p where p.uuid = target.relHolderUuid)',
$updates$
contactUuid = new.contactUuid
$updates$);
--//

View File

@ -1,72 +1,158 @@
### hs_office_partner RBAC
### rbac partner
This code generated was by RbacViewMermaidFlowchartGenerator at 2024-03-11T15:29:41.494727519.
```mermaid
%%{init:{'flowchart':{'htmlLabels':false}}}%%
flowchart TB
subgraph external[ ]
style external fill:#fff
subgraph global
style global fill:#eee
role:global.admin[global.admin]
end
subgraph partnerRel.contact["`**partnerRel.contact**`"]
direction TB
style partnerRel.contact fill:#99bcdb,stroke:#274d6e,stroke-width:8px
subgraph partnerPerson
style partnerPerson fill:#eee
role:partnerPerson.admin[partnerPerson.admin]
end
subgraph partnerRel.contact:roles[ ]
style partnerRel.contact:roles fill:#99bcdb,stroke:white
subgraph otherRelatedPerson
style otherRelatedPerson fill:#eee
role:otherRelatedPerson.admin[otherRelatedPerson.admin]
end
subgraph hsOfficeRelationship[hsOfficeRelationship:PARTNER]
direction TB
style hsOfficeRelationship fill:#eee
role:global.admin
--> role:hsOfficeRelationship.owner[relationship.owner]
--> role:hsOfficeRelationship.admin[relationship.admin]
--> role:hsOfficeRelationship.agent[relationship.agent]
--> role:hsOfficeRelationship.tenant[relationship.tenant]
role:partnerPerson.admin --> role:hsOfficeRelationship.agent
role:otherRelatedPerson.admin --> role:hsOfficeRelationship.tenant
role:partnerRel.contact:owner[[partnerRel.contact:owner]]
role:partnerRel.contact:admin[[partnerRel.contact:admin]]
role:partnerRel.contact:referrer[[partnerRel.contact:referrer]]
end
end
subgraph internal[ ]
subgraph partner["`**partner**`"]
direction TB
style partner fill:#dd4901,stroke:#274d6e,stroke-width:8px
subgraph hsOfficePartner
style hsOfficePartner fill:#fff
perm:hsOfficePartner.*{{partner.*}}
role:hsOfficeRelationship.owner ==> perm:hsOfficePartner.*
perm:hsOfficePartner.edit{{partner.edit}}
role:hsOfficeRelationship.admin ==> perm:hsOfficePartner.edit
perm:hsOfficePartner.view{{partner.view}}
role:hsOfficeRelationship.tenant ==> perm:hsOfficePartner.view
subgraph partner:permissions[ ]
style partner:permissions fill:#dd4901,stroke:white
perm:partner:new-partner{{partner:new-partner}}
perm:partner:DELETE{{partner:DELETE}}
perm:partner:UPDATE{{partner:UPDATE}}
perm:partner:SELECT{{partner:SELECT}}
end
subgraph hsOfficePartnerDetails
subgraph partnerRel["`**partnerRel**`"]
direction TB
style hsOfficePartnerDetails fill:#eee
perm:hsOfficePartnerDetails.*{{partnerDetails.*}}
role:hsOfficeRelationship.owner ==> perm:hsOfficePartnerDetails.*
style partnerRel fill:#99bcdb,stroke:#274d6e,stroke-width:8px
subgraph partnerRel.contact["`**partnerRel.contact**`"]
direction TB
style partnerRel.contact fill:#99bcdb,stroke:#274d6e,stroke-width:8px
perm:hsOfficePartnerDetails.edit{{partnerDetails.edit}}
role:hsOfficeRelationship.agent ==> perm:hsOfficePartnerDetails.edit
role:hsOfficeRelationship.agent ==> perm:hsOfficePartnerDetails.view
perm:hsOfficePartnerDetails.view{{partnerDetails.view}}
subgraph partnerRel.contact:roles[ ]
style partnerRel.contact:roles fill:#99bcdb,stroke:white
role:partnerRel.contact:owner[[partnerRel.contact:owner]]
role:partnerRel.contact:admin[[partnerRel.contact:admin]]
role:partnerRel.contact:referrer[[partnerRel.contact:referrer]]
end
end
subgraph partnerRel.anchorPerson["`**partnerRel.anchorPerson**`"]
direction TB
style partnerRel.anchorPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
subgraph partnerRel.anchorPerson:roles[ ]
style partnerRel.anchorPerson:roles fill:#99bcdb,stroke:white
role:partnerRel.anchorPerson:owner[[partnerRel.anchorPerson:owner]]
role:partnerRel.anchorPerson:admin[[partnerRel.anchorPerson:admin]]
role:partnerRel.anchorPerson:referrer[[partnerRel.anchorPerson:referrer]]
end
end
subgraph partnerRel.holderPerson["`**partnerRel.holderPerson**`"]
direction TB
style partnerRel.holderPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
subgraph partnerRel.holderPerson:roles[ ]
style partnerRel.holderPerson:roles fill:#99bcdb,stroke:white
role:partnerRel.holderPerson:owner[[partnerRel.holderPerson:owner]]
role:partnerRel.holderPerson:admin[[partnerRel.holderPerson:admin]]
role:partnerRel.holderPerson:referrer[[partnerRel.holderPerson:referrer]]
end
end
subgraph partnerRel:roles[ ]
style partnerRel:roles fill:#99bcdb,stroke:white
role:partnerRel:owner[[partnerRel:owner]]
role:partnerRel:admin[[partnerRel:admin]]
role:partnerRel:agent[[partnerRel:agent]]
role:partnerRel:tenant[[partnerRel:tenant]]
end
end
end
subgraph partnerDetails["`**partnerDetails**`"]
direction TB
style partnerDetails fill:#feb28c,stroke:#274d6e,stroke-width:8px
subgraph partnerDetails:permissions[ ]
style partnerDetails:permissions fill:#feb28c,stroke:white
perm:partnerDetails:DELETE{{partnerDetails:DELETE}}
perm:partnerDetails:UPDATE{{partnerDetails:UPDATE}}
perm:partnerDetails:SELECT{{partnerDetails:SELECT}}
end
end
subgraph partnerRel.anchorPerson["`**partnerRel.anchorPerson**`"]
direction TB
style partnerRel.anchorPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
subgraph partnerRel.anchorPerson:roles[ ]
style partnerRel.anchorPerson:roles fill:#99bcdb,stroke:white
role:partnerRel.anchorPerson:owner[[partnerRel.anchorPerson:owner]]
role:partnerRel.anchorPerson:admin[[partnerRel.anchorPerson:admin]]
role:partnerRel.anchorPerson:referrer[[partnerRel.anchorPerson:referrer]]
end
end
subgraph partnerRel.holderPerson["`**partnerRel.holderPerson**`"]
direction TB
style partnerRel.holderPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
subgraph partnerRel.holderPerson:roles[ ]
style partnerRel.holderPerson:roles fill:#99bcdb,stroke:white
role:partnerRel.holderPerson:owner[[partnerRel.holderPerson:owner]]
role:partnerRel.holderPerson:admin[[partnerRel.holderPerson:admin]]
role:partnerRel.holderPerson:referrer[[partnerRel.holderPerson:referrer]]
end
end
%% granting roles to roles
role:global:admin -.-> role:partnerRel.anchorPerson:owner
role:partnerRel.anchorPerson:owner -.-> role:partnerRel.anchorPerson:admin
role:partnerRel.anchorPerson:admin -.-> role:partnerRel.anchorPerson:referrer
role:global:admin -.-> role:partnerRel.holderPerson:owner
role:partnerRel.holderPerson:owner -.-> role:partnerRel.holderPerson:admin
role:partnerRel.holderPerson:admin -.-> role:partnerRel.holderPerson:referrer
role:global:admin -.-> role:partnerRel.contact:owner
role:partnerRel.contact:owner -.-> role:partnerRel.contact:admin
role:partnerRel.contact:admin -.-> role:partnerRel.contact:referrer
role:global:admin -.-> role:partnerRel:owner
role:partnerRel:owner -.-> role:partnerRel:admin
role:partnerRel.anchorPerson:admin -.-> role:partnerRel:admin
role:partnerRel:admin -.-> role:partnerRel:agent
role:partnerRel.holderPerson:admin -.-> role:partnerRel:agent
role:partnerRel:agent -.-> role:partnerRel:tenant
role:partnerRel.holderPerson:admin -.-> role:partnerRel:tenant
role:partnerRel.contact:admin -.-> role:partnerRel:tenant
role:partnerRel:tenant -.-> role:partnerRel.anchorPerson:referrer
role:partnerRel:tenant -.-> role:partnerRel.holderPerson:referrer
role:partnerRel:tenant -.-> role:partnerRel.contact:referrer
%% granting permissions to roles
role:global:admin ==> perm:partner:new-partner
role:partnerRel:admin ==> perm:partner:DELETE
role:partnerRel:agent ==> perm:partner:UPDATE
role:partnerRel:tenant ==> perm:partner:SELECT
role:partnerRel:admin ==> perm:partnerDetails:DELETE
role:partnerRel:agent ==> perm:partnerDetails:UPDATE
role:partnerRel:agent ==> perm:partnerDetails:SELECT
```

View File

@ -32,58 +32,7 @@ begin
if TG_OP = 'INSERT' then
-- === ATTENTION: code generated from related Mermaid flowchart: ===
perform createRoleWithGrants(
hsOfficePartnerOwner(NEW),
permissions => array['DELETE'],
incomingSuperRoles => array[globalAdmin()]
);
perform createRoleWithGrants(
hsOfficePartnerAdmin(NEW),
permissions => array['UPDATE'],
incomingSuperRoles => array[
hsOfficePartnerOwner(NEW)],
outgoingSubRoles => array[
hsOfficeRelationshipTenant(newPartnerRole),
hsOfficePersonTenant(newPerson),
hsOfficeContactTenant(newContact)]
);
perform createRoleWithGrants(
hsOfficePartnerAgent(NEW),
incomingSuperRoles => array[
hsOfficePartnerAdmin(NEW),
hsOfficeRelationshipAdmin(newPartnerRole),
hsOfficePersonAdmin(newPerson),
hsOfficeContactAdmin(newContact)]
);
perform createRoleWithGrants(
hsOfficePartnerTenant(NEW),
incomingSuperRoles => array[
hsOfficePartnerAgent(NEW)],
outgoingSubRoles => array[
hsOfficeRelationshipTenant(newPartnerRole),
hsOfficePersonGuest(newPerson),
hsOfficeContactGuest(newContact)]
);
perform createRoleWithGrants(
hsOfficePartnerGuest(NEW),
permissions => array['SELECT'],
incomingSuperRoles => array[hsOfficePartnerTenant(NEW)]
);
-- === END of code generated from Mermaid flowchart. ===
-- Each partner-details entity belong exactly to one partner entity
-- and it makes little sense just to delegate partner-details roles.
-- Therefore, we did not model partner-details roles,
-- but instead just assign extra permissions to existing partner-roles.
--Attention: Cannot be in partner-details because of insert order (partner is not in database yet)
-- Permissions and Grants for Partner
call grantPermissionsToRole(
getRoleId(hsOfficeRelationshipOwner(newPartnerRel)),
@ -96,21 +45,21 @@ begin
);
call grantPermissionsToRole(
getRoleId(hsOfficeRelationshipTenant(newPartnerRel), 'fail'),
createPermissions(partnerUuid, array ['view'])
getRoleId(hsOfficeRelationshipTenant(newPartnerRel)),
createPermissions(partnerUuid, array ['SELECT'])
);
-- Permissions and Grants for PartnerDetails
call grantPermissionsToRole(
getRoleId(hsOfficeRelationshipOwner(newPartnerRel), 'fail'),
createPermissions(partnerDetailsUuid, array ['*'])
);
getRoleId(hsOfficeRelationshipOwner(newPartnerRel)),
createPermissions(partnerDetailsUuid, array ['DELETE'])
);
call grantPermissionsToRole(
getRoleId(hsOfficeRelationshipAdmin(newPartnerRel), 'fail'),
createPermissions(partnerDetailsUuid, array ['edit'])
);
getRoleId(hsOfficeRelationshipAdmin(newPartnerRel)),
createPermissions(partnerDetailsUuid, array ['UPDATE'])
);
call grantPermissionsToRole(
-- Yes, here hsOfficePartnerAGENT is used, not hsOfficeRelationshipTENANT.
@ -118,7 +67,7 @@ begin
-- Otherwise package-admins etc. would be able to read the data.
getRoleId(hsOfficeRelationshipAgent(newPartnerRel)),
createPermissions(partnerDetailsUuid, array ['SELECT'])
);
);
elsif TG_OP = 'UPDATE' then
@ -129,72 +78,72 @@ begin
-- Revokes from Partner
call revokePermissionFromRole(
findPermissionId(partnerUuid, 'view'),
findPermissionId(partnerUuid, 'SELECT'),
hsOfficeRelationshipTenant(oldPartnerRel)
);
-- call revokePermissionFromRole(
-- findPermissionId(partnerUuid, 'edit'),
-- hsOfficeRelationshipAdmin(oldPartnerRel)
-- );
--
-- call revokePermissionFromRole(
-- findPermissionId(partnerUuid, '*'),
-- hsOfficeRelationshipOwner(oldPartnerRel)
-- );
-- call revokePermissionFromRole(
-- findPermissionId(partnerUuid, 'edit'),
-- hsOfficeRelationshipAdmin(oldPartnerRel)
-- );
--
-- call revokePermissionFromRole(
-- findPermissionId(partnerUuid, '*'),
-- hsOfficeRelationshipOwner(oldPartnerRel)
-- );
-- Grants for Partner
call grantPermissionsToRole(
getRoleId(hsOfficeRelationshipOwner(newPartnerRel), 'fail'),
array[findPermissionId(partnerUuid, '*')]
getRoleId(hsOfficeRelationshipOwner(newPartnerRel)),
array[findPermissionId(partnerUuid, 'DELETE')]
);
call grantPermissionsToRole(
getRoleId(hsOfficeRelationshipAdmin(newPartnerRel), 'fail'),
array[findPermissionId(partnerUuid, 'edit')]
getRoleId(hsOfficeRelationshipAdmin(newPartnerRel)),
array[findPermissionId(partnerUuid, 'UPDATE')]
);
call grantPermissionsToRole(
getRoleId(hsOfficeRelationshipTenant(newPartnerRel), 'fail'),
array[findPermissionId(partnerUuid, 'view')]
getRoleId(hsOfficeRelationshipTenant(newPartnerRel)),
array[findPermissionId(partnerUuid, 'SELECT')]
);
-- Revokes from PartnerDetails
-- call revokePermissionFromRole(
-- findPermissionId(partnerDetailsUuid, 'view'),
-- hsOfficeRelationshipAgent(oldPartnerRel)
-- );
--
-- call revokePermissionFromRole(
-- findPermissionId(partnerDetailsUuid, 'edit'),
-- hsOfficeRelationshipAdmin(oldPartnerRel)
-- );
--
-- call revokePermissionFromRole(
-- findPermissionId(partnerDetailsUuid, '*'),
-- hsOfficeRelationshipOwner(oldPartnerRel)
-- );
-- call revokePermissionFromRole(
-- findPermissionId(partnerDetailsUuid, 'SELECT'),
-- hsOfficeRelationshipAgent(oldPartnerRel)
-- );
--
-- call revokePermissionFromRole(
-- findPermissionId(partnerDetailsUuid, 'UPDATE'),
-- hsOfficeRelationshipAdmin(oldPartnerRel)
-- );
--
-- call revokePermissionFromRole(
-- findPermissionId(partnerDetailsUuid, 'DELETE'),
-- hsOfficeRelationshipOwner(oldPartnerRel)
-- );
-- Grants for PartnerDetails
call grantPermissionsToRole(
getRoleId(hsOfficeRelationshipOwner(newPartnerRel), 'fail'),
array[findPermissionId(partnerDetailsUuid, '*')]
getRoleId(hsOfficeRelationshipOwner(newPartnerRel)),
array[findPermissionId(partnerDetailsUuid, 'DELETE')]
);
call grantPermissionsToRole(
getRoleId(hsOfficeRelationshipAdmin(newPartnerRel), 'fail'),
array[findPermissionId(partnerDetailsUuid, 'edit')]
getRoleId(hsOfficeRelationshipAdmin(newPartnerRel)),
array[findPermissionId(partnerDetailsUuid, 'UPDATE')]
);
call grantPermissionsToRole(
-- Yes, here hsOfficePartnerAGENT is used, not hsOfficePartnerTENANT.
-- Do NOT grant view permission on partner-details to hsOfficeRelationshipTENANT!
-- Otherwise package-admins etc. would be able to read the data.
getRoleId(hsOfficeRelationshipAgent(newPartnerRel), 'fail'),
array[findPermissionId(partnerDetailsUuid, 'view')]
getRoleId(hsOfficeRelationshipAgent(newPartnerRel)),
array[findPermissionId(partnerDetailsUuid, 'SELECT')]
);
end if;
@ -203,7 +152,7 @@ begin
raise exception 'invalid usage of TRIGGER';
end if;
call leaveTriggerForObjectUuid(NEW.uuid);
call leaveTriggerForObjectUuid(partnerUuid);
return NEW;
end; $$;

View File

@ -1,50 +1,45 @@
### hs_office_bankaccount RBAC Roles
### rbac bankAccount
This code generated was by RbacViewMermaidFlowchartGenerator at 2024-03-11T19:09:38.350576842.
```mermaid
%%{init:{'flowchart':{'htmlLabels':false}}}%%
flowchart TB
%%% RbacEntity.builder().forEntity(HsOfficeBankAccountEntity.class)
%%% .alias("bankAccount")
%% the global subgraph would get imported implicitly by later usage
subgraph global
style global fill: #eee
role:global.admin[global.admin]
end
subgraph hsOfficeBankAccount
subgraph bankAccount["`**bankAccount**`"]
direction TB
style hsOfficeBankAccount fill: #e9f7ef
style bankAccount fill:#dd4901,stroke:#274d6e,stroke-width:8px
user:hsOfficeBankAccount.creator([bankAccount.creator])
subgraph bankAccount:roles[ ]
style bankAccount:roles fill:#dd4901,stroke:white
%%% .createRole(OWNER)
role:hsOfficeBankAccount.owner[[bankAccount.owner]]
%%% .withPermission(ALL)
%% permissions
role:hsOfficeBankAccount.owner --> perm:hsOfficeBankAccount.*{{hsOfficeBankAccount.*}}
%% incoming
%%% .withCreatorAsOwningUser()
user:hsOfficeBankAccount.creator ---> role:hsOfficeBankAccount.owner
%%% .withIncomingSuperRole(GlobalEntity.class, ADMIN)
role:global.admin --> role:hsOfficeBankAccount.owner
%%% .createSubRole(ADMIN)
role:hsOfficeBankAccount.admin[[bankAccount.admin]]
%% permissions
%%% .withPermission(EDIT)
role:hsOfficeBankAccount.admin --> perm:hsOfficeBankAccount.edit{{hsOfficeBankAccount.edit}}
%% incoming
role:hsOfficeBankAccount.owner ---> role:hsOfficeBankAccount.admin
role:bankAccount:owner[[bankAccount:owner]]
role:bankAccount:admin[[bankAccount:admin]]
role:bankAccount:referrer[[bankAccount:referrer]]
end
%%% .createSubRole(REFERRER)
role:hsOfficeBankAccount.referrer[[bankAccount.referrer]]
%% permissions
%%% .withPermission(VIEW)
role:hsOfficeBankAccount.referrer --> perm:hsOfficeBankAccount.view{{hsOfficeBankAccount.view}}
%% incoming
role:hsOfficeBankAccount.admin ---> role:hsOfficeBankAccount.referrer
subgraph bankAccount:permissions[ ]
style bankAccount:permissions fill:#dd4901,stroke:white
perm:bankAccount:INSERT{{bankAccount:INSERT}}
perm:bankAccount:DELETE{{bankAccount:DELETE}}
perm:bankAccount:UPDATE{{bankAccount:UPDATE}}
perm:bankAccount:SELECT{{bankAccount:SELECT}}
end
end
```
%% granting roles to users
user:creator ==> role:bankAccount:owner
%% granting roles to roles
role:global:admin ==> role:bankAccount:owner
role:bankAccount:owner ==> role:bankAccount:admin
role:bankAccount:admin ==> role:bankAccount:referrer
%% granting permissions to roles
role:global:guest ==> perm:bankAccount:INSERT
role:bankAccount:owner ==> perm:bankAccount:DELETE
role:bankAccount:admin ==> perm:bankAccount:UPDATE
role:bankAccount:referrer ==> perm:bankAccount:SELECT
```

View File

@ -1,4 +1,5 @@
--liquibase formatted sql
-- This code generated was by RbacViewPostgresGenerator at 2024-03-11T19:09:38.359318650.
-- ============================================================================
--changeset hs-office-bankaccount-rbac-OBJECT:1 endDelimiter:--//
@ -15,125 +16,141 @@ call generateRbacRoleDescriptors('hsOfficeBankAccount', 'hs_office_bankaccount')
-- ============================================================================
--changeset hs-office-bankaccount-rbac-ROLES-CREATION:1 endDelimiter:--//
--changeset hs-office-bankaccount-rbac-insert-trigger:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
/*
Creates the roles and their assignments for a new bankaccount for the AFTER INSERT TRIGGER.
Creates the roles, grants and permission for the AFTER INSERT TRIGGER.
*/
create or replace function createRbacRolesForHsOfficeBankAccount()
create or replace procedure buildRbacSystemForHsOfficeBankAccount(
NEW hs_office_bankaccount
)
language plpgsql as $$
declare
begin
call enterTriggerForObjectUuid(NEW.uuid);
perform createRoleWithGrants(
hsOfficeBankAccountOwner(NEW),
permissions => array['DELETE'],
userUuids => array[currentUserUuid()],
incomingSuperRoles => array[globalAdmin()]
);
perform createRoleWithGrants(
hsOfficeBankAccountAdmin(NEW),
permissions => array['UPDATE'],
incomingSuperRoles => array[hsOfficeBankAccountOwner(NEW)]
);
perform createRoleWithGrants(
hsOfficeBankAccountReferrer(NEW),
permissions => array['SELECT'],
incomingSuperRoles => array[hsOfficeBankAccountAdmin(NEW)]
);
call leaveTriggerForObjectUuid(NEW.uuid);
end; $$;
/*
AFTER INSERT TRIGGER to create the role+grant structure for a new hs_office_bankaccount row.
*/
create or replace function insertTriggerForHsOfficeBankAccount_tf()
returns trigger
language plpgsql
strict as $$
begin
if TG_OP <> 'INSERT' then
raise exception 'invalid usage of TRIGGER AFTER INSERT';
end if;
perform createRoleWithGrants(
hsOfficeBankAccountOwner(NEW),
permissions => array['DELETE'],
incomingSuperRoles => array[globalAdmin()],
userUuids => array[currentUserUuid()],
grantedByRole => globalAdmin()
);
perform createRoleWithGrants(
hsOfficeBankAccountAdmin(NEW),
incomingSuperRoles => array[hsOfficeBankAccountOwner(NEW)]
);
perform createRoleWithGrants(
hsOfficeBankAccountTenant(NEW),
incomingSuperRoles => array[hsOfficeBankAccountAdmin(NEW)]
);
perform createRoleWithGrants(
hsOfficeBankAccountGuest(NEW),
permissions => array['SELECT'],
incomingSuperRoles => array[hsOfficeBankAccountTenant(NEW)]
);
call buildRbacSystemForHsOfficeBankAccount(NEW);
return NEW;
end; $$;
/*
An AFTER INSERT TRIGGER which creates the role structure for a new customer.
*/
create trigger createRbacRolesForHsOfficeBankAccount_Trigger
after insert
on hs_office_bankaccount
create trigger insertTriggerForHsOfficeBankAccount_tg
after insert on hs_office_bankaccount
for each row
execute procedure createRbacRolesForHsOfficeBankAccount();
execute procedure insertTriggerForHsOfficeBankAccount_tf();
--//
-- ============================================================================
--changeset hs-office-bankaccount-rbac-INSERT:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
/*
Creates INSERT INTO hs_office_bankaccount permissions for the related global rows.
*/
do language plpgsql $$
declare
row global;
permissionUuid uuid;
roleUuid uuid;
begin
call defineContext('create INSERT INTO hs_office_bankaccount permissions for the related global rows');
FOR row IN SELECT * FROM global
LOOP
roleUuid := findRoleId(globalGuest());
permissionUuid := createPermission(row.uuid, 'INSERT', 'hs_office_bankaccount');
call grantPermissionToRole(roleUuid, permissionUuid);
END LOOP;
END;
$$;
/**
Adds hs_office_bankaccount INSERT permission to specified role of new global rows.
*/
create or replace function hs_office_bankaccount_global_insert_tf()
returns trigger
language plpgsql
strict as $$
begin
call grantPermissionToRole(
globalGuest(),
createPermission(NEW.uuid, 'INSERT', 'hs_office_bankaccount'));
return NEW;
end; $$;
create trigger hs_office_bankaccount_global_insert_tg
after insert on global
for each row
execute procedure hs_office_bankaccount_global_insert_tf();
/**
Checks if the user or assumed roles are allowed to insert a row to hs_office_bankaccount.
*/
create or replace function hs_office_bankaccount_insert_permission_missing_tf()
returns trigger
language plpgsql as $$
begin
raise exception '[403] insert into hs_office_bankaccount not allowed for current subjects % (%)',
currentSubjects(), currentSubjectsUuids();
end; $$;
--//
-- ============================================================================
--changeset hs-office-bankaccount-rbac-IDENTITY-VIEW:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
call generateRbacIdentityViewFromProjection('hs_office_bankaccount', $idName$
target.iban || ':' || target.holder
concat(iban, ':', holder)
$idName$);
--//
-- ============================================================================
--changeset hs-office-bankaccount-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
call generateRbacRestrictedView('hs_office_bankaccount', 'target.holder',
call generateRbacRestrictedView('hs_office_bankaccount',
$orderBy$
concat(iban, ':', holder)
$orderBy$,
$updates$
holder = new.holder,
holder = new.holder,
iban = new.iban,
bic = new.bic
$updates$);
--/
-- ============================================================================
--changeset hs-office-bankaccount-rbac-NEW-BANKACCOUNT:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
/*
Creates a global permission for new-bankaccount and assigns it to the hostsharing admins role.
*/
do language plpgsql $$
declare
addCustomerPermissions uuid[];
globalObjectUuid uuid;
globalAdminRoleUuid uuid ;
begin
call defineContext('granting global new-bankaccount permission to global admin role', null, null, null);
globalAdminRoleUuid := findRoleId(globalAdmin());
globalObjectUuid := (select uuid from global);
addCustomerPermissions := createPermissions(globalObjectUuid, array ['new-bankaccount']);
call grantPermissionsToRole(globalAdminRoleUuid, addCustomerPermissions);
end;
$$;
/**
Used by the trigger to prevent the add-customer to current user respectively assumed roles.
*/
create or replace function addHsOfficeBankAccountNotAllowedForCurrentSubjects()
returns trigger
language PLPGSQL
as $$
begin
raise exception '[403] new-bankaccount not permitted for %',
array_to_string(currentSubjects(), ';', 'null');
end; $$;
/**
Checks if the user or assumed roles are allowed to create a new customer.
*/
create trigger hs_office_bankaccount_insert_trigger
before insert
on hs_office_bankaccount
for each row
-- TODO.spec: who is allowed to create new bankaccounts
when ( not hasAssumedRole() )
execute procedure addHsOfficeBankAccountNotAllowedForCurrentSubjects();
--//

View File

@ -1,62 +1,178 @@
### hs_office_sepaMandate RBAC
### rbac sepaMandate
This code generated was by RbacViewMermaidFlowchartGenerator at 2024-03-11T18:29:47.084556363.
```mermaid
%%{init:{'flowchart':{'htmlLabels':false}}}%%
flowchart TB
subgraph global
style global fill:#eee
role:global.admin[global.admin]
end
subgraph hsOfficeBankAccount
subgraph bankAccount["`**bankAccount**`"]
direction TB
style hsOfficeBankAccount fill:#eee
role:hsOfficeBankAccount.owner[bankAccount.owner]
--> role:hsOfficeBankAccount.admin[bankAccount.admin]
--> role:hsOfficeBankAccount.referrer[bankAccount.referrer]
style bankAccount fill:#99bcdb,stroke:#274d6e,stroke-width:8px
subgraph bankAccount:roles[ ]
style bankAccount:roles fill:#99bcdb,stroke:white
role:bankAccount:owner[[bankAccount:owner]]
role:bankAccount:admin[[bankAccount:admin]]
role:bankAccount:referrer[[bankAccount:referrer]]
end
end
subgraph hsOfficeRelationship:DEBITOR
subgraph debitorRel.contact["`**debitorRel.contact**`"]
direction TB
style hsOfficeRelationship:DEBITOR fill:#eee
role:hsOfficeRelationship:DEBITOR.owner[debitorRel.owner]
--> role:hsOfficeRelationship:DEBITOR.admin[debitorRel.admin]
--> role:hsOfficeRelationship:DEBITOR.agent[debitorRel.agent]
--> role:hsOfficeRelationship:DEBITOR.tenant[debitorRel.tenant]
style debitorRel.contact fill:#99bcdb,stroke:#274d6e,stroke-width:8px
subgraph debitorRel.contact:roles[ ]
style debitorRel.contact:roles fill:#99bcdb,stroke:white
role:debitorRel.contact:owner[[debitorRel.contact:owner]]
role:debitorRel.contact:admin[[debitorRel.contact:admin]]
role:debitorRel.contact:referrer[[debitorRel.contact:referrer]]
end
end
subgraph hsOfficeSepaMandate
role:hsOfficeSepaMandate.owner[sepaMandate.owner]
%% permissions
role:hsOfficeSepaMandate.owner --> perm:hsOfficeSepaMandate.*{{sepaMandate.*}}
%% incoming
role:global.admin ---> role:hsOfficeSepaMandate.owner
role:hsOfficeSepaMandate.admin[sepaMandate.admin]
%% permissions
role:hsOfficeSepaMandate.admin --> perm:hsOfficeSepaMandate.edit{{sepaMandate.edit}}
%% incoming
role:hsOfficeSepaMandate.owner ---> role:hsOfficeSepaMandate.admin
role:hsOfficeSepaMandate.agent[sepaMandate.agent]
%% incoming
role:hsOfficeSepaMandate.admin ---> role:hsOfficeSepaMandate.agent
role:hsOfficeRelationship:DEBITOR.admin --> role:hsOfficeSepaMandate.agent
role:hsOfficeBankAccount.admin --> role:hsOfficeSepaMandate.agent
%% outgoing
role:hsOfficeSepaMandate.admin --> role:hsOfficeBankAccount.referrer
role:hsOfficeSepaMandate.agent --> role:hsOfficeRelationship:DEBITOR.tenant
role:hsOfficeSepaMandate.agent --> role:hsOfficeBankAccount.referrer
role:hsOfficeSepaMandate.tenant[sepaMandate.tenant]
%% incoming
role:hsOfficeSepaMandate.agent --> role:hsOfficeSepaMandate.tenant
subgraph debitorRel.anchorPerson["`**debitorRel.anchorPerson**`"]
direction TB
style debitorRel.anchorPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
subgraph debitorRel.anchorPerson:roles[ ]
style debitorRel.anchorPerson:roles fill:#99bcdb,stroke:white
role:debitorRel.anchorPerson:owner[[debitorRel.anchorPerson:owner]]
role:debitorRel.anchorPerson:admin[[debitorRel.anchorPerson:admin]]
role:debitorRel.anchorPerson:referrer[[debitorRel.anchorPerson:referrer]]
end
end
subgraph debitorRel.holderPerson["`**debitorRel.holderPerson**`"]
direction TB
style debitorRel.holderPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
subgraph debitorRel.holderPerson:roles[ ]
style debitorRel.holderPerson:roles fill:#99bcdb,stroke:white
role:debitorRel.holderPerson:owner[[debitorRel.holderPerson:owner]]
role:debitorRel.holderPerson:admin[[debitorRel.holderPerson:admin]]
role:debitorRel.holderPerson:referrer[[debitorRel.holderPerson:referrer]]
end
end
subgraph sepaMandate["`**sepaMandate**`"]
direction TB
style sepaMandate fill:#dd4901,stroke:#274d6e,stroke-width:8px
subgraph sepaMandate:roles[ ]
style sepaMandate:roles fill:#dd4901,stroke:white
role:sepaMandate:owner[[sepaMandate:owner]]
role:sepaMandate:admin[[sepaMandate:admin]]
role:sepaMandate:agent[[sepaMandate:agent]]
role:sepaMandate:referrer[[sepaMandate:referrer]]
end
subgraph sepaMandate:permissions[ ]
style sepaMandate:permissions fill:#dd4901,stroke:white
perm:sepaMandate:DELETE{{sepaMandate:DELETE}}
perm:sepaMandate:UPDATE{{sepaMandate:UPDATE}}
perm:sepaMandate:SELECT{{sepaMandate:SELECT}}
end
end
subgraph debitorRel["`**debitorRel**`"]
direction TB
style debitorRel fill:#99bcdb,stroke:#274d6e,stroke-width:8px
subgraph debitorRel.contact["`**debitorRel.contact**`"]
direction TB
style debitorRel.contact fill:#99bcdb,stroke:#274d6e,stroke-width:8px
subgraph debitorRel.contact:roles[ ]
style debitorRel.contact:roles fill:#99bcdb,stroke:white
role:debitorRel.contact:owner[[debitorRel.contact:owner]]
role:debitorRel.contact:admin[[debitorRel.contact:admin]]
role:debitorRel.contact:referrer[[debitorRel.contact:referrer]]
end
end
subgraph debitorRel.anchorPerson["`**debitorRel.anchorPerson**`"]
direction TB
style debitorRel.anchorPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
subgraph debitorRel.anchorPerson:roles[ ]
style debitorRel.anchorPerson:roles fill:#99bcdb,stroke:white
role:debitorRel.anchorPerson:owner[[debitorRel.anchorPerson:owner]]
role:debitorRel.anchorPerson:admin[[debitorRel.anchorPerson:admin]]
role:debitorRel.anchorPerson:referrer[[debitorRel.anchorPerson:referrer]]
end
end
subgraph debitorRel.holderPerson["`**debitorRel.holderPerson**`"]
direction TB
style debitorRel.holderPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
subgraph debitorRel.holderPerson:roles[ ]
style debitorRel.holderPerson:roles fill:#99bcdb,stroke:white
role:debitorRel.holderPerson:owner[[debitorRel.holderPerson:owner]]
role:debitorRel.holderPerson:admin[[debitorRel.holderPerson:admin]]
role:debitorRel.holderPerson:referrer[[debitorRel.holderPerson:referrer]]
end
end
subgraph debitorRel:roles[ ]
style debitorRel:roles fill:#99bcdb,stroke:white
role:debitorRel:owner[[debitorRel:owner]]
role:debitorRel:admin[[debitorRel:admin]]
role:debitorRel:agent[[debitorRel:agent]]
role:debitorRel:tenant[[debitorRel:tenant]]
end
end
%% granting roles to users
user:creator ==> role:sepaMandate:owner
%% granting roles to roles
role:global:admin -.-> role:debitorRel.anchorPerson:owner
role:debitorRel.anchorPerson:owner -.-> role:debitorRel.anchorPerson:admin
role:debitorRel.anchorPerson:admin -.-> role:debitorRel.anchorPerson:referrer
role:global:admin -.-> role:debitorRel.holderPerson:owner
role:debitorRel.holderPerson:owner -.-> role:debitorRel.holderPerson:admin
role:debitorRel.holderPerson:admin -.-> role:debitorRel.holderPerson:referrer
role:global:admin -.-> role:debitorRel.contact:owner
role:debitorRel.contact:owner -.-> role:debitorRel.contact:admin
role:debitorRel.contact:admin -.-> role:debitorRel.contact:referrer
role:global:admin -.-> role:debitorRel:owner
role:debitorRel:owner -.-> role:debitorRel:admin
role:debitorRel.anchorPerson:admin -.-> role:debitorRel:admin
role:debitorRel:admin -.-> role:debitorRel:agent
role:debitorRel.holderPerson:admin -.-> role:debitorRel:agent
role:debitorRel:agent -.-> role:debitorRel:tenant
role:debitorRel.holderPerson:admin -.-> role:debitorRel:tenant
role:debitorRel.contact:admin -.-> role:debitorRel:tenant
role:debitorRel:tenant -.-> role:debitorRel.anchorPerson:referrer
role:debitorRel:tenant -.-> role:debitorRel.holderPerson:referrer
role:debitorRel:tenant -.-> role:debitorRel.contact:referrer
role:global:admin -.-> role:bankAccount:owner
role:bankAccount:owner -.-> role:bankAccount:admin
role:bankAccount:admin -.-> role:bankAccount:referrer
role:global:admin ==> role:sepaMandate:owner
role:sepaMandate:owner ==> role:sepaMandate:admin
role:sepaMandate:admin ==> role:sepaMandate:agent
role:sepaMandate:agent ==> role:bankAccount:referrer
role:sepaMandate:agent ==> role:debitorRel:agent
role:sepaMandate:agent ==> role:sepaMandate:referrer
role:bankAccount:admin ==> role:sepaMandate:referrer
role:debitorRel:agent ==> role:sepaMandate:referrer
role:sepaMandate:referrer ==> role:debitorRel:tenant
%% granting permissions to roles
role:sepaMandate:owner ==> perm:sepaMandate:DELETE
role:sepaMandate:admin ==> perm:sepaMandate:UPDATE
role:sepaMandate:referrer ==> perm:sepaMandate:SELECT
```

View File

@ -1,4 +1,5 @@
--liquibase formatted sql
-- This code generated was by RbacViewPostgresGenerator at 2024-03-11T18:29:47.095199204.
-- ============================================================================
--changeset hs-office-sepamandate-rbac-OBJECT:1 endDelimiter:--//
@ -15,105 +16,123 @@ call generateRbacRoleDescriptors('hsOfficeSepaMandate', 'hs_office_sepamandate')
-- ============================================================================
--changeset hs-office-sepamandate-rbac-ROLES-CREATION:1 endDelimiter:--//
--changeset hs-office-sepamandate-rbac-insert-trigger:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
/*
Creates and updates the roles and their assignments for sepaMandate entities.
Creates the roles, grants and permission for the AFTER INSERT TRIGGER.
*/
create or replace function hsOfficeSepaMandateRbacRolesTrigger()
returns trigger
language plpgsql
strict as $$
create or replace procedure buildRbacSystemForHsOfficeSepaMandate(
NEW hs_office_sepamandate
)
language plpgsql as $$
declare
newHsOfficeDebitor hs_office_debitor;
newhsOfficeRelationship:DEBITOR hs_office_relationship;
newHsOfficeBankAccount hs_office_bankAccount;
newBankAccount hs_office_bankaccount;
newDebitorRel hs_office_relationship;
begin
call enterTriggerForObjectUuid(NEW.uuid);
SELECT * FROM hs_office_bankaccount WHERE uuid = NEW.bankAccountUuid into newBankAccount;
SELECT * FROM hs_office_relationship WHERE uuid = NEW.debitorUuid into newDebitorRel;
select * from hs_office_debitor as p where p.uuid = NEW.debitorUuid into newHsOfficeDebitor;
select * from hs_office_relationship as r where r.uuid = newHsOfficeDebitor.debitorRelUuid into newhsOfficeRelationship:DEBITOR;
select * from hs_office_bankAccount as c where c.uuid = NEW.bankAccountUuid into newHsOfficeBankAccount;
perform createRoleWithGrants(
hsOfficeSepaMandateOwner(NEW),
permissions => array['DELETE'],
userUuids => array[currentUserUuid()],
incomingSuperRoles => array[globalAdmin()]
);
if TG_OP = 'INSERT' then
perform createRoleWithGrants(
hsOfficeSepaMandateAdmin(NEW),
permissions => array['UPDATE'],
incomingSuperRoles => array[hsOfficeSepaMandateOwner(NEW)]
);
-- === ATTENTION: code generated from related Mermaid flowchart: ===
perform createRoleWithGrants(
hsOfficeSepaMandateAgent(NEW),
incomingSuperRoles => array[hsOfficeSepaMandateAdmin(NEW)],
outgoingSubRoles => array[
hsOfficeBankAccountReferrer(newBankAccount),
hsOfficeRelationshipAgent(newDebitorRel)]
);
perform createRoleWithGrants(
hsOfficeSepaMandateOwner(NEW),
permissions => array['DELETE'],
incomingSuperRoles => array[globalAdmin()]
);
perform createRoleWithGrants(
hsOfficeSepaMandateAdmin(NEW),
permissions => array['UPDATE'],
incomingSuperRoles => array[
hsOfficeSepaMandateOwner(NEW)],
outgoingSubRoles => array[
hsOfficeBankAccountTenant(newHsOfficeBankAccount)]
);
perform createRoleWithGrants(
hsOfficeSepaMandateAgent(NEW),
incomingSuperRoles => array[
hsOfficeSepaMandateAdmin(NEW),
hsOfficeRelationshipAdmin(newhsOfficeRelationship:DEBITOR),
hsOfficeBankAccountAdmin(newHsOfficeBankAccount)],
outgoingSubRoles => array[
hsOfficeRelationshipTenant(newhsOfficeRelationship:DEBITOR)]
);
perform createRoleWithGrants(
hsOfficeSepaMandateTenant(NEW),
incomingSuperRoles => array[hsOfficeSepaMandateAgent(NEW)],
outgoingSubRoles => array[
hsOfficeRelationshipReferrer(newhsOfficeRelationship:DEBITOR),
hsOfficeBankAccountGuest(newHsOfficeBankAccount)]
);
perform createRoleWithGrants(
hsOfficeSepaMandateGuest(NEW),
permissions => array['SELECT'],
incomingSuperRoles => array[hsOfficeSepaMandateTenant(NEW)]
);
-- === END of code generated from Mermaid flowchart. ===
else
raise exception 'invalid usage of TRIGGER';
end if;
perform createRoleWithGrants(
hsOfficeSepaMandateReferrer(NEW),
permissions => array['SELECT'],
incomingSuperRoles => array[
hsOfficeRelationshipAgent(newDebitorRel),
hsOfficeBankAccountAdmin(newBankAccount),
hsOfficeSepaMandateAgent(NEW)],
outgoingSubRoles => array[hsOfficeRelationshipTenant(newDebitorRel)]
);
call leaveTriggerForObjectUuid(NEW.uuid);
return NEW;
end; $$;
/*
An AFTER INSERT TRIGGER which creates the role structure for a new customer.
AFTER INSERT TRIGGER to create the role+grant structure for a new hs_office_sepamandate row.
*/
create trigger createRbacRolesForHsOfficeSepaMandate_Trigger
after insert
on hs_office_sepamandate
create or replace function insertTriggerForHsOfficeSepaMandate_tf()
returns trigger
language plpgsql
strict as $$
begin
call buildRbacSystemForHsOfficeSepaMandate(NEW);
return NEW;
end; $$;
create trigger insertTriggerForHsOfficeSepaMandate_tg
after insert on hs_office_sepamandate
for each row
execute procedure hsOfficeSepaMandateRbacRolesTrigger();
execute procedure insertTriggerForHsOfficeSepaMandate_tf();
--//
-- ============================================================================
--changeset hs-office-sepamandate-rbac-INSERT:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
/**
Checks if the user or assumed roles are allowed to insert a row to hs_office_sepamandate.
*/
create or replace function hs_office_sepamandate_insert_permission_missing_tf()
returns trigger
language plpgsql as $$
begin
raise exception '[403] insert into hs_office_sepamandate not allowed for current subjects % (%)',
currentSubjects(), currentSubjectsUuids();
end; $$;
create trigger hs_office_sepamandate_insert_permission_check_tg
before insert on hs_office_sepamandate
for each row
-- As there is no explicit INSERT grant specified for this table,
-- only global admins are allowed to insert any rows.
when ( not isGlobalAdmin() )
execute procedure hs_office_sepamandate_insert_permission_missing_tf();
--//
-- ============================================================================
--changeset hs-office-sepamandate-rbac-IDENTITY-VIEW:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
call generateRbacIdentityViewFromProjection('hs_office_sepamandate', 'target.reference');
call generateRbacIdentityViewFromQuery('hs_office_sepamandate', $idName$
select sm.uuid as uuid, ba.iban || '-' || sm.validity as idName
from hs_office_sepamandate sm
join hs_office_bankaccount ba on ba.uuid = sm.bankAccountUuid
$idName$);
--//
-- ============================================================================
--changeset hs-office-sepamandate-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
call generateRbacRestrictedView('hs_office_sepamandate',
orderby => 'target.reference',
columnUpdates => $updates$
'validity',
$updates$
reference = new.reference,
agreement = new.agreement,
validity = new.validity
@ -121,48 +140,3 @@ call generateRbacRestrictedView('hs_office_sepamandate',
--//
-- ============================================================================
--changeset hs-office-sepamandate-rbac-NEW-SepaMandate:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
/*
Creates a global permission for new-sepaMandate and assigns it to the hostsharing admins role.
*/
do language plpgsql $$
declare
addCustomerPermissions uuid[];
globalObjectUuid uuid;
globalAdminRoleUuid uuid ;
begin
call defineContext('granting global new-sepaMandate permission to global admin role', null, null, null);
globalAdminRoleUuid := findRoleId(globalAdmin());
globalObjectUuid := (select uuid from global);
addCustomerPermissions := createPermissions(globalObjectUuid, array ['new-sepamandate']);
call grantPermissionsToRole(globalAdminRoleUuid, addCustomerPermissions);
end;
$$;
/**
Used by the trigger to prevent the add-customer to current user respectively assumed roles.
*/
create or replace function addHsOfficeSepaMandateNotAllowedForCurrentSubjects()
returns trigger
language PLPGSQL
as $$
begin
raise exception '[403] new-sepaMandate not permitted for %',
array_to_string(currentSubjects(), ';', 'null');
end; $$;
/**
Checks if the user or assumed roles are allowed to create a new customer.
*/
create trigger hs_office_sepamandate_insert_trigger
before insert
on hs_office_sepamandate
for each row
-- TODO.spec: who is allowed to create new sepaMandates
when ( not hasAssumedRole() )
execute procedure addHsOfficeSepaMandateNotAllowedForCurrentSubjects();
--//

View File

@ -50,25 +50,21 @@ begin
-- Permissions and Grants for Debitor
-- call grantPermissionsToRole(
-- getRoleId(hsOfficeRelationshipOwner(newDebitorRel), 'fail'),
-- getRoleId(hsOfficeRelationshipOwner(newDebitorRel)),
-- createPermissions(partnerUuid, array ['DELETE'])
-- );
--
-- call grantPermissionsToRole(
-- getRoleId(hsOfficeRelationshipAdmin(newDebitorRel), 'fail'),
-- createPermissions(partnerUuid, array ['edit'])
-- createPermissions(partnerUuid, array ['UPDATE'])
-- );
--
-- call grantPermissionsToRole(
-- getRoleId(hsOfficeRelationshipTenant(newDebitorRel), 'fail'),
-- createPermissions(partnerUuid, array ['view'])
-- createPermissions(partnerUuid, array ['SELECT'])
-- );
perform createRoleWithGrants(
hsOfficeDebitorAdmin(NEW),
permissions => array['UPDATE'],
incomingSuperRoles => array[hsOfficeDebitorOwner(NEW)]
);
-- Grants to and from related Partner Relationship
-- call grantRoleToRole(hsOfficeRelationshipAdmin(newDebitorRel), hsOfficeRelationshipAdmin(newPartnerRel), true);
-- call grantRoleToRole(hsOfficeRelationshipAgent(newPartnerRel), hsOfficeRelationshipAdmin(newDebitorRel), true);
@ -78,12 +74,10 @@ begin
-- Grants to and from refundBankAccount
perform createRoleWithGrants(
hsOfficeDebitorGuest(NEW),
permissions => array['SELECT'],
incomingSuperRoles => array[
hsOfficeDebitorTenant(NEW)]
);
-- if newBankAccount is not null then
-- call grantRoleToRole(hsOfficeBankAccountReferrer(newBankAccount), hsOfficeRelationshipAgent(newDebitorRel), true);
-- call grantRoleToRole(hsOfficeRelationshipAgent(newDebitorRel), hsOfficeBankAccountAdmin(newBankAccount), true);
-- end if;
elsif TG_OP = 'UPDATE' then
@ -93,18 +87,18 @@ begin
from hs_office_relationship as r where r.relType = 'ACCOUNTING' and r.relHolderUuid = NEW.debitorRelUuid;
-- call grantPermissionsToRole(
-- getRoleId(hsOfficeRelationshipOwner(newDebitorRel), 'fail'),
-- createPermissions(partnerUuid, array ['*'])
-- getRoleId(hsOfficeRelationshipOwner(newDebitorRel)),
-- createPermissions(partnerUuid, array ['DELETE'])
-- );
--
-- call grantPermissionsToRole(
-- getRoleId(hsOfficeRelationshipAdmin(newDebitorRel), 'fail'),
-- createPermissions(partnerUuid, array ['edit'])
-- getRoleId(hsOfficeRelationshipAdmin(newDebitorRel)),
-- createPermissions(partnerUuid, array ['UPDATE'])
-- );
--
-- call grantPermissionsToRole(
-- getRoleId(hsOfficeRelationshipTenant(newDebitorRel), 'fail'),
-- createPermissions(partnerUuid, array ['view'])
-- getRoleId(hsOfficeRelationshipTenant(newDebitorRel)),
-- createPermissions(partnerUuid, array ['SELECT'])
-- );
end if;

View File

@ -9,8 +9,8 @@ import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEnti
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipType;
import net.hostsharing.hsadminng.hs.office.test.ContextBasedTestWithCleanup;
import net.hostsharing.hsadminng.rbac.rbacgrant.RawRbacGrantRepository;
import net.hostsharing.hsadminng.rbac.rbacgrant.RbacGrantsMermaidService;
import net.hostsharing.hsadminng.rbac.rbacgrant.RbacGrantsMermaidService.Include;
import net.hostsharing.hsadminng.rbac.rbacgrant.RbacGrantsDiagramService;
import net.hostsharing.hsadminng.rbac.rbacgrant.RbacGrantsDiagramService.Include;
import net.hostsharing.hsadminng.rbac.rbacrole.RawRbacRoleRepository;
import net.hostsharing.test.Array;
import net.hostsharing.test.JpaAttempt;
@ -40,7 +40,7 @@ import static net.hostsharing.test.JpaAttempt.attempt;
import static org.assertj.core.api.Assertions.assertThat;
@DataJpaTest
@Import( { Context.class, JpaAttempt.class, RbacGrantsMermaidService.class })
@Import( { Context.class, JpaAttempt.class, RbacGrantsDiagramService.class })
class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithCleanup {
@Autowired
@ -71,7 +71,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
JpaAttempt jpaAttempt;
@Autowired
RbacGrantsMermaidService mermaidService;
RbacGrantsDiagramService mermaidService;
@MockBean
HttpServletRequest request;
@ -315,7 +315,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
context("superuser-alex@hostsharing.net");
final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "fifth contact", "Fourth", "fif");
RbacGrantsMermaidService.writeToFile("initial partner: Fourth eG + fourth contact",
RbacGrantsDiagramService.writeToFile("initial partner: Fourth eG + fourth contact",
mermaidService.allGrantsFrom(givenDebitor.getUuid(), "view", EnumSet.of(Include.USERS, Include.DETAILS)),
"doc/all-grants-before-globalAdmin_canUpdateArbitraryDebitor.md");

View File

@ -465,24 +465,14 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
final var givenPartnerPerson = personRepo.findPersonByOptionalNameLike(person).get(0);
final var givenContact = contactRepo.findContactByOptionalLabelLike(contact).get(0);
final var partnerRole = HsOfficeRelationshipEntity.builder()
.relHolder(givenPartnerPerson)
.relType(HsOfficeRelationshipType.PARTNER)
.relAnchor(givenMandantorPerson)
.contact(givenContact)
.build();
relationshipRepo.save(partnerRole);
final var newPartner = HsOfficePartnerEntity.builder()
.partnerNumber(partnerNumber)
.partnerRole(partnerRole)
.person(givenPartnerPerson)
.contact(givenContact)
.details(HsOfficePartnerDetailsEntity.builder().build())
.build();
return partnerRepo.save(newPartner);
}).assertSuccessful().returnedValue();
final var partnerRole = HsOfficeRelationshipEntity.builder()
.relHolder(givenPartnerPerson)
.relType(HsOfficeRelationshipType.PARTNER)
.relAnchor(givenMandantorPerson)
.contact(givenContact)
.build();
relationshipRepo.save(partnerRole);
return partnerRole;
}
void exactlyThesePartnersAreReturned(final List<HsOfficePartnerEntity> actualResult, final String... partnerNames) {

View File

@ -59,7 +59,6 @@ class HsOfficePersonRepositoryIntegrationTest extends ContextBasedTestWithCleanu
final var count = personRepo.count();
// when
final var result = attempt(em, () -> toCleanup(personRepo.save(
hsOfficePerson("a new person"))));

View File

@ -1,136 +0,0 @@
package net.hostsharing.hsadminng.rbac.rbacgrant;
import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.hs.office.test.ContextBasedTestWithCleanup;
import net.hostsharing.hsadminng.rbac.rbacgrant.RbacGrantsMermaidService.Include;
import net.hostsharing.test.JpaAttempt;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import jakarta.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.EnumSet;
import java.util.UUID;
import static java.lang.String.join;
import static org.assertj.core.api.Assertions.assertThat;
@DataJpaTest
@Import( { Context.class, JpaAttempt.class, RbacGrantsMermaidService.class})
class RbacGrantsMermaidServiceIntegrationTest extends ContextBasedTestWithCleanup {
@Autowired
RbacGrantsMermaidService grantsMermaidService;
@MockBean
HttpServletRequest request;
@Test
void allGrantsToCurrentUser() {
context("superuser-alex@hostsharing.net", "test_domain#xxx00-aaaa.owner");
final var graph = grantsMermaidService.allGrantsToCurrentUser(EnumSet.of(Include.TEST_ENTITIES));
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]
""".trim());
}
@Test
void allGrantsToCurrentUserIncludingPermissions() {
context("superuser-alex@hostsharing.net", "test_domain#xxx00-aaaa.owner");
final var graph = grantsMermaidService.allGrantsToCurrentUser(EnumSet.of(Include.TEST_ENTITIES, Include.PERMISSIONS));
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]
""".trim());
}
@Test
// @Disabled
void print() throws IOException {
//context("superuser-alex@hostsharing.net", "hs_office_person#FirbySusan.admin");
context("superuser-alex@hostsharing.net");
//final var graph = grantsMermaidService.allGrantsToCurrentUser(EnumSet.of(Include.NON_TEST_ENTITIES, Include.PERMISSIONS));
final var targetObject = (UUID) em.createNativeQuery("SELECT uuid FROM hs_office_coopassetstransaction WHERE reference='ref 1000101-1'").getSingleResult();
final var graph = grantsMermaidService.allGrantsFrom(targetObject, "view", EnumSet.of(Include.USERS));
RbacGrantsMermaidService.writeToFile(join(";", context.getAssumedRoles()), graph, "doc/all-grants.md");
}
}

View File

@ -4,8 +4,8 @@ spring:
platform: postgres
datasource:
url: jdbc:tc:postgresql:15.5-bookworm:///spring_boot_testcontainers
url-local: jdbc:postgresql://localhost:5432/postgres
url-tc: jdbc:tc:postgresql:15.5-bookworm:///spring_boot_testcontainers
url: jdbc:postgresql://localhost:5432/postgres
username: postgres
password: password