test-data-generation working up to debitor, fails in membership

This commit is contained in:
Michael Hoennig 2024-03-12 16:34:16 +01:00
parent a9c3df6c7c
commit 76b98eab2e
16 changed files with 577 additions and 310 deletions

View File

@ -16,11 +16,11 @@ import org.hibernate.annotations.GenericGenerator;
import jakarta.persistence.*; import jakarta.persistence.*;
import java.io.IOException; import java.io.IOException;
import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import static java.util.Optional.ofNullable; import static java.util.Optional.ofNullable;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NULLABLE;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.fetchedBySql; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.fetchedBySql;
@ -108,7 +108,7 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
public static RbacView rbac() { public static RbacView rbac() {
return rbacViewFor("debitor", HsOfficeDebitorEntity.class) return rbacViewFor("debitor", HsOfficeDebitorEntity.class)
.withIdentityView(SQL.query(""" .withIdentityView(SQL.query("""
SELECT debitor.uuid, SELECT debitor.uuid AS uuid,
'D-' || (SELECT partner.partnerNumber 'D-' || (SELECT partner.partnerNumber
FROM hs_office_partner partner FROM hs_office_partner partner
JOIN hs_office_relationship partnerRel JOIN hs_office_relationship partnerRel
@ -116,9 +116,10 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
JOIN hs_office_relationship debitorRel JOIN hs_office_relationship debitorRel
ON debitorRel.relAnchorUuid = partnerRel.relHolderUuid AND partnerRel.relType = 'ACCOUNTING' ON debitorRel.relAnchorUuid = partnerRel.relHolderUuid AND partnerRel.relType = 'ACCOUNTING'
WHERE debitorRel.uuid = debitor.debitorRelUuid) WHERE debitorRel.uuid = debitor.debitorRelUuid)
|| to_char(debitorNumberSuffix, 'fm00') || to_char(debitorNumberSuffix, 'fm00') as idName
from hs_office_debitor as debitor FROM hs_office_debitor AS debitor
""")) """))
.withRestrictedViewOrderBy(SQL.projection("defaultPrefix"))
.withUpdatableColumns( .withUpdatableColumns(
"debitorRel", "debitorRel",
"billable", "billable",
@ -129,13 +130,13 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
"vatBusiness", "vatBusiness",
"vatReverseCharge", "vatReverseCharge",
"defaultPrefix" /* TODO: do we want that updatable? */) "defaultPrefix" /* TODO: do we want that updatable? */)
.createPermission(custom("new-debitor")).grantedTo("global", ADMIN) .toRole("global", ADMIN).grantPermission("debitor", INSERT)
.importRootEntityAliasProxy("debitorRel", HsOfficeRelationshipEntity.class, .importRootEntityAliasProxy("debitorRel", HsOfficeRelationshipEntity.class,
fetchedBySql(""" fetchedBySql("""
SELECT * SELECT *
FROM hs_office_relationship AS r FROM hs_office_relationship AS r
WHERE r.relType = 'ACCOUNTING' AND r.relHolderUuid = ${REF}.debitorRelUuid WHERE r.relType = 'ACCOUNTING' AND r.uuid = ${REF}.debitorRelUuid
"""), """),
dependsOnColumn("debitorRelUuid")) dependsOnColumn("debitorRelUuid"))
.createPermission(DELETE).grantedTo("debitorRel", OWNER) .createPermission(DELETE).grantedTo("debitorRel", OWNER)
@ -143,20 +144,26 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
.createPermission(SELECT).grantedTo("debitorRel", TENANT) .createPermission(SELECT).grantedTo("debitorRel", TENANT)
.importEntityAlias("refundBankAccount", HsOfficeBankAccountEntity.class, .importEntityAlias("refundBankAccount", HsOfficeBankAccountEntity.class,
dependsOnColumn("refundBankAccountUuid"), fetchedBySql(""" dependsOnColumn("refundBankAccountUuid"),
fetchedBySql("""
SELECT * SELECT *
FROM hs_office_relationship AS r FROM hs_office_relationship AS r
WHERE r.relType = 'ACCOUNTING' AND r.relHolderUuid = ${REF}.debitorRelUuid WHERE r.relType = 'ACCOUNTING' AND r.relHolderUuid = ${REF}.debitorRelUuid
""") """),
NULLABLE
) )
.toRole("refundBankAccount", ADMIN).grantRole("debitorRel", AGENT) .toRole("refundBankAccount", ADMIN).grantRole("debitorRel", AGENT)
.toRole("debitorRel", AGENT).grantRole("refundBankAccount", REFERRER) .toRole("debitorRel", AGENT).grantRole("refundBankAccount", REFERRER)
.importEntityAlias("partnerRel", HsOfficeRelationshipEntity.class, .importEntityAlias("partnerRel", HsOfficeRelationshipEntity.class,
dependsOnColumn("partnerRelUuid"), fetchedBySql(""" dependsOnColumn("debitorRelUuid"),
SELECT * fetchedBySql("""
SELECT partnerRel.*
FROM hs_office_relationship AS partnerRel FROM hs_office_relationship AS partnerRel
WHERE ${debitorRel}.relAnchorUuid = partnerRel.relHolderUuid JOIN hs_office_relationship AS debitorRel
ON debitorRel.relType = 'ACCOUNTING' AND debitorRel.relAnchorUuid = partnerRel.relHolderUuid
WHERE partnerRel.relType = 'PARTNER'
AND ${REF}.debitorRelUuid = debitorRel.uuid
""") """)
) )
.toRole("partnerRel", ADMIN).grantRole("debitorRel", ADMIN) .toRole("partnerRel", ADMIN).grantRole("debitorRel", ADMIN)
@ -169,6 +176,6 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
} }
public static void main(String[] args) throws IOException { public static void main(String[] args) throws IOException {
rbac().generateWithBaseFileName("273-hs-office-debitor-rbac-generated"); rbac().generateWithBaseFileName("273-hs-office-debitor-rbac");
} }
} }

View File

@ -83,7 +83,7 @@ public class HsOfficePartnerDetailsEntity implements HasUuid, Stringifyable {
"birthName", "birthName",
"birthday", "birthday",
"dateOfDeath") "dateOfDeath")
.createPermission(custom("new-partner-details")).grantedTo("global", ADMIN) .toRole("global", ADMIN).grantPermission("partner-details", INSERT)
.importRootEntityAliasProxy("partnerRel", HsOfficeRelationshipEntity.class, .importRootEntityAliasProxy("partnerRel", HsOfficeRelationshipEntity.class,
fetchedBySql(""" fetchedBySql("""

View File

@ -27,7 +27,6 @@ import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table; import jakarta.persistence.Table;
import jakarta.persistence.*; import jakarta.persistence.*;
import java.io.IOException; import java.io.IOException;
import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
@ -105,7 +104,7 @@ public class HsOfficePartnerEntity implements Stringifyable, HasUuid {
"partnerRoleUuid", "partnerRoleUuid",
"personUuid", "personUuid",
"contactUuid") "contactUuid")
.createPermission(custom("new-partner")).grantedTo("global", ADMIN) .toRole("global", ADMIN).grantPermission("partner", INSERT) // FIXME: global -> partnerRel.relAnchor?
.importRootEntityAliasProxy("partnerRel", HsOfficeRelationshipEntity.class, .importRootEntityAliasProxy("partnerRel", HsOfficeRelationshipEntity.class,
fetchedBySql("SELECT * FROM hs_office_relationship AS r WHERE r.uuid = ${ref}.partnerRoleUuid"), fetchedBySql("SELECT * FROM hs_office_relationship AS r WHERE r.uuid = ${ref}.partnerRoleUuid"),

View File

@ -55,7 +55,7 @@ public class InsertTriggerGenerator {
LOOP LOOP
roleUuid := findRoleId(${rawSuperRoleDescriptor}); roleUuid := findRoleId(${rawSuperRoleDescriptor});
permissionUuid := createPermission(row.uuid, 'INSERT', '${rawSubTableName}'); permissionUuid := createPermission(row.uuid, 'INSERT', '${rawSubTableName}');
call grantPermissionToRole(roleUuid, permissionUuid); call grantPermissionToRole(permissionUuid, roleUuid);
END LOOP; END LOOP;
END; END;
$$; $$;

View File

@ -32,6 +32,7 @@ import java.util.stream.Stream;
import static java.lang.reflect.Modifier.isStatic; import static java.lang.reflect.Modifier.isStatic;
import static java.util.Arrays.stream; import static java.util.Arrays.stream;
import static java.util.Optional.ofNullable; import static java.util.Optional.ofNullable;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.autoFetched; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.autoFetched;
import static org.apache.commons.lang3.StringUtils.uncapitalize; import static org.apache.commons.lang3.StringUtils.uncapitalize;
@ -141,35 +142,42 @@ public class RbacView {
if (rootEntityAliasProxy != null) { if (rootEntityAliasProxy != null) {
throw new IllegalStateException("there is already an entityAliasProxy: " + rootEntityAliasProxy); throw new IllegalStateException("there is already an entityAliasProxy: " + rootEntityAliasProxy);
} }
rootEntityAliasProxy = importEntityAliasImpl(aliasName, entityClass, fetchSql, dependsOnColum, false); rootEntityAliasProxy = importEntityAliasImpl(aliasName, entityClass, fetchSql, dependsOnColum, false, NOT_NULL);
return this; return this;
} }
public RbacView importSubEntityAlias( public RbacView importSubEntityAlias(
final String aliasName, final Class<? extends HasUuid> entityClass, final String aliasName, final Class<? extends HasUuid> entityClass,
final SQL fetchSql, final Column dependsOnColum) { final SQL fetchSql, final Column dependsOnColum) {
importEntityAliasImpl(aliasName, entityClass, fetchSql, dependsOnColum, true); importEntityAliasImpl(aliasName, entityClass, fetchSql, dependsOnColum, true, NOT_NULL);
return this;
}
public RbacView importEntityAlias(
final String aliasName, final Class<? extends HasUuid> entityClass,
final Column dependsOnColum, final SQL fetchSql, final Nullable nullable) {
importEntityAliasImpl(aliasName, entityClass, fetchSql, dependsOnColum, false, nullable);
return this; return this;
} }
public RbacView importEntityAlias( public RbacView importEntityAlias(
final String aliasName, final Class<? extends HasUuid> entityClass, final String aliasName, final Class<? extends HasUuid> entityClass,
final Column dependsOnColum, final SQL fetchSql) { final Column dependsOnColum, final SQL fetchSql) {
importEntityAliasImpl(aliasName, entityClass, fetchSql, dependsOnColum, false); importEntityAliasImpl(aliasName, entityClass, fetchSql, dependsOnColum, false, NOT_NULL);
return this; return this;
} }
public RbacView importEntityAlias( public RbacView importEntityAlias(
final String aliasName, final Class<? extends HasUuid> entityClass, final String aliasName, final Class<? extends HasUuid> entityClass,
final Column dependsOnColum) { final Column dependsOnColum) {
importEntityAliasImpl(aliasName, entityClass, autoFetched(), dependsOnColum, false); importEntityAliasImpl(aliasName, entityClass, autoFetched(), dependsOnColum, false, null);
return this; return this;
} }
private EntityAlias importEntityAliasImpl( private EntityAlias importEntityAliasImpl(
final String aliasName, final Class<? extends HasUuid> entityClass, final String aliasName, final Class<? extends HasUuid> entityClass,
final SQL fetchSql, final Column dependsOnColum, boolean asSubEntity) { final SQL fetchSql, final Column dependsOnColum, boolean asSubEntity, final Nullable nullable) {
final var entityAlias = new EntityAlias(aliasName, entityClass, fetchSql, dependsOnColum, asSubEntity); final var entityAlias = new EntityAlias(aliasName, entityClass, fetchSql, dependsOnColum, asSubEntity, nullable);
entityAliases.put(aliasName, entityAlias); entityAliases.put(aliasName, entityAlias);
try { try {
importAsAlias(aliasName, rbacDefinition(entityClass), asSubEntity); importAsAlias(aliasName, rbacDefinition(entityClass), asSubEntity);
@ -281,6 +289,7 @@ public class RbacView {
return RbacView.this; return RbacView.this;
} }
// TODO: switch order or parameters for more natural readability
public RbacView grantPermission(final String entityAliasName, final Permission perm) { public RbacView grantPermission(final String entityAliasName, final Permission perm) {
final var entityAlias = findEntityAlias(entityAliasName); final var entityAlias = findEntityAlias(entityAliasName);
final var forTable = entityAlias.getRawTableName(); final var forTable = entityAlias.getRawTableName();
@ -290,6 +299,11 @@ public class RbacView {
} }
public enum Nullable {
NOT_NULL, // DEFAULT
NULLABLE
}
@Getter @Getter
@EqualsAndHashCode @EqualsAndHashCode
public class RbacGrantDefinition { public class RbacGrantDefinition {
@ -560,14 +574,14 @@ public class RbacView {
.orElseGet(() -> new RbacGrantDefinition(subRoleDefinition, superRoleDefinition)); .orElseGet(() -> new RbacGrantDefinition(subRoleDefinition, superRoleDefinition));
} }
record EntityAlias(String aliasName, Class<? extends RbacObject> entityClass, SQL fetchSql, Column dependsOnColum, boolean isSubEntity) { record EntityAlias(String aliasName, Class<? extends RbacObject> entityClass, SQL fetchSql, Column dependsOnColum, boolean isSubEntity, Nullable nullable) {
public EntityAlias(final String aliasName) { public EntityAlias(final String aliasName) {
this(aliasName, null, null, null, false); this(aliasName, null, null, null, false, null);
} }
public EntityAlias(final String aliasName, final Class<? extends RbacObject> entityClass) { public EntityAlias(final String aliasName, final Class<? extends RbacObject> entityClass) {
this(aliasName, entityClass, null, null, false); this(aliasName, entityClass, null, null, false, null);
} }
boolean isGlobal() { boolean isGlobal() {
@ -646,20 +660,15 @@ public class RbacView {
} }
} }
public record Permission(String permission) { public enum Permission {
INSERT,
public static final Permission INSERT = new Permission("INSERT"); DELETE,
public static final Permission DELETE = new Permission("DELETE"); UPDATE,
public static final Permission UPDATE = new Permission("UPDATE"); SELECT;
public static final Permission SELECT = new Permission("SELECT");
public static Permission custom(final String permission) {
return new Permission(permission);
}
@Override @Override
public String toString() { public String toString() {
return ":" + permission; return ":" + name();
} }
} }

View File

@ -37,7 +37,10 @@ public class RbacViewPostgresGenerator {
@Override @Override
public String toString() { public String toString() {
return plPgSql.toString(); return plPgSql.toString()
.replace("\n\n\n", "\n\n")
.replace("-- ====", "\n-- ====")
.replace("\n\n--//", "\n--//");
} }
@SneakyThrows @SneakyThrows

View File

@ -82,6 +82,7 @@ class RolesGrantsAndPermissionsGenerator {
plPgSql.writeLn("begin"); plPgSql.writeLn("begin");
plPgSql.indented(() -> { plPgSql.indented(() -> {
plPgSql.writeLn("call enterTriggerForObjectUuid(NEW.uuid);"); plPgSql.writeLn("call enterTriggerForObjectUuid(NEW.uuid);");
plPgSql.writeLn();
generateCreateRolesAndGrantsAfterInsert(plPgSql); generateCreateRolesAndGrantsAfterInsert(plPgSql);
plPgSql.ensureSingleEmptyLine(); plPgSql.ensureSingleEmptyLine();
plPgSql.writeLn("call leaveTriggerForObjectUuid(NEW.uuid);"); plPgSql.writeLn("call leaveTriggerForObjectUuid(NEW.uuid);");
@ -109,7 +110,7 @@ class RolesGrantsAndPermissionsGenerator {
plPgSql.chopEmptyLines(); plPgSql.chopEmptyLines();
plPgSql.indented(() -> { plPgSql.indented(() -> {
updatableEntityAliases() referencedEntityAliases()
.forEach((ea) -> { .forEach((ea) -> {
plPgSql.writeLn(entityRefVar(OLD, ea) + " " + ea.getRawTableName() + ";"); plPgSql.writeLn(entityRefVar(OLD, ea) + " " + ea.getRawTableName() + ";");
plPgSql.writeLn(entityRefVar(NEW, ea) + " " + ea.getRawTableName() + ";"); plPgSql.writeLn(entityRefVar(NEW, ea) + " " + ea.getRawTableName() + ";");
@ -120,6 +121,7 @@ class RolesGrantsAndPermissionsGenerator {
plPgSql.writeLn("begin"); plPgSql.writeLn("begin");
plPgSql.indented(() -> { plPgSql.indented(() -> {
plPgSql.writeLn("call enterTriggerForObjectUuid(NEW.uuid);"); plPgSql.writeLn("call enterTriggerForObjectUuid(NEW.uuid);");
plPgSql.writeLn();
generateUpdateRolesAndGrantsAfterUpdate(plPgSql); generateUpdateRolesAndGrantsAfterUpdate(plPgSql);
plPgSql.ensureSingleEmptyLine(); plPgSql.ensureSingleEmptyLine();
plPgSql.writeLn("call leaveTriggerForObjectUuid(NEW.uuid);"); plPgSql.writeLn("call leaveTriggerForObjectUuid(NEW.uuid);");
@ -134,9 +136,10 @@ class RolesGrantsAndPermissionsGenerator {
private void generateCreateRolesAndGrantsAfterInsert(final StringWriter plPgSql) { private void generateCreateRolesAndGrantsAfterInsert(final StringWriter plPgSql) {
referencedEntityAliases() referencedEntityAliases()
.forEach((ea) -> plPgSql.writeLn( .forEach((ea) -> {
ea.fetchSql().sql + " into " + entityRefVar(NEW, ea) + ";", generateFetchedVars(plPgSql, ea, NEW);
with("ref", NEW.name()))); plPgSql.writeLn();
});
createRolesWithGrantsSql(plPgSql, OWNER); createRolesWithGrantsSql(plPgSql, OWNER);
createRolesWithGrantsSql(plPgSql, ADMIN); createRolesWithGrantsSql(plPgSql, ADMIN);
@ -165,14 +168,11 @@ class RolesGrantsAndPermissionsGenerator {
private void generateUpdateRolesAndGrantsAfterUpdate(final StringWriter plPgSql) { private void generateUpdateRolesAndGrantsAfterUpdate(final StringWriter plPgSql) {
plPgSql.ensureSingleEmptyLine(); plPgSql.ensureSingleEmptyLine();
updatableEntityAliases() referencedEntityAliases()
.forEach((ea) -> { .forEach((ea) -> {
plPgSql.writeLn( generateFetchedVars(plPgSql, ea, OLD);
ea.fetchSql().sql + " into " + entityRefVar(OLD, ea) + ";", generateFetchedVars(plPgSql, ea, NEW);
with("ref", OLD.name())); plPgSql.writeLn();
plPgSql.writeLn(
ea.fetchSql().sql + " into " + entityRefVar(NEW, ea) + ";",
with("ref", NEW.name()));
}); });
updatableEntityAliases() updatableEntityAliases()
@ -190,6 +190,23 @@ class RolesGrantsAndPermissionsGenerator {
}); });
} }
private void generateFetchedVars(
final StringWriter plPgSql,
final RbacView.EntityAlias ea,
final PostgresTriggerReference old) {
plPgSql.writeLn(
ea.fetchSql().sql + " INTO " + entityRefVar(old, ea) + ";",
with("ref", old.name()));
if (ea.nullable() == RbacView.Nullable.NOT_NULL) {
plPgSql.writeLn(
"assert ${entityRefVar}.uuid is not null, format('${entityRefVar} must not be null for ${REF}.${dependsOnColumn} = %s', ${REF}.${dependsOnColumn});",
with("entityRefVar", entityRefVar(old, ea)),
with("dependsOnColumn", ea.dependsOnColumName()),
with("ref", old.name()));
plPgSql.writeLn();
}
}
private boolean isUpdatable(final RbacView.Column c) { private boolean isUpdatable(final RbacView.Column c) {
return rbacDef.getUpdatableColumns().contains(c); return rbacDef.getUpdatableColumns().contains(c);
} }
@ -256,7 +273,7 @@ class RolesGrantsAndPermissionsGenerator {
.replace("${entityRef}", rbacDef.isRootEntityAlias(permDef.entityAlias) .replace("${entityRef}", rbacDef.isRootEntityAlias(permDef.entityAlias)
? ref.name() ? ref.name()
: refVarName(ref, permDef.entityAlias)) : refVarName(ref, permDef.entityAlias))
.replace("${perm}", permDef.permission.permission()); .replace("${perm}", permDef.permission.name());
} }
private String refVarName(final PostgresTriggerReference ref, final RbacView.EntityAlias entityAlias) { private String refVarName(final PostgresTriggerReference ref, final RbacView.EntityAlias entityAlias) {
@ -333,7 +350,7 @@ class RolesGrantsAndPermissionsGenerator {
final var arrayElements = permissionGrantsForRole.stream() final var arrayElements = permissionGrantsForRole.stream()
.map(RbacView.RbacGrantDefinition::getPermDef) .map(RbacView.RbacGrantDefinition::getPermDef)
.map(RbacPermissionDefinition::getPermission) .map(RbacPermissionDefinition::getPermission)
.map(RbacView.Permission::permission) .map(RbacView.Permission::name)
.map(p -> "'" + p + "'") .map(p -> "'" + p + "'")
.sorted() .sorted()
.toList(); .toList();

View File

@ -103,8 +103,8 @@ public class StringWriter {
text = matcher.replaceAll(varDef.value()); text = matcher.replaceAll(varDef.value());
}); });
return text; return text;
} catch (Exception exc) { } catch (final RuntimeException exc) {
throw exc; throw exc; // FIXME: just for debugging, remove try/catch before merging to master
} }
} }
} }

View File

@ -609,7 +609,7 @@ select exists(
); );
$$; $$;
create or replace procedure grantPermissionToRole(roleUuid uuid, permissionUuid uuid) create or replace procedure grantPermissionToRole(permissionUuid uuid, roleUuid uuid)
language plpgsql as $$ language plpgsql as $$
begin begin
perform assertReferenceType('roleId (ascendant)', roleUuid, 'RbacRole'); perform assertReferenceType('roleId (ascendant)', roleUuid, 'RbacRole');
@ -622,10 +622,10 @@ begin
end; end;
$$; $$;
create or replace procedure grantPermissionToRole(roleDesc RbacRoleDescriptor, permissionUuid uuid) create or replace procedure grantPermissionToRole(permissionUuid uuid, roleDesc RbacRoleDescriptor)
language plpgsql as $$ language plpgsql as $$
begin begin
call grantPermissionToRole(findRoleId(roleDesc), permissionUuid); call grantPermissionToRole(permissionUuid, findRoleId(roleDesc));
end; end;
$$; $$;
@ -671,6 +671,11 @@ declare
superRoleId uuid; superRoleId uuid;
subRoleId uuid; subRoleId uuid;
begin begin
-- FIXME: maybe separate method grantRoleToRoleIfNotNull(...)?
if superRole.objectUuid is null or subRole.objectuuid is null then
return;
end if;
superRoleId := findRoleId(superRole); superRoleId := findRoleId(superRole);
subRoleId := findRoleId(subRole); subRoleId := findRoleId(subRole);

View File

@ -160,7 +160,7 @@ do language plpgsql $$
LOOP LOOP
roleUuid := findRoleId(testCustomerAdmin(row)); roleUuid := findRoleId(testCustomerAdmin(row));
permissionUuid := createPermission(row.uuid, 'INSERT', 'test_package'); permissionUuid := createPermission(row.uuid, 'INSERT', 'test_package');
call grantPermissionToRole(roleUuid, permissionUuid); call grantPermissionToRole(permissionUuid, roleUuid);
END LOOP; END LOOP;
END; END;
$$; $$;
@ -174,8 +174,8 @@ create or replace function test_package_test_customer_insert_tf()
strict as $$ strict as $$
begin begin
call grantPermissionToRole( call grantPermissionToRole(
testCustomerAdmin(NEW), createPermission(NEW.uuid, 'INSERT', 'test_package'),
createPermission(NEW.uuid, 'INSERT', 'test_package')); testCustomerAdmin(NEW));
return NEW; return NEW;
end; $$; end; $$;

View File

@ -159,7 +159,7 @@ do language plpgsql $$
LOOP LOOP
roleUuid := findRoleId(testPackageAdmin(row)); roleUuid := findRoleId(testPackageAdmin(row));
permissionUuid := createPermission(row.uuid, 'INSERT', 'test_domain'); permissionUuid := createPermission(row.uuid, 'INSERT', 'test_domain');
call grantPermissionToRole(roleUuid, permissionUuid); call grantPermissionToRole(permissionUuid, roleUuid);
END LOOP; END LOOP;
END; END;
$$; $$;
@ -173,8 +173,8 @@ create or replace function test_domain_test_package_insert_tf()
strict as $$ strict as $$
begin begin
call grantPermissionToRole( call grantPermissionToRole(
testPackageAdmin(NEW), createPermission(NEW.uuid, 'INSERT', 'test_domain'),
createPermission(NEW.uuid, 'INSERT', 'test_domain')); testPackageAdmin(NEW));
return NEW; return NEW;
end; $$; end; $$;

View File

@ -94,7 +94,7 @@ do language plpgsql $$
LOOP LOOP
roleUuid := findRoleId(globalGuest()); roleUuid := findRoleId(globalGuest());
permissionUuid := createPermission(row.uuid, 'INSERT', 'hs_office_person'); permissionUuid := createPermission(row.uuid, 'INSERT', 'hs_office_person');
call grantPermissionToRole(roleUuid, permissionUuid); call grantPermissionToRole(permissionUuid, roleUuid);
END LOOP; END LOOP;
END; END;
$$; $$;
@ -108,8 +108,8 @@ create or replace function hs_office_person_global_insert_tf()
strict as $$ strict as $$
begin begin
call grantPermissionToRole( call grantPermissionToRole(
globalGuest(), createPermission(NEW.uuid, 'INSERT', 'hs_office_person'),
createPermission(NEW.uuid, 'INSERT', 'hs_office_person')); globalGuest());
return NEW; return NEW;
end; $$; end; $$;

View File

@ -106,7 +106,7 @@ do language plpgsql $$
call createHsOfficeRelationshipTestData('Third OHG', 'ACCOUNTING', 'Third OHG', 'third contact'); call createHsOfficeRelationshipTestData('Third OHG', 'ACCOUNTING', 'Third OHG', 'third contact');
call createHsOfficeRelationshipTestData('Smith', 'PARTNER', 'Hostsharing eG', 'sixth contact'); call createHsOfficeRelationshipTestData('Smith', 'PARTNER', 'Hostsharing eG', 'sixth contact');
call createHsOfficeRelationshipTestData('Smith', 'ACCOUNTING', 'Smith', 'third contact', 'members-announce'); call createHsOfficeRelationshipTestData('Smith', 'ACCOUNTING', 'Smith', 'third contact');
call createHsOfficeRelationshipTestData('Smith', 'SUBSCRIBER', 'Third OHG', 'third contact', 'members-announce'); call createHsOfficeRelationshipTestData('Smith', 'SUBSCRIBER', 'Third OHG', 'third contact', 'members-announce');
end; end;
$$; $$;

View File

@ -94,7 +94,7 @@ do language plpgsql $$
LOOP LOOP
roleUuid := findRoleId(globalGuest()); roleUuid := findRoleId(globalGuest());
permissionUuid := createPermission(row.uuid, 'INSERT', 'hs_office_bankaccount'); permissionUuid := createPermission(row.uuid, 'INSERT', 'hs_office_bankaccount');
call grantPermissionToRole(roleUuid, permissionUuid); call grantPermissionToRole(permissionUuid, roleUuid);
END LOOP; END LOOP;
END; END;
$$; $$;
@ -108,8 +108,8 @@ create or replace function hs_office_bankaccount_global_insert_tf()
strict as $$ strict as $$
begin begin
call grantPermissionToRole( call grantPermissionToRole(
globalGuest(), createPermission(NEW.uuid, 'INSERT', 'hs_office_bankaccount'),
createPermission(NEW.uuid, 'INSERT', 'hs_office_bankaccount')); globalGuest());
return NEW; return NEW;
end; $$; end; $$;

View File

@ -1,74 +1,275 @@
### hs_office_debitor RBAC Roles ### rbac debitor
This code generated was by RbacViewMermaidFlowchartGenerator at 2024-03-12T16:22:27.339854728.
```mermaid ```mermaid
%%{init:{'flowchart':{'htmlLabels':false}}}%%
flowchart TB flowchart TB
subgraph partnerRelationship[hsOfficeRelationship:PARTNER] subgraph debitorRel.anchorPerson["`**debitorRel.anchorPerson**`"]
direction TB direction TB
style partnerRelationship fill:#eee style debitorRel.anchorPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
role:partnerRelationship.owner[relationship.owner] subgraph debitorRel.anchorPerson:roles[ ]
--> role:partnerRelationship.admin[relationship.admin] style debitorRel.anchorPerson:roles fill:#99bcdb,stroke:white
--> role:partnerRelationship.agent[relationship.agent]
--> role:partnerRelationship.tenant[relationship.tenant]
partnerPersonAdmin>e.g. partnerPerson.admin] --> role:partnerRelationship.agent role:debitorRel.anchorPerson:owner[[debitorRel.anchorPerson:owner]]
otherPersonAdmin>e.g. operationalPerson.admin] --> role:partnerRelationship.tenant role:debitorRel.anchorPerson:admin[[debitorRel.anchorPerson:admin]]
role:partnerRelationship.tenant --> partnerPersonReferrer>e.g. partnerPerson.referrer] role:debitorRel.anchorPerson:referrer[[debitorRel.anchorPerson:referrer]]
end
end end
subgraph internal[ ] subgraph debitorRel.holderPerson["`**debitorRel.holderPerson**`"]
direction TB direction TB
style internal fill:#fff style debitorRel.holderPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
subgraph refundBankAccount subgraph debitorRel.holderPerson:roles[ ]
direction TB style debitorRel.holderPerson:roles fill:#99bcdb,stroke:white
style refundBankAccount fill:#eee
role:refundBankAccount.owner[bankAccount.owner] role:debitorRel.holderPerson:owner[[debitorRel.holderPerson:owner]]
--> role:refundBankAccount.admin[bankAccount.admin] role:debitorRel.holderPerson:admin[[debitorRel.holderPerson:admin]]
--> role:refundBankAccount.referrer[bankAccount.referrer] role:debitorRel.holderPerson:referrer[[debitorRel.holderPerson:referrer]]
end
end end
subgraph partnerRel.holderPerson["`**partnerRel.holderPerson**`"]
subgraph debitorRelationship[hsOfficeRelationship:DEBITOR]
direction TB direction TB
style debitorRelationship fill:#eee style partnerRel.holderPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
role:debitorRelationship.owner[relationship.owner] subgraph partnerRel.holderPerson:roles[ ]
--> role:debitorRelationship.admin[relationship.admin] style partnerRel.holderPerson:roles fill:#99bcdb,stroke:white
--> role:debitorRelationship.agent[relationship.agent]
--> role:debitorRelationship.tenant[relationship.tenant] role:partnerRel.holderPerson:owner[[partnerRel.holderPerson:owner]]
role:partnerRel.holderPerson:admin[[partnerRel.holderPerson:admin]]
role:partnerRel.holderPerson:referrer[[partnerRel.holderPerson:referrer]]
end
end end
subgraph debitor subgraph debitor["`**debitor**`"]
direction TB direction TB
style debitor fill:#dd4901,stroke:#274d6e,stroke-width:8px
role:debitorRelationship.owner[debitorRelationship.owner] subgraph debitor:permissions[ ]
%% permissions style debitor:permissions fill:#dd4901,stroke:white
==> perm:debitor.*{{debitor.*}}
role:debitorRelationship.admin[debitorRelationship.admin]
%% permissions
==> perm:debitor.edit{{debitor.edit}}
%% incoming
role:partnerRelationship.admin ==> role:debitorRelationship.admin
%% outgoing
role:debitorRelationship.admin ==> role:partnerRelationship.agent
role:debitorRelationship.agent[debitorRelationship.agent]
%% incoming
role:partnerRelationship.agent ==> role:debitorRelationship.agent
role:refundBankAccount.admin ==> role:debitorRelationship.agent
%% outgoing
role:debitorRelationship.agent ==> role:partnerRelationship.tenant
role:debitorRelationship.agent ==> role:refundBankAccount.referrer
role:debitorRelationship.tenant[debitorRelationship.tenant]
==> perm:debitor.view{{debitor.view}}
perm:debitor:INSERT{{debitor:INSERT}}
perm:debitor:DELETE{{debitor:DELETE}}
perm:debitor:UPDATE{{debitor:UPDATE}}
perm:debitor:SELECT{{debitor:SELECT}}
end end
subgraph debitorRel["`**debitorRel**`"]
direction TB
style debitorRel fill:#99bcdb,stroke:#274d6e,stroke-width:8px
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
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.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: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
end
subgraph partnerRel["`**partnerRel**`"]
direction TB
style partnerRel fill:#99bcdb,stroke:#274d6e,stroke-width:8px
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.contact["`**partnerRel.contact**`"]
direction TB
style partnerRel.contact fill:#99bcdb,stroke:#274d6e,stroke-width:8px
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: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
subgraph partnerRel.contact["`**partnerRel.contact**`"]
direction TB
style partnerRel.contact fill:#99bcdb,stroke:#274d6e,stroke-width:8px
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 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 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 refundBankAccount["`**refundBankAccount**`"]
direction TB
style refundBankAccount fill:#99bcdb,stroke:#274d6e,stroke-width:8px
subgraph refundBankAccount:roles[ ]
style refundBankAccount:roles fill:#99bcdb,stroke:white
role:refundBankAccount:owner[[refundBankAccount:owner]]
role:refundBankAccount:admin[[refundBankAccount:admin]]
role:refundBankAccount:referrer[[refundBankAccount:referrer]]
end
end
%% 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:refundBankAccount:owner
role:refundBankAccount:owner -.-> role:refundBankAccount:admin
role:refundBankAccount:admin -.-> role:refundBankAccount:referrer
role:refundBankAccount:admin ==> role:debitorRel:agent
role:debitorRel:agent ==> role:refundBankAccount:referrer
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
role:partnerRel:admin ==> role:debitorRel:admin
role:partnerRel:agent ==> role:debitorRel:agent
role:debitorRel:agent ==> role:partnerRel:tenant
%% granting permissions to roles
role:global:admin ==> perm:debitor:INSERT
role:debitorRel:owner ==> perm:debitor:DELETE
role:debitorRel:admin ==> perm:debitor:UPDATE
role:debitorRel:tenant ==> perm:debitor:SELECT
``` ```

View File

@ -1,4 +1,6 @@
--liquibase formatted sql --liquibase formatted sql
-- This code generated was by RbacViewPostgresGenerator at 2024-03-12T16:22:27.348469700.
-- ============================================================================ -- ============================================================================
--changeset hs-office-debitor-rbac-OBJECT:1 endDelimiter:--// --changeset hs-office-debitor-rbac-OBJECT:1 endDelimiter:--//
@ -15,249 +17,273 @@ call generateRbacRoleDescriptors('hsOfficeDebitor', 'hs_office_debitor');
-- ============================================================================ -- ============================================================================
--changeset hs-office-debitor-rbac-ROLES-CREATION:1 endDelimiter:--// --changeset hs-office-debitor-rbac-insert-trigger:1 endDelimiter:--//
-- ---------------------------------------------------------------------------- -- ----------------------------------------------------------------------------
/* /*
Creates and updates the roles and their assignments for debitor entities. Creates the roles, grants and permission for the AFTER INSERT TRIGGER.
*/ */
create or replace function hsOfficeDebitorRbacRolesTrigger() create or replace procedure buildRbacSystemForHsOfficeDebitor(
NEW hs_office_debitor
)
language plpgsql as $$
declare
newPartnerRel hs_office_relationship;
newDebitorRel hs_office_relationship;
newRefundBankAccount hs_office_bankaccount;
begin
call enterTriggerForObjectUuid(NEW.uuid);
SELECT partnerRel.*
FROM hs_office_relationship AS partnerRel
JOIN hs_office_relationship AS debitorRel
ON debitorRel.relType = 'ACCOUNTING' AND debitorRel.relAnchorUuid = partnerRel.relHolderUuid
WHERE partnerRel.relType = 'PARTNER'
AND NEW.debitorRelUuid = debitorRel.uuid
INTO newPartnerRel;
assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.debitorRelUuid = %s', NEW.debitorRelUuid);
SELECT *
FROM hs_office_relationship AS r
WHERE r.relType = 'ACCOUNTING' AND r.uuid = NEW.debitorRelUuid
INTO newDebitorRel;
assert newDebitorRel.uuid is not null, format('newDebitorRel must not be null for NEW.debitorRelUuid = %s', NEW.debitorRelUuid);
SELECT *
FROM hs_office_relationship AS r
WHERE r.relType = 'ACCOUNTING' AND r.relHolderUuid = NEW.debitorRelUuid
INTO newRefundBankAccount;
call grantRoleToRole(hsOfficeBankAccountReferrer(newRefundBankAccount), hsOfficeRelationshipAgent(newDebitorRel));
call grantRoleToRole(hsOfficeRelationshipAdmin(newDebitorRel), hsOfficeRelationshipAdmin(newPartnerRel));
call grantRoleToRole(hsOfficeRelationshipAgent(newDebitorRel), hsOfficeBankAccountAdmin(newRefundBankAccount));
call grantRoleToRole(hsOfficeRelationshipAgent(newDebitorRel), hsOfficeRelationshipAgent(newPartnerRel));
call grantRoleToRole(hsOfficeRelationshipTenant(newPartnerRel), hsOfficeRelationshipAgent(newDebitorRel));
call grantPermissionToRole(createPermission(NEW.uuid, 'DELETE'), hsOfficeRelationshipOwner(newDebitorRel));
call grantPermissionToRole(createPermission(NEW.uuid, 'SELECT'), hsOfficeRelationshipTenant(newDebitorRel));
call grantPermissionToRole(createPermission(NEW.uuid, 'UPDATE'), hsOfficeRelationshipAdmin(newDebitorRel));
call leaveTriggerForObjectUuid(NEW.uuid);
end; $$;
/*
AFTER INSERT TRIGGER to create the role+grant structure for a new hs_office_debitor row.
*/
create or replace function insertTriggerForHsOfficeDebitor_tf()
returns trigger returns trigger
language plpgsql language plpgsql
strict as $$ strict as $$
declare begin
debitorUuid uuid; call buildRbacSystemForHsOfficeDebitor(NEW);
return NEW;
end; $$;
create trigger insertTriggerForHsOfficeDebitor_tg
after insert on hs_office_debitor
for each row
execute procedure insertTriggerForHsOfficeDebitor_tf();
--//
-- ============================================================================
--changeset hs-office-debitor-rbac-update-trigger:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
/*
Called from the AFTER UPDATE TRIGGER to re-wire the grants.
*/
create or replace procedure updateRbacRulesForHsOfficeDebitor(
OLD hs_office_debitor,
NEW hs_office_debitor
)
language plpgsql as $$
declare
oldPartnerRel hs_office_relationship;
newPartnerRel hs_office_relationship;
oldDebitorRel hs_office_relationship; oldDebitorRel hs_office_relationship;
newDebitorRel hs_office_relationship; newDebitorRel hs_office_relationship;
oldRefundBankAccount hs_office_bankaccount;
newPartnerRel hs_office_relationship; newRefundBankAccount hs_office_bankaccount;
newBankAccount hs_office_bankaccount;
oldBankAccount hs_office_bankaccount;
begin begin
call enterTriggerForObjectUuid(NEW.uuid); call enterTriggerForObjectUuid(NEW.uuid);
debitorUuid := NEW.uuid; SELECT partnerRel.*
FROM hs_office_relationship AS partnerRel
JOIN hs_office_relationship AS debitorRel
ON debitorRel.relType = 'ACCOUNTING' AND debitorRel.relAnchorUuid = partnerRel.relHolderUuid
WHERE partnerRel.relType = 'PARTNER'
AND OLD.debitorRelUuid = debitorRel.uuid
INTO oldPartnerRel;
assert oldPartnerRel.uuid is not null, format('oldPartnerRel must not be null for OLD.debitorRelUuid = %s', OLD.debitorRelUuid);
select * into newDebitorRel SELECT partnerRel.*
from hs_office_relationship as r where r.relType = 'ACCOUNTING' and r.relHolderUuid = NEW.debitorRelUuid; FROM hs_office_relationship AS partnerRel
JOIN hs_office_relationship AS debitorRel
ON debitorRel.relType = 'ACCOUNTING' AND debitorRel.relAnchorUuid = partnerRel.relHolderUuid
WHERE partnerRel.relType = 'PARTNER'
AND NEW.debitorRelUuid = debitorRel.uuid
INTO newPartnerRel;
assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.debitorRelUuid = %s', NEW.debitorRelUuid);
select * into newPartnerRel SELECT *
from hs_office_relationship as partnerRel FROM hs_office_relationship AS r
where newDebitorRel.relAnchorUuid = partnerRel.relHolderUuid; WHERE r.relType = 'ACCOUNTING' AND r.uuid = OLD.debitorRelUuid
INTO oldDebitorRel;
assert oldDebitorRel.uuid is not null, format('oldDebitorRel must not be null for OLD.debitorRelUuid = %s', OLD.debitorRelUuid);
select * into newBankAccount SELECT *
from hs_office_bankaccount as b where b.uuid = NEW.refundBankAccountUuid; FROM hs_office_relationship AS r
WHERE r.relType = 'ACCOUNTING' AND r.uuid = NEW.debitorRelUuid
INTO newDebitorRel;
assert newDebitorRel.uuid is not null, format('newDebitorRel must not be null for NEW.debitorRelUuid = %s', NEW.debitorRelUuid);
if TG_OP = 'INSERT' then SELECT *
FROM hs_office_relationship AS r
WHERE r.relType = 'ACCOUNTING' AND r.relHolderUuid = OLD.debitorRelUuid
INTO oldRefundBankAccount;
SELECT *
FROM hs_office_relationship AS r
WHERE r.relType = 'ACCOUNTING' AND r.relHolderUuid = NEW.debitorRelUuid
INTO newRefundBankAccount;
-- Permissions and Grants for Debitor if NEW.refundBankAccountUuid <> OLD.refundBankAccountUuid then
call grantPermissionsToRole( call revokeRoleFromRole(hsOfficeRelationshipAgent(oldDebitorRel), hsOfficeBankAccountAdmin(oldRefundBankAccount));
getRoleId(hsOfficeRelationshipOwner(newDebitorRel)), call grantRoleToRole(hsOfficeRelationshipAgent(newDebitorRel), hsOfficeBankAccountAdmin(newRefundBankAccount));
createPermissions(partnerUuid, array ['DELETE'])
);
call grantPermissionsToRole( call revokeRoleFromRole(hsOfficeBankAccountReferrer(oldRefundBankAccount), hsOfficeRelationshipAgent(oldDebitorRel));
getRoleId(hsOfficeRelationshipAdmin(newDebitorRel), 'fail'), call grantRoleToRole(hsOfficeBankAccountReferrer(newRefundBankAccount), hsOfficeRelationshipAgent(newDebitorRel));
createPermissions(partnerUuid, array ['UPDATE'])
);
call grantPermissionsToRole(
getRoleId(hsOfficeRelationshipTenant(newDebitorRel), 'fail'),
createPermissions(partnerUuid, array ['SELECT'])
);
-- Grants to and from related Partner Relationship
-- call grantRoleToRole(hsOfficeRelationshipAdmin(newDebitorRel), hsOfficeRelationshipAdmin(newPartnerRel), true);
-- call grantRoleToRole(hsOfficeRelationshipAgent(newPartnerRel), hsOfficeRelationshipAdmin(newDebitorRel), true);
--
-- call grantRoleToRole(hsOfficeRelationshipAgent(newDebitorRel), hsOfficeRelationshipAgent(newPartnerRel), true);
-- call grantRoleToRole(hsOfficeRelationshipTenant(newPartnerRel), hsOfficeRelationshipAgent(newDebitorRel), true);
-- Grants to and from refundBankAccount
-- 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
if OLD.debitorRelUuid is distinct from NEW.debitorRelUuid then
select * into oldDebitorRel
from hs_office_relationship as r where r.relType = 'ACCOUNTING' and r.relHolderUuid = NEW.debitorRelUuid;
-- call grantPermissionsToRole(
-- getRoleId(hsOfficeRelationshipOwner(newDebitorRel)),
-- createPermissions(partnerUuid, array ['DELETE'])
-- );
--
-- call grantPermissionsToRole(
-- getRoleId(hsOfficeRelationshipAdmin(newDebitorRel)),
-- createPermissions(partnerUuid, array ['UPDATE'])
-- );
--
-- call grantPermissionsToRole(
-- getRoleId(hsOfficeRelationshipTenant(newDebitorRel)),
-- createPermissions(partnerUuid, array ['SELECT'])
-- );
end if;
if OLD.refundBankAccountUuid is distinct from NEW.refundBankAccountUuid then
select * into oldBankAccount
from hs_office_bankaccount as b where b.uuid = OLD.refundBankAccountUuid;
if oldBankAccount is not null then
call revokeRoleFromRole(hsOfficeBankAccountReferrer(oldBankAccount), hsOfficeRelationshipAgent(oldDebitorRel), true);
call revokeRoleFromRole(hsOfficeRelationshipAgent(oldDebitorRel), hsOfficeBankAccountAdmin(oldBankAccount), true);
end if;
if newBankAccount is not null then
call grantRoleToRole(hsOfficeBankAccountReferrer(newBankAccount), hsOfficeRelationshipAgent(newDebitorRel), true);
call grantRoleToRole(hsOfficeRelationshipAgent(newDebitorRel), hsOfficeBankAccountAdmin(newBankAccount), true);
end if;
end if;
else
raise exception 'invalid usage of TRIGGER';
end if; end if;
call leaveTriggerForObjectUuid(NEW.uuid); call leaveTriggerForObjectUuid(NEW.uuid);
return NEW;
end; $$; end; $$;
/* /*
An AFTER INSERT TRIGGER which creates the role structure for a new debitor. AFTER INSERT TRIGGER to re-wire the grant structure for a new hs_office_debitor row.
*/
create trigger createRbacRolesForHsOfficeDebitor_Trigger
after insert
on hs_office_debitor
for each row
execute procedure hsOfficeDebitorRbacRolesTrigger();
/*
An AFTER UPDATE TRIGGER which updates the role structure of a debitor.
*/
create trigger updateRbacRolesForHsOfficeDebitor_Trigger
after update
on hs_office_debitor
for each row
execute procedure hsOfficeDebitorRbacRolesTrigger();
--//
/*
Creates and updates the roles and their assignments for debitor entities if partner rel changes.
*/ */
create or replace function hsOfficeDebitorPartnerRelRbacRolesTrigger() create or replace function updateTriggerForHsOfficeDebitor_tf()
returns trigger returns trigger
language plpgsql language plpgsql
strict as $$ strict as $$
declare
begin begin
call enterTriggerForObjectUuid(NEW.uuid); call updateRbacRulesForHsOfficeDebitor(OLD, NEW);
-- TODO
call leaveTriggerForObjectUuid(NEW.uuid);
return NEW; return NEW;
end; $$; end; $$;
create trigger updateTriggerForHsOfficeDebitor_tg
after update on hs_office_debitor
for each row
execute procedure updateTriggerForHsOfficeDebitor_tf();
--// --//
-- ============================================================================
--changeset hs-office-debitor-rbac-INSERT:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
/* /*
An AFTER UPDATE TRIGGER which creates the role structure for debitors if partner relations change. Creates INSERT INTO hs_office_debitor permissions for the related global rows.
*/ */
create trigger updateRbacRolesForHsOfficeDebitor_Trigger do language plpgsql $$
after update declare
on hs_office_partner row global;
for each row permissionUuid uuid;
execute procedure hsOfficeDebitorPartnerRelRbacRolesTrigger(); roleUuid uuid;
--// begin
call defineContext('create INSERT INTO hs_office_debitor permissions for the related global rows');
FOR row IN SELECT * FROM global
LOOP
roleUuid := findRoleId(globalAdmin());
permissionUuid := createPermission(row.uuid, 'INSERT', 'hs_office_debitor');
call grantPermissionToRole(permissionUuid, roleUuid);
END LOOP;
END;
$$;
/**
Adds hs_office_debitor INSERT permission to specified role of new global rows.
*/
create or replace function hs_office_debitor_global_insert_tf()
returns trigger
language plpgsql
strict as $$
begin
call grantPermissionToRole(
globalAdmin(),
createPermission(NEW.uuid, 'INSERT', 'hs_office_debitor'));
return NEW;
end; $$;
create trigger hs_office_debitor_global_insert_tg
after insert on global
for each row
execute procedure hs_office_debitor_global_insert_tf();
/**
Checks if the user or assumed roles are allowed to insert a row to hs_office_debitor.
*/
create or replace function hs_office_debitor_insert_permission_missing_tf()
returns trigger
language plpgsql as $$
begin
raise exception '[403] insert into hs_office_debitor not allowed for current subjects % (%)',
currentSubjects(), currentSubjectsUuids();
end; $$;
create trigger hs_office_debitor_insert_permission_check_tg
before insert on hs_office_debitor
for each row
when ( not isGlobalAdmin() )
execute procedure hs_office_debitor_insert_permission_missing_tf();
--//
-- ============================================================================ -- ============================================================================
--changeset hs-office-debitor-rbac-IDENTITY-VIEW:1 endDelimiter:--// --changeset hs-office-debitor-rbac-IDENTITY-VIEW:1 endDelimiter:--//
-- ---------------------------------------------------------------------------- -- ----------------------------------------------------------------------------
call generateRbacIdentityViewFromProjection('hs_office_debitor', $idName$
'#' || (select partner.partnerNumber call generateRbacIdentityViewFromQuery('hs_office_debitor', $idName$
from hs_office_partner partner SELECT debitor.uuid AS uuid,
join hs_office_relationship partnerRel on partnerRel.uuid = partner.partnerRoleUUid and partnerRel.relType = 'PARTNER' 'D-' || (SELECT partner.partnerNumber
join hs_office_relationship debitorRel on debitorRel.relAnchorUuid = partnerRel.relHolderUuid and partnerRel.relType = 'ACCOUNTING' FROM hs_office_partner partner
where debitorRel.uuid = target.debitorRelUuid) JOIN hs_office_relationship partnerRel
|| to_char(debitorNumberSuffix, 'fm00') ON partnerRel.uuid = partner.partnerRoleUUid AND partnerRel.relType = 'PARTNER'
|| ':' || (select split_part(idName, ':', 2) from hs_office_relationship_iv ri where ri.uuid = target.debitorRelUuid) JOIN hs_office_relationship debitorRel
ON debitorRel.relAnchorUuid = partnerRel.relHolderUuid AND partnerRel.relType = 'ACCOUNTING'
WHERE debitorRel.uuid = debitor.debitorRelUuid)
|| to_char(debitorNumberSuffix, 'fm00') as idName
FROM hs_office_debitor AS debitor
$idName$); $idName$);
--// --//
-- ============================================================================ -- ============================================================================
--changeset hs-office-debitor-rbac-RESTRICTED-VIEW:1 endDelimiter:--// --changeset hs-office-debitor-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
-- ---------------------------------------------------------------------------- -- ----------------------------------------------------------------------------
call generateRbacRestrictedView('hs_office_debitor', 'target.debitorNumberSuffix', call generateRbacRestrictedView('hs_office_debitor',
$orderBy$
defaultPrefix
$orderBy$,
$updates$ $updates$
debitorRel = new.debitorRel, debitorRel = new.debitorRel,
billable = new.billable, billable = new.billable,
billingContactUuid = new.billingContactUuid, debitorUuid = new.debitorUuid,
refundBankAccountUuid = new.refundBankAccountUuid, refundBankAccountUuid = new.refundBankAccountUuid,
vatId = new.vatId, vatId = new.vatId,
vatCountryCode = new.vatCountryCode, vatCountryCode = new.vatCountryCode,
vatBusiness = new.vatBusiness, vatBusiness = new.vatBusiness,
vatreversecharge = new.vatreversecharge, vatReverseCharge = new.vatReverseCharge,
defaultPrefix = new.defaultPrefix -- TODO: Should it be allowed to updated this value? defaultPrefix = new.defaultPrefix
$updates$); $updates$);
--// --//
-- ============================================================================
--changeset hs-office-debitor-rbac-NEW-DEBITOR:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
/*
Creates a global permission for new-debitor and assigns it to the hostsharing admins role.
*/
do language plpgsql $$
declare
addDebitorPermissions uuid[];
globalObjectUuid uuid;
globalAdminRoleUuid uuid ;
begin
call defineContext('granting global new-debitor permission to global admin role', null, null, null);
globalAdminRoleUuid := findRoleId(globalAdmin());
globalObjectUuid := (select uuid from global);
addDebitorPermissions := createPermissions(globalObjectUuid, array ['new-debitor']);
call grantPermissionsToRole(globalAdminRoleUuid, addDebitorPermissions);
end;
$$;
/**
Used by the trigger to prevent the add-debitor to current user respectively assumed roles.
*/
create or replace function addHsOfficeDebitorNotAllowedForCurrentSubjects()
returns trigger
language PLPGSQL
as $$
begin
raise exception '[403] new-debitor not permitted for %',
array_to_string(currentSubjects(), ';', 'null');
end; $$;
/**
Checks if the user or assumed roles are allowed to create a new debitor.
*/
create trigger hs_office_debitor_insert_trigger
before insert
on hs_office_debitor
for each row
-- TODO.spec: who is allowed to create new debitors
when ( not hasAssumedRole() )
execute procedure addHsOfficeDebitorNotAllowedForCurrentSubjects();
--//