improved RBAC generators (#26)
Co-authored-by: Michael Hoennig <michael@hoennig.de> Reviewed-on: #26 Reviewed-by: Timotheus Pokorra <timotheus.pokorra@hostsharing.net>
This commit is contained in:
parent
67c1b50239
commit
4572c6bda0
@ -19,9 +19,10 @@ import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
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.Role.*;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.fetchedBySql;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.directlyFetchedByDependsOnColumn;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
|
||||
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
||||
|
||||
@ -131,36 +132,26 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
|
||||
"vatBusiness",
|
||||
"vatReverseCharge",
|
||||
"defaultPrefix" /* TODO: do we want that updatable? */)
|
||||
.createPermission(custom("new-debitor")).grantedTo("global", ADMIN)
|
||||
.createPermission(INSERT).grantedTo("global", ADMIN)
|
||||
|
||||
.importRootEntityAliasProxy("debitorRel", HsOfficeRelationEntity.class,
|
||||
fetchedBySql("""
|
||||
SELECT *
|
||||
FROM hs_office_relation AS r
|
||||
WHERE r.type = 'DEBITOR' AND r.holderUuid = ${REF}.debitorRelUuid
|
||||
"""),
|
||||
directlyFetchedByDependsOnColumn(),
|
||||
dependsOnColumn("debitorRelUuid"))
|
||||
.createPermission(DELETE).grantedTo("debitorRel", OWNER)
|
||||
.createPermission(UPDATE).grantedTo("debitorRel", ADMIN)
|
||||
.createPermission(SELECT).grantedTo("debitorRel", TENANT)
|
||||
|
||||
.importEntityAlias("refundBankAccount", HsOfficeBankAccountEntity.class,
|
||||
dependsOnColumn("refundBankAccountUuid"), fetchedBySql("""
|
||||
SELECT *
|
||||
FROM hs_office_relation AS r
|
||||
WHERE r.type = 'DEBITOR' AND r.holderUuid = ${REF}.debitorRelUuid
|
||||
""")
|
||||
)
|
||||
dependsOnColumn("refundBankAccountUuid"),
|
||||
directlyFetchedByDependsOnColumn(),
|
||||
NULLABLE)
|
||||
.toRole("refundBankAccount", ADMIN).grantRole("debitorRel", AGENT)
|
||||
.toRole("debitorRel", AGENT).grantRole("refundBankAccount", REFERRER)
|
||||
|
||||
.importEntityAlias("partnerRel", HsOfficeRelationEntity.class,
|
||||
dependsOnColumn("partnerRelUuid"), fetchedBySql("""
|
||||
SELECT *
|
||||
FROM hs_office_relation AS partnerRel
|
||||
WHERE ${debitorRel}.anchorUuid = partnerRel.holderUuid
|
||||
""")
|
||||
)
|
||||
dependsOnColumn("partnerRelUuid"),
|
||||
directlyFetchedByDependsOnColumn(),
|
||||
NULLABLE)
|
||||
.toRole("partnerRel", ADMIN).grantRole("debitorRel", ADMIN)
|
||||
.toRole("partnerRel", AGENT).grantRole("debitorRel", AGENT)
|
||||
.toRole("debitorRel", AGENT).grantRole("partnerRel", TENANT)
|
||||
|
@ -84,11 +84,11 @@ public class HsOfficePartnerDetailsEntity implements HasUuid, Stringifyable {
|
||||
"birthName",
|
||||
"birthday",
|
||||
"dateOfDeath")
|
||||
.createPermission(custom("new-partner-details")).grantedTo("global", ADMIN)
|
||||
.createPermission(INSERT).grantedTo("global", ADMIN)
|
||||
|
||||
.importRootEntityAliasProxy("partnerRel", HsOfficeRelationEntity.class,
|
||||
fetchedBySql("""
|
||||
SELECT partnerRel.*
|
||||
SELECT ${columns}
|
||||
FROM hs_office_relation AS partnerRel
|
||||
JOIN hs_office_partner AS partner
|
||||
ON partner.detailsUuid = ${ref}.uuid
|
||||
|
@ -22,7 +22,7 @@ import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnCo
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.SELECT;
|
||||
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.directlyFetchedByDependsOnColumn;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
|
||||
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
||||
|
||||
@ -90,17 +90,17 @@ public class HsOfficePartnerEntity implements Stringifyable, HasUuid {
|
||||
"partnerRelUuid",
|
||||
"personUuid",
|
||||
"contactUuid")
|
||||
.createPermission(custom("new-partner")).grantedTo("global", ADMIN)
|
||||
.createPermission(INSERT).grantedTo("global", ADMIN)
|
||||
|
||||
.importRootEntityAliasProxy("partnerRel", HsOfficeRelationEntity.class,
|
||||
fetchedBySql("SELECT * FROM hs_office_relation AS r WHERE r.uuid = ${ref}.partnerRelUuid"),
|
||||
directlyFetchedByDependsOnColumn(),
|
||||
dependsOnColumn("partnerRelUuid"))
|
||||
.createPermission(DELETE).grantedTo("partnerRel", ADMIN)
|
||||
.createPermission(UPDATE).grantedTo("partnerRel", AGENT)
|
||||
.createPermission(SELECT).grantedTo("partnerRel", TENANT)
|
||||
|
||||
.importSubEntityAlias("partnerDetails", HsOfficePartnerDetailsEntity.class,
|
||||
fetchedBySql("SELECT * FROM hs_office_partner_details AS d WHERE d.uuid = ${ref}.detailsUuid"),
|
||||
directlyFetchedByDependsOnColumn(),
|
||||
dependsOnColumn("detailsUuid"))
|
||||
.createPermission("partnerDetails", DELETE).grantedTo("partnerRel", ADMIN)
|
||||
.createPermission("partnerDetails", UPDATE).grantedTo("partnerRel", AGENT)
|
||||
|
@ -16,10 +16,11 @@ import java.util.UUID;
|
||||
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.GLOBAL;
|
||||
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.RbacUserReference.UserRole.CREATOR;
|
||||
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.directlyFetchedByDependsOnColumn;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
|
||||
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
||||
|
||||
@ -90,16 +91,16 @@ public class HsOfficeRelationEntity implements HasUuid, Stringifyable {
|
||||
.withUpdatableColumns("contactUuid")
|
||||
.importEntityAlias("anchorPerson", HsOfficePersonEntity.class,
|
||||
dependsOnColumn("anchorUuid"),
|
||||
fetchedBySql("select * from hs_office_person as p where p.uuid = ${REF}.anchorUuid")
|
||||
)
|
||||
directlyFetchedByDependsOnColumn(),
|
||||
NULLABLE)
|
||||
.importEntityAlias("holderPerson", HsOfficePersonEntity.class,
|
||||
dependsOnColumn("holderUuid"),
|
||||
fetchedBySql("select * from hs_office_person as p where p.uuid = ${REF}.holderUuid")
|
||||
)
|
||||
directlyFetchedByDependsOnColumn(),
|
||||
NULLABLE)
|
||||
.importEntityAlias("contact", HsOfficeContactEntity.class,
|
||||
dependsOnColumn("contactUuid"),
|
||||
fetchedBySql("select * from hs_office_contact as c where c.uuid = ${REF}.contactUuid")
|
||||
)
|
||||
directlyFetchedByDependsOnColumn(),
|
||||
NULLABLE)
|
||||
.createRole(OWNER, (with) -> {
|
||||
with.owningUser(CREATOR);
|
||||
with.incomingSuperRole(GLOBAL, ADMIN);
|
||||
|
@ -4,6 +4,7 @@ import java.util.Optional;
|
||||
import java.util.function.BinaryOperator;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.PostgresTriggerReference.NEW;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.INSERT;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacGrantDefinition.GrantType.PERM_TO_ROLE;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.StringWriter.with;
|
||||
@ -22,7 +23,7 @@ public class InsertTriggerGenerator {
|
||||
|
||||
void generateTo(final StringWriter plPgSql) {
|
||||
generateLiquibaseChangesetHeader(plPgSql);
|
||||
generateGrantInsertRoleToExistingCustomers(plPgSql);
|
||||
generateGrantInsertRoleToExistingObjects(plPgSql);
|
||||
generateInsertPermissionGrantTrigger(plPgSql);
|
||||
generateInsertCheckTrigger(plPgSql);
|
||||
plPgSql.writeLn("--//");
|
||||
@ -37,7 +38,7 @@ public class InsertTriggerGenerator {
|
||||
with("liquibaseTagPrefix", liquibaseTagPrefix));
|
||||
}
|
||||
|
||||
private void generateGrantInsertRoleToExistingCustomers(final StringWriter plPgSql) {
|
||||
private void generateGrantInsertRoleToExistingObjects(final StringWriter plPgSql) {
|
||||
getOptionalInsertSuperRole().ifPresent( superRoleDef -> {
|
||||
plPgSql.writeLn("""
|
||||
/*
|
||||
@ -53,16 +54,16 @@ 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);
|
||||
call grantPermissionToRole(permissionUuid, roleUuid);
|
||||
END LOOP;
|
||||
END;
|
||||
$$;
|
||||
""",
|
||||
with("rawSubTableName", rbacDef.getRootEntityAlias().getRawTableName()),
|
||||
with("rawSuperTableName", superRoleDef.getEntityAlias().getRawTableName()),
|
||||
with("rawSuperRoleDescriptor", toVar(superRoleDef))
|
||||
with("rawSuperRoleDescriptor", toRoleDescriptor(superRoleDef, "row"))
|
||||
);
|
||||
});
|
||||
}
|
||||
@ -79,39 +80,69 @@ public class InsertTriggerGenerator {
|
||||
strict as $$
|
||||
begin
|
||||
call grantPermissionToRole(
|
||||
${rawSuperRoleDescriptor}(NEW),
|
||||
createPermission(NEW.uuid, 'INSERT', '${rawSubTableName}'));
|
||||
createPermission(NEW.uuid, 'INSERT', '${rawSubTableName}'),
|
||||
${rawSuperRoleDescriptor});
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
create trigger ${rawSubTableName}_${rawSuperTableName}_insert_tg
|
||||
-- z_... is to put it at the end of after insert triggers, to make sure the roles exist
|
||||
create trigger z_${rawSubTableName}_${rawSuperTableName}_insert_tg
|
||||
after insert on ${rawSuperTableName}
|
||||
for each row
|
||||
execute procedure ${rawSubTableName}_${rawSuperTableName}_insert_tf();
|
||||
""",
|
||||
with("rawSubTableName", rbacDef.getRootEntityAlias().getRawTableName()),
|
||||
with("rawSuperTableName", superRoleDef.getEntityAlias().getRawTableName()),
|
||||
with("rawSuperRoleDescriptor", toVar(superRoleDef))
|
||||
with("rawSuperRoleDescriptor", toRoleDescriptor(superRoleDef, NEW.name()))
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
private void generateInsertCheckTrigger(final StringWriter plPgSql) {
|
||||
plPgSql.writeLn("""
|
||||
/**
|
||||
Checks if the user or assumed roles are allowed to insert a row to ${rawSubTable}.
|
||||
*/
|
||||
create or replace function ${rawSubTable}_insert_permission_missing_tf()
|
||||
returns trigger
|
||||
language plpgsql as $$
|
||||
begin
|
||||
raise exception '[403] insert into ${rawSubTable} not allowed for current subjects % (%)',
|
||||
currentSubjects(), currentSubjectsUuids();
|
||||
end; $$;
|
||||
""",
|
||||
with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableName()));
|
||||
getOptionalInsertGrant().ifPresentOrElse(g -> {
|
||||
plPgSql.writeLn("""
|
||||
if (g.getSuperRoleDef().getEntityAlias().isGlobal()) {
|
||||
switch (g.getSuperRoleDef().getRole()) {
|
||||
case ADMIN -> {
|
||||
generateInsertPermissionTriggerAllowOnlyGlobalAdmin(plPgSql);
|
||||
}
|
||||
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());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (g.getSuperRoleDef().getEntityAlias().isFetchedByDirectForeignKey()) {
|
||||
generateInsertPermissionTriggerAllowByRoleOfDirectForeignKey(plPgSql, g);
|
||||
} else {
|
||||
generateInsertPermissionTriggerAllowByRoleOfIndirectForeignKey(plPgSql, g);
|
||||
}
|
||||
}
|
||||
},
|
||||
() -> {
|
||||
System.err.println("WARNING: no explicit INSERT grant for " + rbacDef.getRootEntityAlias().simpleName() + " => implicitly grant INSERT to global.admin");
|
||||
generateInsertPermissionTriggerAllowOnlyGlobalAdmin(plPgSql);
|
||||
});
|
||||
}
|
||||
|
||||
private void generateInsertPermissionTriggerAllowByRoleOfDirectForeignKey(final StringWriter plPgSql, final RbacView.RbacGrantDefinition g) {
|
||||
plPgSql.writeLn("""
|
||||
/**
|
||||
Checks if the user or assumed roles are allowed to insert a row to ${rawSubTable},
|
||||
where the check is performed by a direct role.
|
||||
|
||||
A direct role is a role depending on a foreign key directly available in the NEW row.
|
||||
*/
|
||||
create or replace function ${rawSubTable}_insert_permission_missing_tf()
|
||||
returns trigger
|
||||
language plpgsql as $$
|
||||
begin
|
||||
raise exception '[403] insert into ${rawSubTable} not allowed for current subjects % (%)',
|
||||
currentSubjects(), currentSubjectsUuids();
|
||||
end; $$;
|
||||
|
||||
create trigger ${rawSubTable}_insert_permission_check_tg
|
||||
before insert on ${rawSubTable}
|
||||
for each row
|
||||
@ -119,20 +150,78 @@ public class InsertTriggerGenerator {
|
||||
execute procedure ${rawSubTable}_insert_permission_missing_tf();
|
||||
""",
|
||||
with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableName()),
|
||||
with("referenceColumn", g.getSuperRoleDef().getEntityAlias().dependsOnColumName() ));
|
||||
},
|
||||
() -> {
|
||||
plPgSql.writeLn("""
|
||||
with("referenceColumn", g.getSuperRoleDef().getEntityAlias().dependsOnColumName()));
|
||||
}
|
||||
|
||||
private void generateInsertPermissionTriggerAllowByRoleOfIndirectForeignKey(
|
||||
final StringWriter plPgSql,
|
||||
final RbacView.RbacGrantDefinition g) {
|
||||
plPgSql.writeLn("""
|
||||
/**
|
||||
Checks if the user or assumed roles are allowed to insert a row to ${rawSubTable},
|
||||
where the check is performed by an indirect role.
|
||||
|
||||
An indirect role is a role which depends on an object uuid which is not a direct foreign key
|
||||
of the source entity, but needs to be fetched via joined tables.
|
||||
*/
|
||||
create or replace function ${rawSubTable}_insert_permission_check_tf()
|
||||
returns trigger
|
||||
language plpgsql as $$
|
||||
|
||||
declare
|
||||
superRoleObjectUuid uuid;
|
||||
|
||||
begin
|
||||
""",
|
||||
with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableName()));
|
||||
plPgSql.chopEmptyLines();
|
||||
plPgSql.indented(2, () -> {
|
||||
plPgSql.writeLn(
|
||||
"superRoleObjectUuid := (" + g.getSuperRoleDef().getEntityAlias().fetchSql().sql + ");\n" +
|
||||
"assert superRoleObjectUuid is not null, 'superRoleObjectUuid must not be null';",
|
||||
with("columns", g.getSuperRoleDef().getEntityAlias().aliasName() + ".uuid"),
|
||||
with("ref", NEW.name()));
|
||||
});
|
||||
plPgSql.writeLn();
|
||||
plPgSql.writeLn("""
|
||||
if ( not hasInsertPermission(superRoleObjectUuid, 'INSERT', '${rawSubTable}') ) then
|
||||
raise exception
|
||||
'[403] insert into ${rawSubTable} not allowed for current subjects % (%)',
|
||||
currentSubjects(), currentSubjectsUuids();
|
||||
end if;
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
create trigger ${rawSubTable}_insert_permission_check_tg
|
||||
before insert on ${rawSubTable}
|
||||
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 ${rawSubTable}_insert_permission_missing_tf();
|
||||
execute procedure ${rawSubTable}_insert_permission_check_tf();
|
||||
|
||||
""",
|
||||
with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableName()));
|
||||
});
|
||||
}
|
||||
|
||||
private void generateInsertPermissionTriggerAllowOnlyGlobalAdmin(final StringWriter plPgSql) {
|
||||
plPgSql.writeLn("""
|
||||
/**
|
||||
Checks if the user or assumed roles are allowed to insert a row to ${rawSubTable},
|
||||
where only global-admin has that permission.
|
||||
*/
|
||||
create or replace function ${rawSubTable}_insert_permission_missing_tf()
|
||||
returns trigger
|
||||
language plpgsql as $$
|
||||
begin
|
||||
raise exception '[403] insert into ${rawSubTable} not allowed for current subjects % (%)',
|
||||
currentSubjects(), currentSubjectsUuids();
|
||||
end; $$;
|
||||
|
||||
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()));
|
||||
}
|
||||
|
||||
private Stream<RbacView.RbacGrantDefinition> getInsertGrants() {
|
||||
@ -162,4 +251,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 + ")";
|
||||
}
|
||||
}
|
||||
|
@ -26,18 +26,20 @@ public class RbacIdentityViewGenerator {
|
||||
plPgSql.writeLn(
|
||||
switch (rbacDef.getIdentityViewSqlQuery().part) {
|
||||
case SQL_PROJECTION -> """
|
||||
call generateRbacIdentityViewFromProjection('${rawTableName}', $idName$
|
||||
${identityViewSqlPart}
|
||||
call generateRbacIdentityViewFromProjection('${rawTableName}',
|
||||
$idName$
|
||||
${identityViewSqlPart}
|
||||
$idName$);
|
||||
""";
|
||||
case SQL_QUERY -> """
|
||||
call generateRbacIdentityViewFromProjection('${rawTableName}', $idName$
|
||||
${identityViewSqlPart}
|
||||
call generateRbacIdentityViewFromQuery('${rawTableName}',
|
||||
$idName$
|
||||
${identityViewSqlPart}
|
||||
$idName$);
|
||||
""";
|
||||
default -> throw new IllegalStateException("illegal SQL part given");
|
||||
},
|
||||
with("identityViewSqlPart", rbacDef.getIdentityViewSqlQuery().sql),
|
||||
with("identityViewSqlPart", StringWriter.indented(2, rbacDef.getIdentityViewSqlQuery().sql)),
|
||||
with("rawTableName", rawTableName));
|
||||
|
||||
plPgSql.writeLn("--//");
|
||||
|
@ -8,13 +8,11 @@ import static net.hostsharing.hsadminng.rbac.rbacdef.StringWriter.with;
|
||||
public class RbacRestrictedViewGenerator {
|
||||
private final RbacView rbacDef;
|
||||
private final String liquibaseTagPrefix;
|
||||
private final String simpleEntityVarName;
|
||||
private final String rawTableName;
|
||||
|
||||
public RbacRestrictedViewGenerator(final RbacView rbacDef, final String liquibaseTagPrefix) {
|
||||
this.rbacDef = rbacDef;
|
||||
this.liquibaseTagPrefix = liquibaseTagPrefix;
|
||||
this.simpleEntityVarName = rbacDef.getRootEntityAlias().simpleName();
|
||||
this.rawTableName = rbacDef.getRootEntityAlias().getRawTableName();
|
||||
}
|
||||
|
||||
@ -24,7 +22,9 @@ public class RbacRestrictedViewGenerator {
|
||||
--changeset ${liquibaseTagPrefix}-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacRestrictedView('${rawTableName}',
|
||||
'${orderBy}',
|
||||
$orderBy$
|
||||
${orderBy}
|
||||
$orderBy$,
|
||||
$updates$
|
||||
${updates}
|
||||
$updates$);
|
||||
@ -32,10 +32,10 @@ public class RbacRestrictedViewGenerator {
|
||||
|
||||
""",
|
||||
with("liquibaseTagPrefix", liquibaseTagPrefix),
|
||||
with("orderBy", rbacDef.getOrderBySqlExpression().sql),
|
||||
with("updates", indented(rbacDef.getUpdatableColumns().stream()
|
||||
with("orderBy", indented(2, rbacDef.getOrderBySqlExpression().sql)),
|
||||
with("updates", indented(2, rbacDef.getUpdatableColumns().stream()
|
||||
.map(c -> c + " = new." + c)
|
||||
.collect(joining(",\n")), 2)),
|
||||
.collect(joining(",\n")))),
|
||||
with("rawTableName", rawTableName));
|
||||
}
|
||||
}
|
||||
|
@ -32,8 +32,10 @@ import java.util.stream.Stream;
|
||||
import static java.lang.reflect.Modifier.isStatic;
|
||||
import static java.util.Arrays.stream;
|
||||
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.SQL.autoFetched;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.Part.AUTO_FETCH;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.directlyFetchedByDependsOnColumn;
|
||||
import static org.apache.commons.lang3.StringUtils.uncapitalize;
|
||||
|
||||
@Getter
|
||||
@ -65,6 +67,21 @@ public class RbacView {
|
||||
private EntityAlias rootEntityAliasProxy;
|
||||
private RbacRoleDefinition previousRoleDef;
|
||||
|
||||
/** Crates an RBAC definition template for the given entity class and defining the given alias.
|
||||
*
|
||||
* @param alias
|
||||
* an alias name for this entity/table, which can be used in further grants
|
||||
*
|
||||
* @param entityClass
|
||||
* the Java class for which this RBAC definition is to be defined
|
||||
* (the class to which the calling method belongs)
|
||||
*
|
||||
* @return
|
||||
* the newly created RBAC definition template
|
||||
*
|
||||
* @param <E>
|
||||
* a JPA entity class extending RbacObject
|
||||
*/
|
||||
public static <E extends RbacObject> RbacView rbacViewFor(final String alias, final Class<E> entityClass) {
|
||||
return new RbacView(alias, entityClass);
|
||||
}
|
||||
@ -76,22 +93,71 @@ public class RbacView {
|
||||
entityAliases.put("global", new EntityAlias("global"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies, which columns of the restricted view are updatable at all.
|
||||
*
|
||||
* @param columnNames
|
||||
* A list of the updatable columns.
|
||||
*
|
||||
* @return
|
||||
* the `this` instance itself to allow chained calls.
|
||||
*/
|
||||
public RbacView withUpdatableColumns(final String... columnNames) {
|
||||
Collections.addAll(updatableColumns, columnNames);
|
||||
verifyVersionColumnExists();
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Specifies the SQL query which creates the identity view for this entity.
|
||||
*
|
||||
* <p>An identity view is a view which maps an objectUuid to an idName.
|
||||
* The idName should be a human-readable representation of the row, but as short as possible.
|
||||
* The idName must only consist of letters (A-Z, a-z), digits (0-9), dash (-), dot (.) and unserscore '_'.
|
||||
* It's used to create the object-specific-role-names like test_customer#abc.admin - here 'abc' is the idName.
|
||||
* The idName not necessarily unique in a table, but it should be avoided.
|
||||
* </p>
|
||||
*
|
||||
* @param sqlExpression
|
||||
* Either specify an SQL projection (the part between SELECT and FROM), e.g. `SQL.projection("columnName")
|
||||
* or the whole SELECT query returning the uuid and idName columns,
|
||||
* e.g. `SQL.query("SELECT ... AS uuid, ... AS idName FROM ... JOIN ...").
|
||||
* Only add really important columns, just enough to create a short human-readable representation.
|
||||
*
|
||||
* @return
|
||||
* the `this` instance itself to allow chained calls.
|
||||
*/
|
||||
public RbacView withIdentityView(final SQL sqlExpression) {
|
||||
this.identityViewSqlQuery = sqlExpression;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies a ORDER BY clause for the generated restricted view.
|
||||
*
|
||||
* <p>A restricted view is generated, no matter if the order was specified or not.</p>
|
||||
*
|
||||
* @param orderBySqlExpression
|
||||
* That's the part behind `ORDER BY`, e.g. `SQL.expression("prefix").
|
||||
*
|
||||
* @return
|
||||
* the `this` instance itself to allow chained calls.
|
||||
*/
|
||||
public RbacView withRestrictedViewOrderBy(final SQL orderBySqlExpression) {
|
||||
this.orderBySqlExpression = orderBySqlExpression;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies that the given role (OWNER, ADMIN, ...) is to be created for new/updated roles in this table.
|
||||
*
|
||||
* @param role
|
||||
* OWNER, ADMIN, AGENT etc.
|
||||
* @param with
|
||||
* a lambda which receives the created role to create grants and permissions to and from the newly created role,
|
||||
* e.g. the owning user, incoming superroles, outgoing subroles
|
||||
* @return
|
||||
* the `this` instance itself to allow chained calls.
|
||||
*/
|
||||
public RbacView createRole(final Role role, final Consumer<RbacRoleDefinition> with) {
|
||||
final RbacRoleDefinition newRoleDef = findRbacRole(rootEntityAlias, role).toCreate();
|
||||
with.accept(newRoleDef);
|
||||
@ -99,6 +165,15 @@ public class RbacView {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies that the given role (OWNER, ADMIN, ...) is to be created for new/updated roles in this table,
|
||||
* which is becomes sub-role of the previously created role.
|
||||
*
|
||||
* @param role
|
||||
* OWNER, ADMIN, AGENT etc.
|
||||
* @return
|
||||
* the `this` instance itself to allow chained calls.
|
||||
*/
|
||||
public RbacView createSubRole(final Role role) {
|
||||
final RbacRoleDefinition newRoleDef = findRbacRole(rootEntityAlias, role).toCreate();
|
||||
findOrCreateGrantDef(newRoleDef, previousRoleDef).toCreate();
|
||||
@ -106,6 +181,19 @@ public class RbacView {
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Specifies that the given role (OWNER, ADMIN, ...) is to be created for new/updated roles in this table,
|
||||
* which is becomes sub-role of the previously created role.
|
||||
*
|
||||
* @param role
|
||||
* OWNER, ADMIN, AGENT etc.
|
||||
* @param with
|
||||
* a lambda which receives the created role to create grants and permissions to and from the newly created role,
|
||||
* e.g. the owning user, incoming superroles, outgoing subroles
|
||||
* @return
|
||||
* the `this` instance itself to allow chained calls.
|
||||
*/
|
||||
public RbacView createSubRole(final Role role, final Consumer<RbacRoleDefinition> with) {
|
||||
final RbacRoleDefinition newRoleDef = findRbacRole(rootEntityAlias, role).toCreate();
|
||||
findOrCreateGrantDef(newRoleDef, previousRoleDef).toCreate();
|
||||
@ -114,10 +202,38 @@ public class RbacView {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies that the given permission is to be created for each new row in the target table.
|
||||
*
|
||||
* <p>Grants to permissions created by this method have to be specified separately,
|
||||
* often it's easier to read to use createRole/createSubRole and use with.permission(...).</p>
|
||||
*
|
||||
* @param permission
|
||||
* e.g. INSERT, SELECT, UPDATE, DELETE
|
||||
*
|
||||
* @return
|
||||
* the newly created permission definition
|
||||
*/
|
||||
public RbacPermissionDefinition createPermission(final Permission permission) {
|
||||
return createPermission(rootEntityAlias, permission);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies that the given permission is to be created for each new row in the target table,
|
||||
* but for another table, e.g. a table with details data with different access rights.
|
||||
*
|
||||
* <p>Grants to permissions created by this method have to be specified separately,
|
||||
* often it's easier to read to use createRole/createSubRole and use with.permission(...).</p>
|
||||
*
|
||||
* @param entityAliasName
|
||||
* A previously defined entity alias name.
|
||||
*
|
||||
* @param permission
|
||||
* e.g. INSERT, SELECT, UPDATE, DELETE
|
||||
*
|
||||
* @return
|
||||
* the newly created permission definition
|
||||
*/
|
||||
public RbacPermissionDefinition createPermission(final String entityAliasName, final Permission permission) {
|
||||
return createPermission(findEntityAlias(entityAliasName), permission);
|
||||
}
|
||||
@ -133,6 +249,32 @@ public class RbacView {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports the RBAC template from the given entity class and defines an alias name for it.
|
||||
* This method is especially for proxy-entities, if the root entity does not have its own
|
||||
* roles, a proxy-entity can be specified and its roles can be used instead.
|
||||
*
|
||||
* @param aliasName
|
||||
* An alias name for the entity class. The same entity class can be imported multiple times,
|
||||
* if multiple references to its table exist, then distinct alias names habe to be defined.
|
||||
*
|
||||
* @param entityClass
|
||||
* A JPA entity class extending RbacObject which also implements an `rbac` method returning
|
||||
* its RBAC specification.
|
||||
*
|
||||
* @param fetchSql
|
||||
* An SQL SELECT statement which fetches the referenced row. Use `${REF}` to speficiy the
|
||||
* newly created or updated row (will be replaced by NEW/OLD from the trigger method).
|
||||
*
|
||||
* @param dependsOnColum
|
||||
* The column, usually containing an uuid, on which this other table depends.
|
||||
*
|
||||
* @return
|
||||
* the newly created permission definition
|
||||
*
|
||||
* @param <EC>
|
||||
* a JPA entity class extending RbacObject
|
||||
*/
|
||||
public <EC extends RbacObject> RbacView importRootEntityAliasProxy(
|
||||
final String aliasName,
|
||||
final Class<? extends HasUuid> entityClass,
|
||||
@ -141,35 +283,75 @@ public class RbacView {
|
||||
if (rootEntityAliasProxy != null) {
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports the RBAC template from the given entity class and defines an alias name for it.
|
||||
* This method is especially to declare sub-entities, e.g. details to a main object.
|
||||
*
|
||||
* @see {@link}
|
||||
*
|
||||
* @return
|
||||
* the newly created permission definition
|
||||
*
|
||||
* @param <EC>
|
||||
* a JPA entity class extending RbacObject
|
||||
*/
|
||||
public RbacView importSubEntityAlias(
|
||||
final String aliasName, final Class<? extends HasUuid> entityClass,
|
||||
final SQL fetchSql, final Column dependsOnColum) {
|
||||
importEntityAliasImpl(aliasName, entityClass, fetchSql, dependsOnColum, true);
|
||||
importEntityAliasImpl(aliasName, entityClass, fetchSql, dependsOnColum, true, NOT_NULL);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports the RBAC template from the given entity class and defines an anlias name for it.
|
||||
*
|
||||
* @param aliasName
|
||||
* An alias name for the entity class. The same entity class can be imported multiple times,
|
||||
* if multiple references to its table exist, then distinct alias names habe to be defined.
|
||||
*
|
||||
* @param entityClass
|
||||
* A JPA entity class extending RbacObject which also implements an `rbac` method returning
|
||||
* its RBAC specification.
|
||||
*
|
||||
* @param fetchSql
|
||||
* An SQL SELECT statement which fetches the referenced row. Use `${REF}` to speficiy the
|
||||
* newly created or updated row (will be replaced by NEW/OLD from the trigger method).
|
||||
*
|
||||
* @param dependsOnColum
|
||||
* The column, usually containing an uuid, on which this other table depends.
|
||||
*
|
||||
* @param nullable
|
||||
* Specifies whether the dependsOnColum is nullable or not.
|
||||
*
|
||||
* @return
|
||||
* the newly created permission definition
|
||||
*
|
||||
* @param <EC>
|
||||
* a JPA entity class extending RbacObject
|
||||
*/
|
||||
public RbacView importEntityAlias(
|
||||
final String aliasName, final Class<? extends HasUuid> entityClass,
|
||||
final Column dependsOnColum, final SQL fetchSql) {
|
||||
importEntityAliasImpl(aliasName, entityClass, fetchSql, dependsOnColum, false);
|
||||
final Column dependsOnColum, final SQL fetchSql, final Nullable nullable) {
|
||||
importEntityAliasImpl(aliasName, entityClass, fetchSql, dependsOnColum, false, nullable);
|
||||
return this;
|
||||
}
|
||||
|
||||
// TODO: remove once it's not used in HsOffice...Entity anymore
|
||||
public RbacView importEntityAlias(
|
||||
final String aliasName, final Class<? extends HasUuid> entityClass,
|
||||
final Column dependsOnColum) {
|
||||
importEntityAliasImpl(aliasName, entityClass, autoFetched(), dependsOnColum, false);
|
||||
importEntityAliasImpl(aliasName, entityClass, directlyFetchedByDependsOnColumn(), dependsOnColum, false, null);
|
||||
return this;
|
||||
}
|
||||
|
||||
private EntityAlias importEntityAliasImpl(
|
||||
final String aliasName, final Class<? extends HasUuid> entityClass,
|
||||
final SQL fetchSql, final Column dependsOnColum, boolean asSubEntity) {
|
||||
final var entityAlias = new EntityAlias(aliasName, entityClass, fetchSql, dependsOnColum, asSubEntity);
|
||||
final SQL fetchSql, final Column dependsOnColum, boolean asSubEntity, final Nullable nullable) {
|
||||
final var entityAlias = new EntityAlias(aliasName, entityClass, fetchSql, dependsOnColum, asSubEntity, nullable);
|
||||
entityAliases.put(aliasName, entityAlias);
|
||||
try {
|
||||
importAsAlias(aliasName, rbacDefinition(entityClass), asSubEntity);
|
||||
@ -224,6 +406,16 @@ public class RbacView {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts declaring a grant to a given role.
|
||||
*
|
||||
* @param entityAlias
|
||||
* A previously speciried entity alias name.
|
||||
* @param role
|
||||
* OWNER, ADMIN, AGENT, ...
|
||||
* @return
|
||||
* a grant builder
|
||||
*/
|
||||
public RbacGrantBuilder toRole(final String entityAlias, final Role role) {
|
||||
return new RbacGrantBuilder(entityAlias, role);
|
||||
}
|
||||
@ -281,15 +473,19 @@ public class RbacView {
|
||||
return RbacView.this;
|
||||
}
|
||||
|
||||
public RbacView grantPermission(final String entityAliasName, final Permission perm) {
|
||||
final var entityAlias = findEntityAlias(entityAliasName);
|
||||
final var forTable = entityAlias.getRawTableName();
|
||||
findOrCreateGrantDef(findRbacPerm(entityAlias, perm, forTable), superRoleDef).toCreate();
|
||||
public RbacView grantPermission(final Permission perm) {
|
||||
final var forTable = rootEntityAlias.getRawTableName();
|
||||
findOrCreateGrantDef(findRbacPerm(rootEntityAlias, perm, forTable), superRoleDef).toCreate();
|
||||
return RbacView.this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public enum Nullable {
|
||||
NOT_NULL, // DEFAULT
|
||||
NULLABLE
|
||||
}
|
||||
|
||||
@Getter
|
||||
@EqualsAndHashCode
|
||||
public class RbacGrantDefinition {
|
||||
@ -418,6 +614,16 @@ public class RbacView {
|
||||
permDefs.add(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Grants the permission under definition to the given role.
|
||||
*
|
||||
* @param entityAlias
|
||||
* A previously declared entity alias name.
|
||||
* @param role
|
||||
* OWNER, ADMIN, ...
|
||||
* @return
|
||||
* The RbacView specification to which this permission definition belongs.
|
||||
*/
|
||||
public RbacView grantedTo(final String entityAlias, final Role role) {
|
||||
findOrCreateGrantDef(this, findRbacRole(entityAlias, role)).toCreate();
|
||||
return RbacView.this;
|
||||
@ -448,19 +654,61 @@ public class RbacView {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies which user becomes the owner of newly created objects.
|
||||
* @param userRole
|
||||
* GLOBAL_ADMIN, CREATOR, ...
|
||||
* @return
|
||||
* The grant definition for further chained calls.
|
||||
*/
|
||||
public RbacGrantDefinition owningUser(final RbacUserReference.UserRole userRole) {
|
||||
return grantRoleToUser(this, findUserRef(userRole));
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies which permission is to be created for newly created objects.
|
||||
* @param permission
|
||||
* INSERT, SELECT, ...
|
||||
* @return
|
||||
* The grant definition for further chained calls.
|
||||
*/
|
||||
public RbacGrantDefinition permission(final Permission permission) {
|
||||
return grantPermissionToRole(createPermission(entityAlias, permission), this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies in incoming super role which gets granted the role under definition.
|
||||
*
|
||||
* <p>Incoming means an incoming grant arrow in our grant-diagrams.
|
||||
* Super-role means that it's the role to which another role is granted.
|
||||
* Both means actually the same, just in different aspects.</p>
|
||||
*
|
||||
* @param entityAlias
|
||||
* A previously declared entity alias name.
|
||||
* @param role
|
||||
* OWNER, ADMIN, ...
|
||||
* @return
|
||||
* The grant definition for further chained calls.
|
||||
*/
|
||||
public RbacGrantDefinition incomingSuperRole(final String entityAlias, final Role role) {
|
||||
final var incomingSuperRole = findRbacRole(entityAlias, role);
|
||||
return grantSubRoleToSuperRole(this, incomingSuperRole);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies in outgoing sub role which gets granted the role under definition.
|
||||
*
|
||||
* <p>Outgoing means an outgoing grant arrow in our grant-diagrams.
|
||||
* Sub-role means which is granted to another role.
|
||||
* Both means actually the same, just in different aspects.</p>
|
||||
*
|
||||
* @param entityAlias
|
||||
* A previously declared entity alias name.
|
||||
* @param role
|
||||
* OWNER, ADMIN, ...
|
||||
* @return
|
||||
* The grant definition for further chained calls.
|
||||
*/
|
||||
public RbacGrantDefinition outgoingSubRole(final String entityAlias, final Role role) {
|
||||
final var outgoingSubRole = findRbacRole(entityAlias, role);
|
||||
return grantSubRoleToSuperRole(outgoingSubRole, this);
|
||||
@ -560,14 +808,14 @@ public class RbacView {
|
||||
.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) {
|
||||
this(aliasName, null, null, null, false);
|
||||
this(aliasName, null, null, null, false, null);
|
||||
}
|
||||
|
||||
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() {
|
||||
@ -592,8 +840,8 @@ public class RbacView {
|
||||
};
|
||||
}
|
||||
|
||||
public boolean hasFetchSql() {
|
||||
return fetchSql != null;
|
||||
boolean isFetchedByDirectForeignKey() {
|
||||
return fetchSql != null && fetchSql.part == AUTO_FETCH;
|
||||
}
|
||||
|
||||
private String withoutEntitySuffix(final String simpleEntityName) {
|
||||
@ -626,39 +874,35 @@ 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();
|
||||
}
|
||||
}
|
||||
|
||||
public record Permission(String permission) {
|
||||
|
||||
public static final Permission INSERT = new Permission("INSERT");
|
||||
public static final Permission DELETE = new Permission("DELETE");
|
||||
public static final Permission UPDATE = new Permission("UPDATE");
|
||||
public static final Permission SELECT = new Permission("SELECT");
|
||||
|
||||
public static Permission custom(final String permission) {
|
||||
return new Permission(permission);
|
||||
}
|
||||
public enum Permission {
|
||||
INSERT,
|
||||
DELETE,
|
||||
UPDATE,
|
||||
SELECT;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return ":" + permission;
|
||||
return ":" + name();
|
||||
}
|
||||
}
|
||||
|
||||
@ -666,14 +910,25 @@ public class RbacView {
|
||||
|
||||
/**
|
||||
* DSL method to specify an SQL SELECT expression which fetches the related entity,
|
||||
* using the reference `${ref}` of the root entity.
|
||||
* `${ref}` is going to be replaced by either `NEW` or `OLD` of the trigger function.
|
||||
* `into ...` will be added with a variable name prefixed with either `new` or `old`.
|
||||
* using the reference `${ref}` of the root entity and `${columns}` for the projection.
|
||||
*
|
||||
* <p>The query <strong>must define</strong> the entity alias name of the fetched table
|
||||
* as its alias for, so it can be used in the generated projection (the columns between
|
||||
* `SELECT` and `FROM`.</p>
|
||||
*
|
||||
* <p>`${ref}` is going to be replaced by either `NEW` or `OLD` of the trigger function.
|
||||
* `into ...` will be added with a variable name prefixed with either `new` or `old`.</p>
|
||||
*
|
||||
* <p>`${columns}` is going to be replaced by the columns which are needed for the query,
|
||||
* e.g. `*` or `uuid`.</p>
|
||||
*
|
||||
* @param sql an SQL SELECT expression (not ending with ';)
|
||||
* @return the wrapped SQL expression
|
||||
*/
|
||||
public static SQL fetchedBySql(final String sql) {
|
||||
if ( !sql.startsWith("SELECT ${columns}") ) {
|
||||
throw new IllegalArgumentException("SQL SELECT expression must start with 'SELECT ${columns}', but is: " + sql);
|
||||
}
|
||||
validateExpression(sql);
|
||||
return new SQL(sql, Part.SQL_QUERY);
|
||||
}
|
||||
@ -685,8 +940,8 @@ public class RbacView {
|
||||
*
|
||||
* @return the wrapped SQL definition object
|
||||
*/
|
||||
public static SQL autoFetched() {
|
||||
return new SQL(null, Part.AUTO_FETCH);
|
||||
public static SQL directlyFetchedByDependsOnColumn() {
|
||||
return new SQL(null, AUTO_FETCH);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -794,6 +1049,26 @@ public class RbacView {
|
||||
}
|
||||
}
|
||||
|
||||
private static void generateRbacView(final Class<? extends HasUuid> c) {
|
||||
final Method mainMethod = stream(c.getMethods()).filter(
|
||||
m -> isStatic(m.getModifiers()) && m.getName().equals("main")
|
||||
)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
if (mainMethod != null) {
|
||||
try {
|
||||
mainMethod.invoke(null, new Object[] { null });
|
||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
} else {
|
||||
System.err.println("WARNING: no main method in: " + c.getName() + " => no RBAC rules generated");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This main method generates the RbacViews (PostgreSQL+diagram) for all given entity classes.
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
Stream.of(
|
||||
TestCustomerEntity.class,
|
||||
@ -810,21 +1085,6 @@ public class RbacView {
|
||||
HsOfficeSepaMandateEntity.class,
|
||||
HsOfficeCoopSharesTransactionEntity.class,
|
||||
HsOfficeMembershipEntity.class
|
||||
).forEach(c -> {
|
||||
final Method mainMethod = stream(c.getMethods()).filter(
|
||||
m -> isStatic(m.getModifiers()) && m.getName().equals("main")
|
||||
)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
if (mainMethod != null) {
|
||||
try {
|
||||
mainMethod.invoke(null, new Object[] { null });
|
||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
} else {
|
||||
System.err.println("no main method in: " + c.getName());
|
||||
}
|
||||
});
|
||||
).forEach(RbacView::generateRbacView);
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ import lombok.SneakyThrows;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.nio.file.*;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static java.util.stream.Collectors.joining;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacGrantDefinition.GrantType.*;
|
||||
@ -149,14 +148,13 @@ public class RbacViewMermaidFlowchartGenerator {
|
||||
"""
|
||||
### rbac %{entityAlias}
|
||||
|
||||
This code generated was by RbacViewMermaidFlowchartGenerator at %{timestamp}.
|
||||
This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually.
|
||||
|
||||
```mermaid
|
||||
%{flowchart}
|
||||
```
|
||||
"""
|
||||
.replace("%{entityAlias}", rbacDef.getRootEntityAlias().aliasName())
|
||||
.replace("%{timestamp}", LocalDateTime.now().toString())
|
||||
.replace("%{flowchart}", flowchart.toString()),
|
||||
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
|
||||
System.out.println("Markdown-File: " + path.toAbsolutePath());
|
||||
|
@ -5,7 +5,6 @@ import lombok.SneakyThrows;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.PostgresTriggerReference.NEW;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.StringWriter.with;
|
||||
@ -21,10 +20,9 @@ public class RbacViewPostgresGenerator {
|
||||
liqibaseTagPrefix = rbacDef.getRootEntityAlias().getRawTableName().replace("_", "-");
|
||||
plPgSql.writeLn("""
|
||||
--liquibase formatted sql
|
||||
-- This code generated was by ${generator} at ${timestamp}.
|
||||
-- This code generated was by ${generator}, do not amend manually.
|
||||
""",
|
||||
with("generator", getClass().getSimpleName()),
|
||||
with("timestamp", LocalDateTime.now().toString()),
|
||||
with("ref", NEW.name()));
|
||||
|
||||
new RbacObjectGenerator(rbacDef, liqibaseTagPrefix).generateTo(plPgSql);
|
||||
@ -37,8 +35,11 @@ public class RbacViewPostgresGenerator {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return plPgSql.toString();
|
||||
}
|
||||
return plPgSql.toString()
|
||||
.replace("\n\n\n", "\n\n")
|
||||
.replace("-- ====", "\n-- ====")
|
||||
.replace("\n\n--//", "\n--//");
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public void generateToChangeLog(final Path outputPath) {
|
||||
|
@ -7,6 +7,7 @@ import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static java.util.Optional.ofNullable;
|
||||
import static java.util.stream.Collectors.joining;
|
||||
import static java.util.stream.Collectors.toSet;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.PostgresTriggerReference.NEW;
|
||||
@ -82,6 +83,7 @@ class RolesGrantsAndPermissionsGenerator {
|
||||
plPgSql.writeLn("begin");
|
||||
plPgSql.indented(() -> {
|
||||
plPgSql.writeLn("call enterTriggerForObjectUuid(NEW.uuid);");
|
||||
plPgSql.writeLn();
|
||||
generateCreateRolesAndGrantsAfterInsert(plPgSql);
|
||||
plPgSql.ensureSingleEmptyLine();
|
||||
plPgSql.writeLn("call leaveTriggerForObjectUuid(NEW.uuid);");
|
||||
@ -90,6 +92,37 @@ class RolesGrantsAndPermissionsGenerator {
|
||||
plPgSql.writeLn();
|
||||
}
|
||||
|
||||
|
||||
private void generateSimplifiedUpdateTriggerFunction(final StringWriter plPgSql) {
|
||||
|
||||
final var updateConditions = updatableEntityAliases()
|
||||
.map(RbacView.EntityAlias::dependsOnColumName)
|
||||
.distinct()
|
||||
.map(columnName -> "NEW." + columnName + " is distinct from OLD." + columnName)
|
||||
.collect(joining( "\n or "));
|
||||
plPgSql.writeLn("""
|
||||
/*
|
||||
Called from the AFTER UPDATE TRIGGER to re-wire the grants.
|
||||
*/
|
||||
|
||||
create or replace procedure updateRbacRulesFor${simpleEntityName}(
|
||||
OLD ${rawTableName},
|
||||
NEW ${rawTableName}
|
||||
)
|
||||
language plpgsql as $$
|
||||
begin
|
||||
|
||||
if ${updateConditions} then
|
||||
delete from rbacgrants g where g.grantedbytriggerof = OLD.uuid;
|
||||
call buildRbacSystemFor${simpleEntityName}(NEW);
|
||||
end if;
|
||||
end; $$;
|
||||
""",
|
||||
with("simpleEntityName", simpleEntityName),
|
||||
with("rawTableName", rawTableName),
|
||||
with("updateConditions", updateConditions));
|
||||
}
|
||||
|
||||
private void generateUpdateTriggerFunction(final StringWriter plPgSql) {
|
||||
plPgSql.writeLn("""
|
||||
/*
|
||||
@ -109,7 +142,7 @@ class RolesGrantsAndPermissionsGenerator {
|
||||
|
||||
plPgSql.chopEmptyLines();
|
||||
plPgSql.indented(() -> {
|
||||
updatableEntityAliases()
|
||||
referencedEntityAliases()
|
||||
.forEach((ea) -> {
|
||||
plPgSql.writeLn(entityRefVar(OLD, ea) + " " + ea.getRawTableName() + ";");
|
||||
plPgSql.writeLn(entityRefVar(NEW, ea) + " " + ea.getRawTableName() + ";");
|
||||
@ -120,6 +153,7 @@ class RolesGrantsAndPermissionsGenerator {
|
||||
plPgSql.writeLn("begin");
|
||||
plPgSql.indented(() -> {
|
||||
plPgSql.writeLn("call enterTriggerForObjectUuid(NEW.uuid);");
|
||||
plPgSql.writeLn();
|
||||
generateUpdateRolesAndGrantsAfterUpdate(plPgSql);
|
||||
plPgSql.ensureSingleEmptyLine();
|
||||
plPgSql.writeLn("call leaveTriggerForObjectUuid(NEW.uuid);");
|
||||
@ -132,11 +166,18 @@ class RolesGrantsAndPermissionsGenerator {
|
||||
return updatableEntityAliases().anyMatch(e -> true);
|
||||
}
|
||||
|
||||
private boolean hasAnyUpdatableAndNullableEntityAliases() {
|
||||
return updatableEntityAliases()
|
||||
.filter(ea -> ea.nullable() == RbacView.Nullable.NULLABLE)
|
||||
.anyMatch(e -> true);
|
||||
}
|
||||
|
||||
private void generateCreateRolesAndGrantsAfterInsert(final StringWriter plPgSql) {
|
||||
referencedEntityAliases()
|
||||
.forEach((ea) -> plPgSql.writeLn(
|
||||
ea.fetchSql().sql + " into " + entityRefVar(NEW, ea) + ";",
|
||||
with("ref", NEW.name())));
|
||||
.forEach((ea) -> {
|
||||
generateFetchedVars(plPgSql, ea, NEW);
|
||||
plPgSql.writeLn();
|
||||
});
|
||||
|
||||
createRolesWithGrantsSql(plPgSql, OWNER);
|
||||
createRolesWithGrantsSql(plPgSql, ADMIN);
|
||||
@ -165,14 +206,11 @@ class RolesGrantsAndPermissionsGenerator {
|
||||
private void generateUpdateRolesAndGrantsAfterUpdate(final StringWriter plPgSql) {
|
||||
plPgSql.ensureSingleEmptyLine();
|
||||
|
||||
updatableEntityAliases()
|
||||
referencedEntityAliases()
|
||||
.forEach((ea) -> {
|
||||
plPgSql.writeLn(
|
||||
ea.fetchSql().sql + " into " + entityRefVar(OLD, ea) + ";",
|
||||
with("ref", OLD.name()));
|
||||
plPgSql.writeLn(
|
||||
ea.fetchSql().sql + " into " + entityRefVar(NEW, ea) + ";",
|
||||
with("ref", NEW.name()));
|
||||
generateFetchedVars(plPgSql, ea, OLD);
|
||||
generateFetchedVars(plPgSql, ea, NEW);
|
||||
plPgSql.writeLn();
|
||||
});
|
||||
|
||||
updatableEntityAliases()
|
||||
@ -190,14 +228,29 @@ class RolesGrantsAndPermissionsGenerator {
|
||||
});
|
||||
}
|
||||
|
||||
private boolean isUpdatable(final RbacView.Column c) {
|
||||
return rbacDef.getUpdatableColumns().contains(c);
|
||||
private void generateFetchedVars(
|
||||
final StringWriter plPgSql,
|
||||
final RbacView.EntityAlias ea,
|
||||
final PostgresTriggerReference old) {
|
||||
plPgSql.writeLn(
|
||||
ea.fetchSql().sql + " INTO " + entityRefVar(old, ea) + ";",
|
||||
with("columns", ea.aliasName() + ".*"),
|
||||
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 void updateGrantsDependingOn(final StringWriter plPgSql, final String columnName) {
|
||||
rbacDef.getGrantDefs().stream()
|
||||
.filter(RbacView.RbacGrantDefinition::isToCreate)
|
||||
.filter(g -> g.dependsOnColumn(columnName))
|
||||
.filter(g -> !isInsertPermissionGrant(g))
|
||||
.forEach(g -> {
|
||||
plPgSql.ensureSingleEmptyLine();
|
||||
plPgSql.writeLn(generateRevoke(g));
|
||||
@ -206,6 +259,11 @@ class RolesGrantsAndPermissionsGenerator {
|
||||
});
|
||||
}
|
||||
|
||||
private static Boolean isInsertPermissionGrant(final RbacView.RbacGrantDefinition g) {
|
||||
final var isInsertPermissionGrant = ofNullable(g.getPermDef()).map(RbacPermissionDefinition::getPermission).map(p -> p == INSERT).orElse(false);
|
||||
return isInsertPermissionGrant;
|
||||
}
|
||||
|
||||
private void generateGrants(final StringWriter plPgSql, final RbacView.RbacGrantDefinition.GrantType grantType) {
|
||||
plPgSql.ensureSingleEmptyLine();
|
||||
rbacGrants.stream()
|
||||
@ -222,7 +280,7 @@ class RolesGrantsAndPermissionsGenerator {
|
||||
.replace("${subRoleRef}", roleRef(OLD, grantDef.getSubRoleDef()))
|
||||
.replace("${superRoleRef}", roleRef(OLD, grantDef.getSuperRoleDef()));
|
||||
case PERM_TO_ROLE -> "call revokePermissionFromRole(${permRef}, ${superRoleRef});"
|
||||
.replace("${permRef}", findPerm(OLD, grantDef.getPermDef()))
|
||||
.replace("${permRef}", getPerm(OLD, grantDef.getPermDef()))
|
||||
.replace("${superRoleRef}", roleRef(OLD, grantDef.getSuperRoleDef()));
|
||||
};
|
||||
}
|
||||
@ -246,6 +304,10 @@ class RolesGrantsAndPermissionsGenerator {
|
||||
return permRef("findPermissionId", ref, permDef);
|
||||
}
|
||||
|
||||
private String getPerm(final PostgresTriggerReference ref, final RbacPermissionDefinition permDef) {
|
||||
return permRef("getPermissionId", ref, permDef);
|
||||
}
|
||||
|
||||
private String createPerm(final PostgresTriggerReference ref, final RbacPermissionDefinition permDef) {
|
||||
return permRef("createPermission", ref, permDef);
|
||||
}
|
||||
@ -256,7 +318,7 @@ class RolesGrantsAndPermissionsGenerator {
|
||||
.replace("${entityRef}", rbacDef.isRootEntityAlias(permDef.entityAlias)
|
||||
? ref.name()
|
||||
: refVarName(ref, permDef.entityAlias))
|
||||
.replace("${perm}", permDef.permission.permission());
|
||||
.replace("${perm}", permDef.permission.name());
|
||||
}
|
||||
|
||||
private String refVarName(final PostgresTriggerReference ref, final RbacView.EntityAlias entityAlias) {
|
||||
@ -301,12 +363,12 @@ class RolesGrantsAndPermissionsGenerator {
|
||||
|
||||
generatePermissionsForRole(plPgSql, role);
|
||||
|
||||
generateUserGrantsForRole(plPgSql, role);
|
||||
|
||||
generateIncomingSuperRolesForRole(plPgSql, role);
|
||||
|
||||
generateOutgoingSubRolesForRole(plPgSql, role);
|
||||
|
||||
generateUserGrantsForRole(plPgSql, role);
|
||||
|
||||
plPgSql.chopTail(",\n");
|
||||
plPgSql.writeLn();
|
||||
});
|
||||
@ -333,7 +395,7 @@ class RolesGrantsAndPermissionsGenerator {
|
||||
final var arrayElements = permissionGrantsForRole.stream()
|
||||
.map(RbacView.RbacGrantDefinition::getPermDef)
|
||||
.map(RbacPermissionDefinition::getPermission)
|
||||
.map(RbacView.Permission::permission)
|
||||
.map(RbacView.Permission::name)
|
||||
.map(p -> "'" + p + "'")
|
||||
.sorted()
|
||||
.toList();
|
||||
@ -348,7 +410,7 @@ class RolesGrantsAndPermissionsGenerator {
|
||||
if (!incomingGrants.isEmpty()) {
|
||||
final var arrayElements = incomingGrants.stream()
|
||||
.map(g -> toPlPgSqlReference(NEW, g.getSuperRoleDef(), g.isAssumed()))
|
||||
.toList();
|
||||
.sorted().toList();
|
||||
plPgSql.indented(() ->
|
||||
plPgSql.writeLn("incomingSuperRoles => array[" + joinArrayElements(arrayElements, 1) + "],\n"));
|
||||
rbacGrants.removeAll(incomingGrants);
|
||||
@ -360,7 +422,7 @@ class RolesGrantsAndPermissionsGenerator {
|
||||
if (!outgoingGrants.isEmpty()) {
|
||||
final var arrayElements = outgoingGrants.stream()
|
||||
.map(g -> toPlPgSqlReference(NEW, g.getSubRoleDef(), g.isAssumed()))
|
||||
.toList();
|
||||
.sorted().toList();
|
||||
plPgSql.indented(() ->
|
||||
plPgSql.writeLn("outgoingSubRoles => array[" + joinArrayElements(arrayElements, 1) + "],\n"));
|
||||
rbacGrants.removeAll(outgoingGrants);
|
||||
@ -444,7 +506,11 @@ class RolesGrantsAndPermissionsGenerator {
|
||||
private void generateUpdateTrigger(final StringWriter plPgSql) {
|
||||
|
||||
generateHeader(plPgSql, "update");
|
||||
generateUpdateTriggerFunction(plPgSql);
|
||||
if ( hasAnyUpdatableAndNullableEntityAliases() ) {
|
||||
generateSimplifiedUpdateTriggerFunction(plPgSql);
|
||||
} else {
|
||||
generateUpdateTriggerFunction(plPgSql);
|
||||
}
|
||||
|
||||
plPgSql.writeLn("""
|
||||
/*
|
||||
|
@ -38,12 +38,26 @@ public class StringWriter {
|
||||
--indentLevel;
|
||||
}
|
||||
|
||||
void indent(int levels) {
|
||||
indentLevel += levels;
|
||||
}
|
||||
|
||||
void unindent(int levels) {
|
||||
indentLevel -= levels;
|
||||
}
|
||||
|
||||
void indented(final Runnable indented) {
|
||||
indent();
|
||||
indented.run();
|
||||
unindent();
|
||||
}
|
||||
|
||||
void indented(int levels, final Runnable indented) {
|
||||
indent(levels);
|
||||
indented.run();
|
||||
unindent(levels);
|
||||
}
|
||||
|
||||
boolean chopTail(final String tail) {
|
||||
if (string.toString().endsWith(tail)) {
|
||||
string.setLength(string.length() - tail.length());
|
||||
@ -68,7 +82,7 @@ public class StringWriter {
|
||||
return string.toString();
|
||||
}
|
||||
|
||||
public static String indented(final String text, final int indentLevel) {
|
||||
public static String indented(final int indentLevel, final String text) {
|
||||
final var indentation = StringUtils.repeat(" ", indentLevel);
|
||||
final var indented = stream(text.split("\n"))
|
||||
.map(line -> line.trim().isBlank() ? "" : indentation + line)
|
||||
@ -80,7 +94,7 @@ public class StringWriter {
|
||||
if ( indentLevel == 0) {
|
||||
return text;
|
||||
}
|
||||
return indented(text, indentLevel);
|
||||
return indented(indentLevel, text);
|
||||
}
|
||||
|
||||
record VarDef(String name, String value){}
|
||||
@ -95,17 +109,13 @@ public class StringWriter {
|
||||
}
|
||||
|
||||
String apply(final String textToAppend) {
|
||||
try {
|
||||
text = textToAppend;
|
||||
stream(varDefs).forEach(varDef -> {
|
||||
final var pattern = Pattern.compile("\\$\\{" + varDef.name() + "}", Pattern.CASE_INSENSITIVE);
|
||||
final var matcher = pattern.matcher(text);
|
||||
text = matcher.replaceAll(varDef.value());
|
||||
});
|
||||
return text;
|
||||
} catch (Exception exc) {
|
||||
throw exc;
|
||||
}
|
||||
}
|
||||
text = textToAppend;
|
||||
stream(varDefs).forEach(varDef -> {
|
||||
final var pattern = Pattern.compile("\\$\\{" + varDef.name() + "}", Pattern.CASE_INSENSITIVE);
|
||||
final var matcher = pattern.matcher(text);
|
||||
text = matcher.replaceAll(varDef.value());
|
||||
});
|
||||
return text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -94,4 +94,17 @@ public class RbacGrantController implements RbacGrantsApi {
|
||||
|
||||
return ResponseEntity.noContent().build();
|
||||
}
|
||||
|
||||
// TODO: implement an endpoint to create a Mermaid flowchart with all grants of a given user
|
||||
// @GetMapping(
|
||||
// path = "/api/rbac/users/{userUuid}/grants",
|
||||
// produces = {"text/vnd.mermaid"})
|
||||
// @Transactional(readOnly = true)
|
||||
// public ResponseEntity<String> allGrantsOfUserAsMermaid(
|
||||
// @RequestHeader(name = "current-user") String currentUser,
|
||||
// @RequestHeader(name = "assumed-roles", required = false) String assumedRoles) {
|
||||
// final var graph = RbacGrantsDiagramService.allGrantsToUser(currentUser);
|
||||
// return ResponseEntity.ok(graph);
|
||||
// }
|
||||
|
||||
}
|
||||
|
@ -21,6 +21,8 @@ import static net.hostsharing.hsadminng.rbac.rbacgrant.RbacGrantsDiagramService.
|
||||
@Service
|
||||
public class RbacGrantsDiagramService {
|
||||
|
||||
private static final int GRANT_LIMIT = 500;
|
||||
|
||||
public static void writeToFile(final String title, final String graph, final String fileName) {
|
||||
|
||||
try (BufferedWriter writer = new BufferedWriter(new FileWriter(fileName))) {
|
||||
@ -42,7 +44,11 @@ public class RbacGrantsDiagramService {
|
||||
PERMISSIONS,
|
||||
NOT_ASSUMED,
|
||||
TEST_ENTITIES,
|
||||
NON_TEST_ENTITIES
|
||||
NON_TEST_ENTITIES;
|
||||
|
||||
public static final EnumSet<Include> ALL = EnumSet.allOf(Include.class);
|
||||
public static final EnumSet<Include> ALL_TEST_ENTITY_RELATED = EnumSet.of(USERS, DETAILS, NOT_ASSUMED, TEST_ENTITIES, PERMISSIONS);
|
||||
public static final EnumSet<Include> ALL_NON_TEST_ENTITY_RELATED = EnumSet.of(USERS, DETAILS, NOT_ASSUMED, NON_TEST_ENTITIES, PERMISSIONS);
|
||||
}
|
||||
|
||||
@Autowired
|
||||
@ -55,7 +61,7 @@ public class RbacGrantsDiagramService {
|
||||
private EntityManager em;
|
||||
|
||||
public String allGrantsToCurrentUser(final EnumSet<Include> includes) {
|
||||
final var graph = new HashSet<RawRbacGrantEntity>();
|
||||
final var graph = new LimitedHashSet<RawRbacGrantEntity>();
|
||||
for ( UUID subjectUuid: context.currentSubjectsUuids() ) {
|
||||
traverseGrantsTo(graph, subjectUuid, includes);
|
||||
}
|
||||
@ -88,7 +94,7 @@ public class RbacGrantsDiagramService {
|
||||
.setParameter("targetObject", targetObject)
|
||||
.setParameter("op", op)
|
||||
.getSingleResult();
|
||||
final var graph = new HashSet<RawRbacGrantEntity>();
|
||||
final var graph = new LimitedHashSet<RawRbacGrantEntity>();
|
||||
traverseGrantsFrom(graph, refUuid, includes);
|
||||
return toMermaidFlowchart(graph, includes);
|
||||
}
|
||||
@ -116,7 +122,7 @@ public class RbacGrantsDiagramService {
|
||||
)
|
||||
.collect(groupingBy(RbacGrantsDiagramService::renderEntityIdName))
|
||||
.entrySet().stream()
|
||||
.map(entity -> "subgraph " + quoted(entity.getKey()) + renderSubgraph(entity.getKey()) + "\n\n "
|
||||
.map(entity -> "subgraph " + cleanId(entity.getKey()) + renderSubgraph(entity.getKey()) + "\n\n "
|
||||
+ entity.getValue().stream()
|
||||
.map(n -> renderNode(n.idName(), n.uuid()).replace("\n", "\n "))
|
||||
.sorted()
|
||||
@ -127,14 +133,15 @@ public class RbacGrantsDiagramService {
|
||||
: "";
|
||||
|
||||
final var grants = graph.stream()
|
||||
.map(g -> quoted(g.getAscendantIdName())
|
||||
.map(g -> cleanId(g.getAscendantIdName())
|
||||
+ " -->" + (g.isAssumed() ? " " : "|XX| ")
|
||||
+ quoted(g.getDescendantIdName()))
|
||||
+ cleanId(g.getDescendantIdName()))
|
||||
.sorted()
|
||||
.collect(joining("\n"));
|
||||
|
||||
final var avoidCroppedNodeLabels = "%%{init:{'flowchart':{'htmlLabels':false}}}%%\n\n";
|
||||
return (includes.contains(DETAILS) ? avoidCroppedNodeLabels : "")
|
||||
+ (graph.size() >= GRANT_LIMIT ? "%% too many grants, graph is cropped\n" : "")
|
||||
+ "flowchart TB\n\n"
|
||||
+ entities
|
||||
+ grants;
|
||||
@ -151,7 +158,7 @@ public class RbacGrantsDiagramService {
|
||||
// }
|
||||
// return "[" + table + "\n" + entity + "]";
|
||||
// }
|
||||
return "[" + entityId + "]";
|
||||
return "[" + cleanId(entityId) + "]";
|
||||
}
|
||||
|
||||
private static String renderEntityIdName(final Node node) {
|
||||
@ -170,7 +177,7 @@ public class RbacGrantsDiagramService {
|
||||
}
|
||||
|
||||
private String renderNode(final String idName, final UUID uuid) {
|
||||
return quoted(idName) + renderNodeContent(idName, uuid);
|
||||
return cleanId(idName) + renderNodeContent(idName, uuid);
|
||||
}
|
||||
|
||||
private String renderNodeContent(final String idName, final UUID uuid) {
|
||||
@ -196,9 +203,24 @@ public class RbacGrantsDiagramService {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String quoted(final String idName) {
|
||||
return idName.replace(" ", ":").replaceAll("@.*", "");
|
||||
private static String cleanId(final String idName) {
|
||||
return idName.replace(" ", ":").replaceAll("@.*", "")
|
||||
.replace("[", "").replace("]", "").replace("(", "").replace(")", "").replace(",", "");
|
||||
}
|
||||
|
||||
|
||||
class LimitedHashSet<T> extends HashSet<T> {
|
||||
|
||||
@Override
|
||||
public boolean add(final T t) {
|
||||
if (size() < GRANT_LIMIT ) {
|
||||
return super.add(t);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
record Node(String idName, UUID uuid) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
package net.hostsharing.hsadminng.rbac.rbacrole;
|
||||
|
||||
public enum RbacRoleType {
|
||||
owner, admin, agent, tenant, guest
|
||||
owner, admin, agent, tenant, guest, referrer
|
||||
}
|
||||
|
@ -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(INSERT)
|
||||
|
||||
.createRole(OWNER, (with) -> {
|
||||
with.owningUser(CREATOR).unassumed();
|
||||
|
@ -14,9 +14,10 @@ import java.io.IOException;
|
||||
import java.util.UUID;
|
||||
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL;
|
||||
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.SQL.fetchedBySql;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.directlyFetchedByDependsOnColumn;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
|
||||
|
||||
@Entity
|
||||
@ -49,11 +50,9 @@ public class TestDomainEntity implements HasUuid {
|
||||
|
||||
.importEntityAlias("package", TestPackageEntity.class,
|
||||
dependsOnColumn("packageUuid"),
|
||||
fetchedBySql("""
|
||||
SELECT * FROM test_package p
|
||||
WHERE p.uuid= ${ref}.packageUuid
|
||||
"""))
|
||||
.toRole("package", ADMIN).grantPermission("domain", INSERT)
|
||||
directlyFetchedByDependsOnColumn(),
|
||||
NOT_NULL)
|
||||
.toRole("package", ADMIN).grantPermission(INSERT)
|
||||
|
||||
.createRole(OWNER, (with) -> {
|
||||
with.incomingSuperRole("package", ADMIN);
|
||||
|
@ -14,6 +14,7 @@ import java.io.IOException;
|
||||
import java.util.UUID;
|
||||
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL;
|
||||
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.SQL.*;
|
||||
@ -50,11 +51,9 @@ public class TestPackageEntity implements HasUuid {
|
||||
|
||||
.importEntityAlias("customer", TestCustomerEntity.class,
|
||||
dependsOnColumn("customerUuid"),
|
||||
fetchedBySql("""
|
||||
SELECT * FROM test_customer c
|
||||
WHERE c.uuid= ${ref}.customerUuid
|
||||
"""))
|
||||
.toRole("customer", ADMIN).grantPermission("package", INSERT)
|
||||
directlyFetchedByDependsOnColumn(),
|
||||
NOT_NULL)
|
||||
.toRole("customer", ADMIN).grantPermission(INSERT)
|
||||
|
||||
.createRole(OWNER, (with) -> {
|
||||
with.incomingSuperRole("customer", ADMIN);
|
||||
|
20
src/main/resources/db/changelog/007-table-columns.sql
Normal file
20
src/main/resources/db/changelog/007-table-columns.sql
Normal file
@ -0,0 +1,20 @@
|
||||
--liquibase formatted sql
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
-- TABLE-COLUMNS-FUNCTION
|
||||
--changeset table-columns-function:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
create or replace function columnsNames( tableName text )
|
||||
returns text
|
||||
stable
|
||||
language 'plpgsql' as $$
|
||||
declare columns text[];
|
||||
begin
|
||||
columns := (select array(select column_name::text
|
||||
from information_schema.columns
|
||||
where table_name = tableName));
|
||||
return array_to_string(columns, ', ');
|
||||
end; $$
|
||||
--//
|
@ -160,6 +160,7 @@ create or replace function cleanIdentifier(rawIdentifier varchar)
|
||||
declare
|
||||
cleanIdentifier varchar;
|
||||
begin
|
||||
-- TODO: remove the ':' from the list of allowed characters as soon as it's not used anymore
|
||||
cleanIdentifier := regexp_replace(rawIdentifier, '[^A-Za-z0-9\-._:]+', '', 'g');
|
||||
return cleanIdentifier;
|
||||
end; $$;
|
||||
|
@ -164,7 +164,7 @@ end; $$;
|
||||
|
||||
*/
|
||||
|
||||
create type RbacRoleType as enum ('owner', 'admin', 'agent', 'tenant', 'guest');
|
||||
create type RbacRoleType as enum ('owner', 'admin', 'agent', 'tenant', 'guest', 'referrer');
|
||||
|
||||
create table RbacRole
|
||||
(
|
||||
@ -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)
|
||||
@ -395,7 +397,10 @@ begin
|
||||
raise exception 'forOpTableName must only be specified for ops: [INSERT]'; -- currently no other
|
||||
end if;
|
||||
|
||||
permissionUuid = (select uuid from RbacPermission where objectUuid = forObjectUuid and op = forOp and opTableName = forOpTableName);
|
||||
permissionUuid := (
|
||||
select uuid from RbacPermission
|
||||
where objectUuid = forObjectUuid
|
||||
and op = forOp and opTableName is not distinct from forOpTableName);
|
||||
if (permissionUuid is null) then
|
||||
insert into RbacReference ("type")
|
||||
values ('RbacPermission')
|
||||
@ -466,8 +471,44 @@ select uuid
|
||||
and p.op = forOp
|
||||
and p.opTableName = forOpTableName
|
||||
$$;
|
||||
|
||||
create or replace function getPermissionId(forObjectUuid uuid, forOp RbacOp, forOpTableName text = null)
|
||||
returns uuid
|
||||
stable -- leakproof
|
||||
language plpgsql as $$
|
||||
declare
|
||||
permissionUuid uuid;
|
||||
begin
|
||||
select uuid into permissionUuid
|
||||
from RbacPermission p
|
||||
where p.objectUuid = forObjectUuid
|
||||
and p.op = forOp
|
||||
and forOpTableName is null or p.opTableName = forOpTableName;
|
||||
assert permissionUuid is not null,
|
||||
format('permission %s %s for object UUID %s cannot be found', forOp, forOpTableName, forObjectUuid);
|
||||
return permissionUuid;
|
||||
end; $$;
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--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-GRANTS:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
@ -588,7 +629,7 @@ select exists(
|
||||
);
|
||||
$$;
|
||||
|
||||
create or replace procedure grantPermissionToRole(roleUuid uuid, permissionUuid uuid)
|
||||
create or replace procedure grantPermissionToRole(permissionUuid uuid, roleUuid uuid)
|
||||
language plpgsql as $$
|
||||
begin
|
||||
perform assertReferenceType('roleId (ascendant)', roleUuid, 'RbacRole');
|
||||
@ -601,10 +642,10 @@ begin
|
||||
end;
|
||||
$$;
|
||||
|
||||
create or replace procedure grantPermissionToRole(roleDesc RbacRoleDescriptor, permissionUuid uuid)
|
||||
create or replace procedure grantPermissionToRole(permissionUuid uuid, roleDesc RbacRoleDescriptor)
|
||||
language plpgsql as $$
|
||||
begin
|
||||
call grantPermissionToRole(findRoleId(roleDesc), permissionUuid);
|
||||
call grantPermissionToRole(permissionUuid, findRoleId(roleDesc));
|
||||
end;
|
||||
$$;
|
||||
|
||||
@ -634,7 +675,7 @@ begin
|
||||
perform assertReferenceType('subRoleId (descendant)', subRoleId, 'RbacRole');
|
||||
|
||||
if isGranted(subRoleId, superRoleId) then
|
||||
raise exception '[400] Cyclic role grant detected between % and %', subRoleId, superRoleId;
|
||||
call raiseDuplicateRoleGrantException(subRoleId, superRoleId);
|
||||
end if;
|
||||
|
||||
insert
|
||||
@ -650,6 +691,11 @@ declare
|
||||
superRoleId uuid;
|
||||
subRoleId uuid;
|
||||
begin
|
||||
-- TODO: maybe separate method grantRoleToRoleIfNotNull(...) for NULLABLE references
|
||||
if superRole.objectUuid is null or subRole.objectuuid is null then
|
||||
return;
|
||||
end if;
|
||||
|
||||
superRoleId := findRoleId(superRole);
|
||||
subRoleId := findRoleId(subRole);
|
||||
|
||||
@ -657,7 +703,7 @@ begin
|
||||
perform assertReferenceType('subRoleId (descendant)', subRoleId, 'RbacRole');
|
||||
|
||||
if isGranted(subRoleId, superRoleId) then
|
||||
raise exception '[400] Cyclic role grant detected between % and %', subRoleId, superRoleId;
|
||||
call raiseDuplicateRoleGrantException(subRoleId, superRoleId);
|
||||
end if;
|
||||
|
||||
insert
|
||||
@ -672,6 +718,7 @@ declare
|
||||
superRoleId uuid;
|
||||
subRoleId uuid;
|
||||
begin
|
||||
if ( superRoleId is null ) then return; end if;
|
||||
superRoleId := findRoleId(superRole);
|
||||
if ( subRoleId is null ) then return; end if;
|
||||
subRoleId := findRoleId(subRole);
|
||||
@ -680,7 +727,7 @@ begin
|
||||
perform assertReferenceType('subRoleId (descendant)', subRoleId, 'RbacRole');
|
||||
|
||||
if isGranted(subRoleId, superRoleId) then
|
||||
raise exception '[400] Cyclic role grant detected between % and %', subRoleId, superRoleId;
|
||||
call raiseDuplicateRoleGrantException(subRoleId, superRoleId);
|
||||
end if;
|
||||
|
||||
insert
|
||||
@ -704,11 +751,39 @@ begin
|
||||
if (isGranted(superRoleId, subRoleId)) then
|
||||
delete from RbacGrants where ascendantUuid = superRoleId and descendantUuid = subRoleId;
|
||||
else
|
||||
raise exception 'cannot revoke role % (%) from % (% because it is not granted',
|
||||
raise exception 'cannot revoke role % (%) from % (%) because it is not granted',
|
||||
subRole, subRoleId, superRole, superRoleId;
|
||||
end if;
|
||||
end; $$;
|
||||
|
||||
create or replace procedure revokePermissionFromRole(permissionId UUID, superRole RbacRoleDescriptor)
|
||||
language plpgsql as $$
|
||||
declare
|
||||
superRoleId uuid;
|
||||
permissionOp text;
|
||||
objectTable text;
|
||||
objectUuid uuid;
|
||||
begin
|
||||
superRoleId := findRoleId(superRole);
|
||||
|
||||
perform assertReferenceType('superRoleId (ascendant)', superRoleId, 'RbacRole');
|
||||
perform assertReferenceType('permission (descendant)', permissionId, 'RbacPermission');
|
||||
|
||||
if (isGranted(superRoleId, permissionId)) then
|
||||
delete from RbacGrants where ascendantUuid = superRoleId and descendantUuid = permissionId;
|
||||
else
|
||||
select p.op, o.objectTable, o.uuid
|
||||
from rbacGrants g
|
||||
join rbacPermission p on p.uuid=g.descendantUuid
|
||||
join rbacobject o on o.uuid=p.objectUuid
|
||||
where g.uuid=permissionId
|
||||
into permissionOp, objectTable, objectUuid;
|
||||
|
||||
raise exception 'cannot revoke permission % (% on %#% (%) from % (%)) because it is not granted',
|
||||
permissionId, permissionOp, objectTable, objectUuid, permissionId, superRole, superRoleId;
|
||||
end if;
|
||||
end; $$;
|
||||
|
||||
-- ============================================================================
|
||||
--changeset rbac-base-QUERY-ACCESSIBLE-OBJECT-UUIDS:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
@ -56,14 +56,17 @@ begin
|
||||
roleTypeToAssume = split_part(roleNameParts, '#', 3);
|
||||
|
||||
objectUuidToAssume = findObjectUuidByIdName(objectTableToAssume, objectNameToAssume);
|
||||
if objectUuidToAssume is null then
|
||||
raise exception '[401] object % cannot be found in table %', objectNameToAssume, objectTableToAssume;
|
||||
end if;
|
||||
|
||||
select uuid as roleuuidToAssume
|
||||
select uuid
|
||||
from RbacRole r
|
||||
where r.objectUuid = objectUuidToAssume
|
||||
and r.roleType = roleTypeToAssume
|
||||
into roleUuidToAssume;
|
||||
if roleUuidToAssume is null then
|
||||
raise exception '[403] role % not accessible for user %', roleName, currentSubjects();
|
||||
raise exception '[403] role % does not exist or is not accessible for user %', roleName, currentUser();
|
||||
end if;
|
||||
if not isGranted(currentUserUuid, roleUuidToAssume) then
|
||||
raise exception '[403] user % has no permission to assume role %', currentUser(), roleName;
|
||||
|
@ -31,13 +31,13 @@ create or replace function createRoleWithGrants(
|
||||
called on null input
|
||||
language plpgsql as $$
|
||||
declare
|
||||
roleUuid uuid;
|
||||
subRoleDesc RbacRoleDescriptor;
|
||||
superRoleDesc RbacRoleDescriptor;
|
||||
subRoleUuid uuid;
|
||||
superRoleUuid uuid;
|
||||
userUuid uuid;
|
||||
grantedByRoleUuid uuid;
|
||||
roleUuid uuid;
|
||||
subRoleDesc RbacRoleDescriptor;
|
||||
superRoleDesc RbacRoleDescriptor;
|
||||
subRoleUuid uuid;
|
||||
superRoleUuid uuid;
|
||||
userUuid uuid;
|
||||
userGrantsByRoleUuid uuid;
|
||||
begin
|
||||
roleUuid := createRole(roleDescriptor);
|
||||
|
||||
@ -58,14 +58,15 @@ begin
|
||||
end loop;
|
||||
|
||||
if cardinality(userUuids) > 0 then
|
||||
-- direct grants to users need a grantedByRole which can revoke the grant
|
||||
if grantedByRole is null then
|
||||
grantedByRoleUuid := roleUuid;
|
||||
userGrantsByRoleUuid := roleUuid; -- TODO: or do we want to require an explicit userGrantsByRoleUuid?
|
||||
else
|
||||
grantedByRoleUuid := getRoleId(grantedByRole);
|
||||
userGrantsByRoleUuid := getRoleId(grantedByRole);
|
||||
end if;
|
||||
foreach userUuid in array userUuids
|
||||
loop
|
||||
call grantRoleToUserUnchecked(grantedByRoleUuid, roleUuid, userUuid);
|
||||
call grantRoleToUserUnchecked(userGrantsByRoleUuid, roleUuid, userUuid);
|
||||
end loop;
|
||||
end if;
|
||||
|
||||
|
@ -73,6 +73,7 @@ begin
|
||||
return roleDescriptor('%2$s', entity.uuid, 'tenant', assumed);
|
||||
end; $f$;
|
||||
|
||||
-- TODO: remove guest role
|
||||
create or replace function %1$sGuest(entity %2$s, assumed boolean = true)
|
||||
returns RbacRoleDescriptor
|
||||
language plpgsql
|
||||
@ -81,6 +82,14 @@ begin
|
||||
return roleDescriptor('%2$s', entity.uuid, 'guest', assumed);
|
||||
end; $f$;
|
||||
|
||||
create or replace function %1$sReferrer(entity %2$s)
|
||||
returns RbacRoleDescriptor
|
||||
language plpgsql
|
||||
strict as $f$
|
||||
begin
|
||||
return roleDescriptor('%2$s', entity.uuid, 'referrer');
|
||||
end; $f$;
|
||||
|
||||
$sql$, prefix, targetTable);
|
||||
execute sql;
|
||||
end; $$;
|
||||
@ -148,12 +157,16 @@ end; $$;
|
||||
--changeset rbac-generators-RESTRICTED-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
create or replace procedure generateRbacRestrictedView(targetTable text, orderBy text, columnUpdates text = null)
|
||||
create or replace procedure generateRbacRestrictedView(targetTable text, orderBy text, columnUpdates text = null, columnNames text = '*')
|
||||
language plpgsql as $$
|
||||
declare
|
||||
sql text;
|
||||
newColumns text;
|
||||
begin
|
||||
targetTable := lower(targetTable);
|
||||
if columnNames = '*' then
|
||||
columnNames := columnsNames(targetTable);
|
||||
end if;
|
||||
|
||||
/*
|
||||
Creates a restricted view based on the 'SELECT' permission of the current subject.
|
||||
@ -175,20 +188,21 @@ begin
|
||||
/**
|
||||
Instead of insert trigger function for the restricted view.
|
||||
*/
|
||||
newColumns := 'new.' || replace(columnNames, ',', ', new.');
|
||||
sql := format($sql$
|
||||
create or replace function %1$sInsert()
|
||||
returns trigger
|
||||
language plpgsql as $f$
|
||||
declare
|
||||
newTargetRow %1$s;
|
||||
begin
|
||||
insert
|
||||
into %1$s
|
||||
values (new.*)
|
||||
returning * into newTargetRow;
|
||||
return newTargetRow;
|
||||
end; $f$;
|
||||
$sql$, targetTable);
|
||||
create or replace function %1$sInsert()
|
||||
returns trigger
|
||||
language plpgsql as $f$
|
||||
declare
|
||||
newTargetRow %1$s;
|
||||
begin
|
||||
insert
|
||||
into %1$s (%2$s)
|
||||
values (%3$s)
|
||||
returning * into newTargetRow;
|
||||
return newTargetRow;
|
||||
end; $f$;
|
||||
$sql$, targetTable, columnNames, newColumns);
|
||||
execute sql;
|
||||
|
||||
/*
|
||||
|
@ -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:--//
|
||||
|
@ -1,6 +1,6 @@
|
||||
### rbac customer
|
||||
|
||||
This code generated was by RbacViewMermaidFlowchartGenerator at 2024-03-11T11:29:11.571772062.
|
||||
This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually.
|
||||
|
||||
```mermaid
|
||||
%%{init:{'flowchart':{'htmlLabels':false}}}%%
|
||||
@ -21,6 +21,7 @@ subgraph customer["`**customer**`"]
|
||||
subgraph customer:permissions[ ]
|
||||
style customer:permissions fill:#dd4901,stroke:white
|
||||
|
||||
perm:customer:INSERT{{customer:INSERT}}
|
||||
perm:customer:DELETE{{customer:DELETE}}
|
||||
perm:customer:UPDATE{{customer:UPDATE}}
|
||||
perm:customer:SELECT{{customer:SELECT}}
|
||||
@ -36,6 +37,7 @@ role:customer:owner ==> role:customer:admin
|
||||
role:customer:admin ==> role:customer:tenant
|
||||
|
||||
%% granting permissions to roles
|
||||
role:global:admin ==> perm:customer:INSERT
|
||||
role:customer:owner ==> perm:customer:DELETE
|
||||
role:customer:admin ==> perm:customer:UPDATE
|
||||
role:customer:tenant ==> perm:customer:SELECT
|
||||
|
@ -1,5 +1,6 @@
|
||||
--liquibase formatted sql
|
||||
-- This code generated was by RbacViewPostgresGenerator at 2024-03-11T11:29:11.584886824.
|
||||
-- This code generated was by RbacViewPostgresGenerator, do not amend manually.
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset test-customer-rbac-OBJECT:1 endDelimiter:--//
|
||||
@ -36,8 +37,8 @@ begin
|
||||
perform createRoleWithGrants(
|
||||
testCustomerOwner(NEW),
|
||||
permissions => array['DELETE'],
|
||||
userUuids => array[currentUserUuid()],
|
||||
incomingSuperRoles => array[globalAdmin(unassumed())]
|
||||
incomingSuperRoles => array[globalAdmin(unassumed())],
|
||||
userUuids => array[currentUserUuid()]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
@ -72,15 +73,56 @@ create trigger insertTriggerForTestCustomer_tg
|
||||
after insert on test_customer
|
||||
for each row
|
||||
execute procedure insertTriggerForTestCustomer_tf();
|
||||
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset test-customer-rbac-INSERT:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
Creates INSERT INTO test_customer permissions for the related global rows.
|
||||
*/
|
||||
do language plpgsql $$
|
||||
declare
|
||||
row global;
|
||||
permissionUuid uuid;
|
||||
roleUuid uuid;
|
||||
begin
|
||||
call defineContext('create INSERT INTO test_customer permissions for the related global rows');
|
||||
|
||||
FOR row IN SELECT * FROM global
|
||||
LOOP
|
||||
roleUuid := findRoleId(globalAdmin());
|
||||
permissionUuid := createPermission(row.uuid, 'INSERT', 'test_customer');
|
||||
call grantPermissionToRole(permissionUuid, roleUuid);
|
||||
END LOOP;
|
||||
END;
|
||||
$$;
|
||||
|
||||
/**
|
||||
Checks if the user or assumed roles are allowed to insert a row to test_customer.
|
||||
Adds test_customer INSERT permission to specified role of new global rows.
|
||||
*/
|
||||
create or replace function test_customer_global_insert_tf()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
strict as $$
|
||||
begin
|
||||
call grantPermissionToRole(
|
||||
createPermission(NEW.uuid, 'INSERT', 'test_customer'),
|
||||
globalAdmin());
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
-- z_... is to put it at the end of after insert triggers, to make sure the roles exist
|
||||
create trigger z_test_customer_global_insert_tg
|
||||
after insert on global
|
||||
for each row
|
||||
execute procedure test_customer_global_insert_tf();
|
||||
|
||||
/**
|
||||
Checks if the user or assumed roles are allowed to insert a row to test_customer,
|
||||
where only global-admin has that permission.
|
||||
*/
|
||||
create or replace function test_customer_insert_permission_missing_tf()
|
||||
returns trigger
|
||||
@ -93,26 +135,27 @@ end; $$;
|
||||
create trigger test_customer_insert_permission_check_tg
|
||||
before insert on test_customer
|
||||
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 test_customer_insert_permission_missing_tf();
|
||||
|
||||
--//
|
||||
|
||||
-- ============================================================================
|
||||
--changeset test-customer-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
call generateRbacIdentityViewFromProjection('test_customer', $idName$
|
||||
prefix
|
||||
call generateRbacIdentityViewFromProjection('test_customer',
|
||||
$idName$
|
||||
prefix
|
||||
$idName$);
|
||||
|
||||
--//
|
||||
|
||||
-- ============================================================================
|
||||
--changeset test-customer-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacRestrictedView('test_customer',
|
||||
'reference',
|
||||
$orderBy$
|
||||
reference
|
||||
$orderBy$,
|
||||
$updates$
|
||||
reference = new.reference,
|
||||
prefix = new.prefix,
|
||||
@ -120,4 +163,3 @@ call generateRbacRestrictedView('test_customer',
|
||||
$updates$);
|
||||
--//
|
||||
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
### rbac package
|
||||
|
||||
This code generated was by RbacViewMermaidFlowchartGenerator at 2024-03-11T11:29:11.624847792.
|
||||
This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually.
|
||||
|
||||
```mermaid
|
||||
%%{init:{'flowchart':{'htmlLabels':false}}}%%
|
||||
|
@ -1,5 +1,6 @@
|
||||
--liquibase formatted sql
|
||||
-- This code generated was by RbacViewPostgresGenerator at 2024-03-11T11:29:11.625353859.
|
||||
-- This code generated was by RbacViewPostgresGenerator, do not amend manually.
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset test-package-rbac-OBJECT:1 endDelimiter:--//
|
||||
@ -33,9 +34,10 @@ declare
|
||||
|
||||
begin
|
||||
call enterTriggerForObjectUuid(NEW.uuid);
|
||||
SELECT * FROM test_customer c
|
||||
WHERE c.uuid= NEW.customerUuid
|
||||
into newCustomer;
|
||||
|
||||
SELECT * FROM test_customer WHERE uuid = NEW.customerUuid INTO newCustomer;
|
||||
assert newCustomer.uuid is not null, format('newCustomer must not be null for NEW.customerUuid = %s', NEW.customerUuid);
|
||||
|
||||
|
||||
perform createRoleWithGrants(
|
||||
testPackageOwner(NEW),
|
||||
@ -75,9 +77,9 @@ create trigger insertTriggerForTestPackage_tg
|
||||
after insert on test_package
|
||||
for each row
|
||||
execute procedure insertTriggerForTestPackage_tf();
|
||||
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset test-package-rbac-update-trigger:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
@ -99,17 +101,15 @@ declare
|
||||
begin
|
||||
call enterTriggerForObjectUuid(NEW.uuid);
|
||||
|
||||
SELECT * FROM test_customer c
|
||||
WHERE c.uuid= OLD.customerUuid
|
||||
into oldCustomer;
|
||||
SELECT * FROM test_customer c
|
||||
WHERE c.uuid= NEW.customerUuid
|
||||
into newCustomer;
|
||||
SELECT * FROM test_customer WHERE uuid = OLD.customerUuid INTO oldCustomer;
|
||||
assert oldCustomer.uuid is not null, format('oldCustomer must not be null for OLD.customerUuid = %s', OLD.customerUuid);
|
||||
|
||||
SELECT * FROM test_customer WHERE uuid = NEW.customerUuid INTO newCustomer;
|
||||
assert newCustomer.uuid is not null, format('newCustomer must not be null for NEW.customerUuid = %s', NEW.customerUuid);
|
||||
|
||||
|
||||
if NEW.customerUuid <> OLD.customerUuid then
|
||||
|
||||
call revokePermissionFromRole(findPermissionId(OLD.uuid, 'INSERT'), testCustomerAdmin(oldCustomer));
|
||||
|
||||
call revokeRoleFromRole(testPackageOwner(OLD), testCustomerAdmin(oldCustomer));
|
||||
call grantRoleToRole(testPackageOwner(NEW), testCustomerAdmin(newCustomer));
|
||||
|
||||
@ -138,9 +138,9 @@ create trigger updateTriggerForTestPackage_tg
|
||||
after update on test_package
|
||||
for each row
|
||||
execute procedure updateTriggerForTestPackage_tf();
|
||||
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset test-package-rbac-INSERT:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
@ -160,7 +160,7 @@ do language plpgsql $$
|
||||
LOOP
|
||||
roleUuid := findRoleId(testCustomerAdmin(row));
|
||||
permissionUuid := createPermission(row.uuid, 'INSERT', 'test_package');
|
||||
call grantPermissionToRole(roleUuid, permissionUuid);
|
||||
call grantPermissionToRole(permissionUuid, roleUuid);
|
||||
END LOOP;
|
||||
END;
|
||||
$$;
|
||||
@ -174,18 +174,22 @@ create or replace function test_package_test_customer_insert_tf()
|
||||
strict as $$
|
||||
begin
|
||||
call grantPermissionToRole(
|
||||
testCustomerAdmin(NEW),
|
||||
createPermission(NEW.uuid, 'INSERT', 'test_package'));
|
||||
createPermission(NEW.uuid, 'INSERT', 'test_package'),
|
||||
testCustomerAdmin(NEW));
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
create trigger test_package_test_customer_insert_tg
|
||||
-- z_... is to put it at the end of after insert triggers, to make sure the roles exist
|
||||
create trigger z_test_package_test_customer_insert_tg
|
||||
after insert on test_customer
|
||||
for each row
|
||||
execute procedure test_package_test_customer_insert_tf();
|
||||
|
||||
/**
|
||||
Checks if the user or assumed roles are allowed to insert a row to test_package.
|
||||
Checks if the user or assumed roles are allowed to insert a row to test_package,
|
||||
where the check is performed by a direct role.
|
||||
|
||||
A direct role is a role depending on a foreign key directly available in the NEW row.
|
||||
*/
|
||||
create or replace function test_package_insert_permission_missing_tf()
|
||||
returns trigger
|
||||
@ -200,22 +204,25 @@ create trigger test_package_insert_permission_check_tg
|
||||
for each row
|
||||
when ( not hasInsertPermission(NEW.customerUuid, 'INSERT', 'test_package') )
|
||||
execute procedure test_package_insert_permission_missing_tf();
|
||||
|
||||
--//
|
||||
|
||||
-- ============================================================================
|
||||
--changeset test-package-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
call generateRbacIdentityViewFromProjection('test_package', $idName$
|
||||
name
|
||||
call generateRbacIdentityViewFromProjection('test_package',
|
||||
$idName$
|
||||
name
|
||||
$idName$);
|
||||
|
||||
--//
|
||||
|
||||
-- ============================================================================
|
||||
--changeset test-package-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacRestrictedView('test_package',
|
||||
'name',
|
||||
$orderBy$
|
||||
name
|
||||
$orderBy$,
|
||||
$updates$
|
||||
version = new.version,
|
||||
customerUuid = new.customerUuid,
|
||||
@ -223,4 +230,3 @@ call generateRbacRestrictedView('test_package',
|
||||
$updates$);
|
||||
--//
|
||||
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
### rbac domain
|
||||
|
||||
This code generated was by RbacViewMermaidFlowchartGenerator at 2024-03-11T11:29:11.644658132.
|
||||
This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually.
|
||||
|
||||
```mermaid
|
||||
%%{init:{'flowchart':{'htmlLabels':false}}}%%
|
||||
|
@ -1,5 +1,6 @@
|
||||
--liquibase formatted sql
|
||||
-- This code generated was by RbacViewPostgresGenerator at 2024-03-11T11:29:11.645391647.
|
||||
-- This code generated was by RbacViewPostgresGenerator, do not amend manually.
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset test-domain-rbac-OBJECT:1 endDelimiter:--//
|
||||
@ -33,9 +34,10 @@ declare
|
||||
|
||||
begin
|
||||
call enterTriggerForObjectUuid(NEW.uuid);
|
||||
SELECT * FROM test_package p
|
||||
WHERE p.uuid= NEW.packageUuid
|
||||
into newPackage;
|
||||
|
||||
SELECT * FROM test_package WHERE uuid = NEW.packageUuid INTO newPackage;
|
||||
assert newPackage.uuid is not null, format('newPackage must not be null for NEW.packageUuid = %s', NEW.packageUuid);
|
||||
|
||||
|
||||
perform createRoleWithGrants(
|
||||
testDomainOwner(NEW),
|
||||
@ -71,9 +73,9 @@ create trigger insertTriggerForTestDomain_tg
|
||||
after insert on test_domain
|
||||
for each row
|
||||
execute procedure insertTriggerForTestDomain_tf();
|
||||
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset test-domain-rbac-update-trigger:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
@ -95,17 +97,15 @@ declare
|
||||
begin
|
||||
call enterTriggerForObjectUuid(NEW.uuid);
|
||||
|
||||
SELECT * FROM test_package p
|
||||
WHERE p.uuid= OLD.packageUuid
|
||||
into oldPackage;
|
||||
SELECT * FROM test_package p
|
||||
WHERE p.uuid= NEW.packageUuid
|
||||
into newPackage;
|
||||
SELECT * FROM test_package WHERE uuid = OLD.packageUuid INTO oldPackage;
|
||||
assert oldPackage.uuid is not null, format('oldPackage must not be null for OLD.packageUuid = %s', OLD.packageUuid);
|
||||
|
||||
SELECT * FROM test_package WHERE uuid = NEW.packageUuid INTO newPackage;
|
||||
assert newPackage.uuid is not null, format('newPackage must not be null for NEW.packageUuid = %s', NEW.packageUuid);
|
||||
|
||||
|
||||
if NEW.packageUuid <> OLD.packageUuid then
|
||||
|
||||
call revokePermissionFromRole(findPermissionId(OLD.uuid, 'INSERT'), testPackageAdmin(oldPackage));
|
||||
|
||||
call revokeRoleFromRole(testDomainOwner(OLD), testPackageAdmin(oldPackage));
|
||||
call grantRoleToRole(testDomainOwner(NEW), testPackageAdmin(newPackage));
|
||||
|
||||
@ -137,9 +137,9 @@ create trigger updateTriggerForTestDomain_tg
|
||||
after update on test_domain
|
||||
for each row
|
||||
execute procedure updateTriggerForTestDomain_tf();
|
||||
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset test-domain-rbac-INSERT:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
@ -159,7 +159,7 @@ do language plpgsql $$
|
||||
LOOP
|
||||
roleUuid := findRoleId(testPackageAdmin(row));
|
||||
permissionUuid := createPermission(row.uuid, 'INSERT', 'test_domain');
|
||||
call grantPermissionToRole(roleUuid, permissionUuid);
|
||||
call grantPermissionToRole(permissionUuid, roleUuid);
|
||||
END LOOP;
|
||||
END;
|
||||
$$;
|
||||
@ -173,18 +173,22 @@ create or replace function test_domain_test_package_insert_tf()
|
||||
strict as $$
|
||||
begin
|
||||
call grantPermissionToRole(
|
||||
testPackageAdmin(NEW),
|
||||
createPermission(NEW.uuid, 'INSERT', 'test_domain'));
|
||||
createPermission(NEW.uuid, 'INSERT', 'test_domain'),
|
||||
testPackageAdmin(NEW));
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
create trigger test_domain_test_package_insert_tg
|
||||
-- z_... is to put it at the end of after insert triggers, to make sure the roles exist
|
||||
create trigger z_test_domain_test_package_insert_tg
|
||||
after insert on test_package
|
||||
for each row
|
||||
execute procedure test_domain_test_package_insert_tf();
|
||||
|
||||
/**
|
||||
Checks if the user or assumed roles are allowed to insert a row to test_domain.
|
||||
Checks if the user or assumed roles are allowed to insert a row to test_domain,
|
||||
where the check is performed by a direct role.
|
||||
|
||||
A direct role is a role depending on a foreign key directly available in the NEW row.
|
||||
*/
|
||||
create or replace function test_domain_insert_permission_missing_tf()
|
||||
returns trigger
|
||||
@ -199,22 +203,25 @@ create trigger test_domain_insert_permission_check_tg
|
||||
for each row
|
||||
when ( not hasInsertPermission(NEW.packageUuid, 'INSERT', 'test_domain') )
|
||||
execute procedure test_domain_insert_permission_missing_tf();
|
||||
|
||||
--//
|
||||
|
||||
-- ============================================================================
|
||||
--changeset test-domain-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
call generateRbacIdentityViewFromProjection('test_domain', $idName$
|
||||
name
|
||||
call generateRbacIdentityViewFromProjection('test_domain',
|
||||
$idName$
|
||||
name
|
||||
$idName$);
|
||||
|
||||
--//
|
||||
|
||||
-- ============================================================================
|
||||
--changeset test-domain-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacRestrictedView('test_domain',
|
||||
'name',
|
||||
$orderBy$
|
||||
name
|
||||
$orderBy$,
|
||||
$updates$
|
||||
version = new.version,
|
||||
packageUuid = new.packageUuid,
|
||||
@ -222,4 +229,3 @@ call generateRbacRestrictedView('test_domain',
|
||||
$updates$);
|
||||
--//
|
||||
|
||||
|
||||
|
@ -0,0 +1,43 @@
|
||||
### rbac contact
|
||||
|
||||
This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually.
|
||||
|
||||
```mermaid
|
||||
%%{init:{'flowchart':{'htmlLabels':false}}}%%
|
||||
flowchart TB
|
||||
|
||||
subgraph contact["`**contact**`"]
|
||||
direction TB
|
||||
style contact fill:#dd4901,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph contact:roles[ ]
|
||||
style contact:roles fill:#dd4901,stroke:white
|
||||
|
||||
role:contact:owner[[contact:owner]]
|
||||
role:contact:admin[[contact:admin]]
|
||||
role:contact:referrer[[contact:referrer]]
|
||||
end
|
||||
|
||||
subgraph contact:permissions[ ]
|
||||
style contact:permissions fill:#dd4901,stroke:white
|
||||
|
||||
perm:contact:DELETE{{contact:DELETE}}
|
||||
perm:contact:UPDATE{{contact:UPDATE}}
|
||||
perm:contact:SELECT{{contact:SELECT}}
|
||||
end
|
||||
end
|
||||
|
||||
%% granting roles to users
|
||||
user:creator ==> role:contact:owner
|
||||
|
||||
%% granting roles to roles
|
||||
role:global:admin ==> role:contact:owner
|
||||
role:contact:owner ==> role:contact:admin
|
||||
role:contact:admin ==> role:contact:referrer
|
||||
|
||||
%% granting permissions to roles
|
||||
role:contact:owner ==> perm:contact:DELETE
|
||||
role:contact:admin ==> perm:contact:UPDATE
|
||||
role:contact:referrer ==> perm:contact:SELECT
|
||||
|
||||
```
|
@ -0,0 +1,126 @@
|
||||
--liquibase formatted sql
|
||||
-- This code generated was by RbacViewPostgresGenerator, do not amend manually.
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-contact-rbac-OBJECT:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRelatedRbacObject('hs_office_contact');
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-contact-rbac-ROLE-DESCRIPTORS:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacRoleDescriptors('hsOfficeContact', 'hs_office_contact');
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-contact-rbac-insert-trigger:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
Creates the roles, grants and permission for the AFTER INSERT TRIGGER.
|
||||
*/
|
||||
|
||||
create or replace procedure buildRbacSystemForHsOfficeContact(
|
||||
NEW hs_office_contact
|
||||
)
|
||||
language plpgsql as $$
|
||||
|
||||
declare
|
||||
|
||||
begin
|
||||
call enterTriggerForObjectUuid(NEW.uuid);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeContactOwner(NEW),
|
||||
permissions => array['DELETE'],
|
||||
incomingSuperRoles => array[globalAdmin()],
|
||||
userUuids => array[currentUserUuid()]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeContactAdmin(NEW),
|
||||
permissions => array['UPDATE'],
|
||||
incomingSuperRoles => array[hsOfficeContactOwner(NEW)]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeContactReferrer(NEW),
|
||||
permissions => array['SELECT'],
|
||||
incomingSuperRoles => array[hsOfficeContactAdmin(NEW)]
|
||||
);
|
||||
|
||||
call leaveTriggerForObjectUuid(NEW.uuid);
|
||||
end; $$;
|
||||
|
||||
/*
|
||||
AFTER INSERT TRIGGER to create the role+grant structure for a new hs_office_contact row.
|
||||
*/
|
||||
|
||||
create or replace function insertTriggerForHsOfficeContact_tf()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
strict as $$
|
||||
begin
|
||||
call buildRbacSystemForHsOfficeContact(NEW);
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
create trigger insertTriggerForHsOfficeContact_tg
|
||||
after insert on hs_office_contact
|
||||
for each row
|
||||
execute procedure insertTriggerForHsOfficeContact_tf();
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-contact-rbac-INSERT:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
Checks if the user or assumed roles are allowed to insert a row to hs_office_contact,
|
||||
where only global-admin has that permission.
|
||||
*/
|
||||
create or replace function hs_office_contact_insert_permission_missing_tf()
|
||||
returns trigger
|
||||
language plpgsql as $$
|
||||
begin
|
||||
raise exception '[403] insert into hs_office_contact not allowed for current subjects % (%)',
|
||||
currentSubjects(), currentSubjectsUuids();
|
||||
end; $$;
|
||||
|
||||
create trigger hs_office_contact_insert_permission_check_tg
|
||||
before insert on hs_office_contact
|
||||
for each row
|
||||
when ( not isGlobalAdmin() )
|
||||
execute procedure hs_office_contact_insert_permission_missing_tf();
|
||||
--//
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-contact-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
call generateRbacIdentityViewFromProjection('hs_office_contact',
|
||||
$idName$
|
||||
label
|
||||
$idName$);
|
||||
--//
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-contact-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacRestrictedView('hs_office_contact',
|
||||
$orderBy$
|
||||
label
|
||||
$orderBy$,
|
||||
$updates$
|
||||
label = new.label,
|
||||
postalAddress = new.postalAddress,
|
||||
emailAddresses = new.emailAddresses,
|
||||
phoneNumbers = new.phoneNumbers
|
||||
$updates$);
|
||||
--//
|
||||
|
@ -0,0 +1,43 @@
|
||||
### rbac person
|
||||
|
||||
This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually.
|
||||
|
||||
```mermaid
|
||||
%%{init:{'flowchart':{'htmlLabels':false}}}%%
|
||||
flowchart TB
|
||||
|
||||
subgraph person["`**person**`"]
|
||||
direction TB
|
||||
style person fill:#dd4901,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph person:roles[ ]
|
||||
style person:roles fill:#dd4901,stroke:white
|
||||
|
||||
role:person:owner[[person:owner]]
|
||||
role:person:admin[[person:admin]]
|
||||
role:person:referrer[[person:referrer]]
|
||||
end
|
||||
|
||||
subgraph person:permissions[ ]
|
||||
style person:permissions fill:#dd4901,stroke:white
|
||||
|
||||
perm:person:DELETE{{person:DELETE}}
|
||||
perm:person:UPDATE{{person:UPDATE}}
|
||||
perm:person:SELECT{{person:SELECT}}
|
||||
end
|
||||
end
|
||||
|
||||
%% granting roles to users
|
||||
user:creator ==> role:person:owner
|
||||
|
||||
%% granting roles to roles
|
||||
role:global:admin ==> role:person:owner
|
||||
role:person:owner ==> role:person:admin
|
||||
role:person:admin ==> role:person:referrer
|
||||
|
||||
%% granting permissions to roles
|
||||
role:person:owner ==> perm:person:DELETE
|
||||
role:person:admin ==> perm:person:UPDATE
|
||||
role:person:referrer ==> perm:person:SELECT
|
||||
|
||||
```
|
@ -0,0 +1,126 @@
|
||||
--liquibase formatted sql
|
||||
-- This code generated was by RbacViewPostgresGenerator, do not amend manually.
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-person-rbac-OBJECT:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRelatedRbacObject('hs_office_person');
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-person-rbac-ROLE-DESCRIPTORS:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacRoleDescriptors('hsOfficePerson', 'hs_office_person');
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-person-rbac-insert-trigger:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
Creates the roles, grants and permission for the AFTER INSERT TRIGGER.
|
||||
*/
|
||||
|
||||
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'],
|
||||
incomingSuperRoles => array[globalAdmin()],
|
||||
userUuids => array[currentUserUuid()]
|
||||
);
|
||||
|
||||
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
|
||||
call buildRbacSystemForHsOfficePerson(NEW);
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
create trigger insertTriggerForHsOfficePerson_tg
|
||||
after insert on hs_office_person
|
||||
for each row
|
||||
execute procedure insertTriggerForHsOfficePerson_tf();
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-person-rbac-INSERT:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
Checks if the user or assumed roles are allowed to insert a row to hs_office_person,
|
||||
where only global-admin has that permission.
|
||||
*/
|
||||
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; $$;
|
||||
|
||||
create trigger hs_office_person_insert_permission_check_tg
|
||||
before insert on hs_office_person
|
||||
for each row
|
||||
when ( not isGlobalAdmin() )
|
||||
execute procedure hs_office_person_insert_permission_missing_tf();
|
||||
--//
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-person-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
call generateRbacIdentityViewFromProjection('hs_office_person',
|
||||
$idName$
|
||||
concat(tradeName, familyName, givenName)
|
||||
$idName$);
|
||||
--//
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-person-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacRestrictedView('hs_office_person',
|
||||
$orderBy$
|
||||
concat(tradeName, familyName, givenName)
|
||||
$orderBy$,
|
||||
$updates$
|
||||
personType = new.personType,
|
||||
tradeName = new.tradeName,
|
||||
givenName = new.givenName,
|
||||
familyName = new.familyName
|
||||
$updates$);
|
||||
--//
|
||||
|
@ -0,0 +1,100 @@
|
||||
### rbac relation
|
||||
|
||||
This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually.
|
||||
|
||||
```mermaid
|
||||
%%{init:{'flowchart':{'htmlLabels':false}}}%%
|
||||
flowchart TB
|
||||
|
||||
subgraph holderPerson["`**holderPerson**`"]
|
||||
direction TB
|
||||
style holderPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph holderPerson:roles[ ]
|
||||
style holderPerson:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:holderPerson:owner[[holderPerson:owner]]
|
||||
role:holderPerson:admin[[holderPerson:admin]]
|
||||
role:holderPerson:referrer[[holderPerson:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph anchorPerson["`**anchorPerson**`"]
|
||||
direction TB
|
||||
style anchorPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph anchorPerson:roles[ ]
|
||||
style anchorPerson:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:anchorPerson:owner[[anchorPerson:owner]]
|
||||
role:anchorPerson:admin[[anchorPerson:admin]]
|
||||
role:anchorPerson:referrer[[anchorPerson:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph contact["`**contact**`"]
|
||||
direction TB
|
||||
style contact fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph contact:roles[ ]
|
||||
style contact:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:contact:owner[[contact:owner]]
|
||||
role:contact:admin[[contact:admin]]
|
||||
role:contact:referrer[[contact:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph relation["`**relation**`"]
|
||||
direction TB
|
||||
style relation fill:#dd4901,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph relation:roles[ ]
|
||||
style relation:roles fill:#dd4901,stroke:white
|
||||
|
||||
role:relation:owner[[relation:owner]]
|
||||
role:relation:admin[[relation:admin]]
|
||||
role:relation:agent[[relation:agent]]
|
||||
role:relation:tenant[[relation:tenant]]
|
||||
end
|
||||
|
||||
subgraph relation:permissions[ ]
|
||||
style relation:permissions fill:#dd4901,stroke:white
|
||||
|
||||
perm:relation:DELETE{{relation:DELETE}}
|
||||
perm:relation:UPDATE{{relation:UPDATE}}
|
||||
perm:relation:SELECT{{relation:SELECT}}
|
||||
end
|
||||
end
|
||||
|
||||
%% granting roles to users
|
||||
user:creator ==> role:relation:owner
|
||||
|
||||
%% granting roles to roles
|
||||
role:global:admin -.-> role:anchorPerson:owner
|
||||
role:anchorPerson:owner -.-> role:anchorPerson:admin
|
||||
role:anchorPerson:admin -.-> role:anchorPerson:referrer
|
||||
role:global:admin -.-> role:holderPerson:owner
|
||||
role:holderPerson:owner -.-> role:holderPerson:admin
|
||||
role:holderPerson:admin -.-> role:holderPerson:referrer
|
||||
role:global:admin -.-> role:contact:owner
|
||||
role:contact:owner -.-> role:contact:admin
|
||||
role:contact:admin -.-> role:contact:referrer
|
||||
role:global:admin ==> role:relation:owner
|
||||
role:relation:owner ==> role:relation:admin
|
||||
role:anchorPerson:admin ==> role:relation:admin
|
||||
role:relation:admin ==> role:relation:agent
|
||||
role:holderPerson:admin ==> role:relation:agent
|
||||
role:relation:agent ==> role:relation:tenant
|
||||
role:holderPerson:admin ==> role:relation:tenant
|
||||
role:contact:admin ==> role:relation:tenant
|
||||
role:relation:tenant ==> role:anchorPerson:referrer
|
||||
role:relation:tenant ==> role:holderPerson:referrer
|
||||
role:relation:tenant ==> role:contact:referrer
|
||||
|
||||
%% granting permissions to roles
|
||||
role:relation:owner ==> perm:relation:DELETE
|
||||
role:relation:admin ==> perm:relation:UPDATE
|
||||
role:relation:tenant ==> perm:relation:SELECT
|
||||
|
||||
```
|
@ -0,0 +1,191 @@
|
||||
--liquibase formatted sql
|
||||
-- This code generated was by RbacViewPostgresGenerator, do not amend manually.
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-relation-rbac-OBJECT:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRelatedRbacObject('hs_office_relation');
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-relation-rbac-ROLE-DESCRIPTORS:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacRoleDescriptors('hsOfficeRelation', 'hs_office_relation');
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-relation-rbac-insert-trigger:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
Creates the roles, grants and permission for the AFTER INSERT TRIGGER.
|
||||
*/
|
||||
|
||||
create or replace procedure buildRbacSystemForHsOfficeRelation(
|
||||
NEW hs_office_relation
|
||||
)
|
||||
language plpgsql as $$
|
||||
|
||||
declare
|
||||
newHolderPerson hs_office_person;
|
||||
newAnchorPerson hs_office_person;
|
||||
newContact hs_office_contact;
|
||||
|
||||
begin
|
||||
call enterTriggerForObjectUuid(NEW.uuid);
|
||||
|
||||
SELECT * FROM hs_office_person WHERE uuid = NEW.holderUuid INTO newHolderPerson;
|
||||
|
||||
SELECT * FROM hs_office_person WHERE uuid = NEW.anchorUuid INTO newAnchorPerson;
|
||||
|
||||
SELECT * FROM hs_office_contact WHERE uuid = NEW.contactUuid INTO newContact;
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeRelationOwner(NEW),
|
||||
permissions => array['DELETE'],
|
||||
incomingSuperRoles => array[globalAdmin()],
|
||||
userUuids => array[currentUserUuid()]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeRelationAdmin(NEW),
|
||||
permissions => array['UPDATE'],
|
||||
incomingSuperRoles => array[
|
||||
hsOfficePersonAdmin(newAnchorPerson),
|
||||
hsOfficeRelationOwner(NEW)]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeRelationAgent(NEW),
|
||||
incomingSuperRoles => array[
|
||||
hsOfficePersonAdmin(newHolderPerson),
|
||||
hsOfficeRelationAdmin(NEW)]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeRelationTenant(NEW),
|
||||
permissions => array['SELECT'],
|
||||
incomingSuperRoles => array[
|
||||
hsOfficeContactAdmin(newContact),
|
||||
hsOfficePersonAdmin(newHolderPerson),
|
||||
hsOfficeRelationAgent(NEW)],
|
||||
outgoingSubRoles => array[
|
||||
hsOfficeContactReferrer(newContact),
|
||||
hsOfficePersonReferrer(newAnchorPerson),
|
||||
hsOfficePersonReferrer(newHolderPerson)]
|
||||
);
|
||||
|
||||
call leaveTriggerForObjectUuid(NEW.uuid);
|
||||
end; $$;
|
||||
|
||||
/*
|
||||
AFTER INSERT TRIGGER to create the role+grant structure for a new hs_office_relation row.
|
||||
*/
|
||||
|
||||
create or replace function insertTriggerForHsOfficeRelation_tf()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
strict as $$
|
||||
begin
|
||||
call buildRbacSystemForHsOfficeRelation(NEW);
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
create trigger insertTriggerForHsOfficeRelation_tg
|
||||
after insert on hs_office_relation
|
||||
for each row
|
||||
execute procedure insertTriggerForHsOfficeRelation_tf();
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-relation-rbac-update-trigger:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
Called from the AFTER UPDATE TRIGGER to re-wire the grants.
|
||||
*/
|
||||
|
||||
create or replace procedure updateRbacRulesForHsOfficeRelation(
|
||||
OLD hs_office_relation,
|
||||
NEW hs_office_relation
|
||||
)
|
||||
language plpgsql as $$
|
||||
begin
|
||||
|
||||
if NEW.contactUuid is distinct from OLD.contactUuid then
|
||||
delete from rbacgrants g where g.grantedbytriggerof = OLD.uuid;
|
||||
call buildRbacSystemForHsOfficeRelation(NEW);
|
||||
end if;
|
||||
end; $$;
|
||||
|
||||
/*
|
||||
AFTER INSERT TRIGGER to re-wire the grant structure for a new hs_office_relation row.
|
||||
*/
|
||||
|
||||
create or replace function updateTriggerForHsOfficeRelation_tf()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
strict as $$
|
||||
begin
|
||||
call updateRbacRulesForHsOfficeRelation(OLD, NEW);
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
create trigger updateTriggerForHsOfficeRelation_tg
|
||||
after update on hs_office_relation
|
||||
for each row
|
||||
execute procedure updateTriggerForHsOfficeRelation_tf();
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-relation-rbac-INSERT:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
Checks if the user or assumed roles are allowed to insert a row to hs_office_relation,
|
||||
where only global-admin has that permission.
|
||||
*/
|
||||
create or replace function hs_office_relation_insert_permission_missing_tf()
|
||||
returns trigger
|
||||
language plpgsql as $$
|
||||
begin
|
||||
raise exception '[403] insert into hs_office_relation not allowed for current subjects % (%)',
|
||||
currentSubjects(), currentSubjectsUuids();
|
||||
end; $$;
|
||||
|
||||
create trigger hs_office_relation_insert_permission_check_tg
|
||||
before insert on hs_office_relation
|
||||
for each row
|
||||
when ( not isGlobalAdmin() )
|
||||
execute procedure hs_office_relation_insert_permission_missing_tf();
|
||||
--//
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-relation-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
call generateRbacIdentityViewFromProjection('hs_office_relation',
|
||||
$idName$
|
||||
(select idName from hs_office_person_iv p where p.uuid = anchorUuid)
|
||||
|| '-with-' || target.type || '-'
|
||||
|| (select idName from hs_office_person_iv p where p.uuid = holderUuid)
|
||||
$idName$);
|
||||
--//
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-relation-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacRestrictedView('hs_office_relation',
|
||||
$orderBy$
|
||||
(select idName from hs_office_person_iv p where p.uuid = target.holderUuid)
|
||||
$orderBy$,
|
||||
$updates$
|
||||
contactUuid = new.contactUuid
|
||||
$updates$);
|
||||
--//
|
||||
|
@ -0,0 +1,158 @@
|
||||
### rbac partner
|
||||
|
||||
This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually.
|
||||
|
||||
```mermaid
|
||||
%%{init:{'flowchart':{'htmlLabels':false}}}%%
|
||||
flowchart TB
|
||||
|
||||
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 partner["`**partner**`"]
|
||||
direction TB
|
||||
style partner fill:#dd4901,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph partner:permissions[ ]
|
||||
style partner:permissions fill:#dd4901,stroke:white
|
||||
|
||||
perm:partner:INSERT{{partner:INSERT}}
|
||||
perm:partner:DELETE{{partner:DELETE}}
|
||||
perm:partner:UPDATE{{partner:UPDATE}}
|
||||
perm:partner:SELECT{{partner:SELECT}}
|
||||
end
|
||||
|
||||
subgraph partnerRel["`**partnerRel**`"]
|
||||
direction TB
|
||||
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
|
||||
|
||||
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:INSERT
|
||||
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
|
||||
|
||||
```
|
@ -0,0 +1,248 @@
|
||||
--liquibase formatted sql
|
||||
-- This code generated was by RbacViewPostgresGenerator, do not amend manually.
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-partner-rbac-OBJECT:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRelatedRbacObject('hs_office_partner');
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-partner-rbac-ROLE-DESCRIPTORS:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacRoleDescriptors('hsOfficePartner', 'hs_office_partner');
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-partner-rbac-insert-trigger:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
Creates the roles, grants and permission for the AFTER INSERT TRIGGER.
|
||||
*/
|
||||
|
||||
create or replace procedure buildRbacSystemForHsOfficePartner(
|
||||
NEW hs_office_partner
|
||||
)
|
||||
language plpgsql as $$
|
||||
|
||||
declare
|
||||
newPartnerRel hs_office_relation;
|
||||
newPartnerDetails hs_office_partner_details;
|
||||
|
||||
begin
|
||||
call enterTriggerForObjectUuid(NEW.uuid);
|
||||
|
||||
SELECT * FROM hs_office_relation WHERE uuid = NEW.partnerRelUuid INTO newPartnerRel;
|
||||
assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.partnerRelUuid = %s', NEW.partnerRelUuid);
|
||||
|
||||
SELECT * FROM hs_office_partner_details WHERE uuid = NEW.detailsUuid INTO newPartnerDetails;
|
||||
assert newPartnerDetails.uuid is not null, format('newPartnerDetails must not be null for NEW.detailsUuid = %s', NEW.detailsUuid);
|
||||
|
||||
call grantPermissionToRole(createPermission(NEW.uuid, 'DELETE'), hsOfficeRelationAdmin(newPartnerRel));
|
||||
call grantPermissionToRole(createPermission(NEW.uuid, 'SELECT'), hsOfficeRelationTenant(newPartnerRel));
|
||||
call grantPermissionToRole(createPermission(NEW.uuid, 'UPDATE'), hsOfficeRelationAgent(newPartnerRel));
|
||||
call grantPermissionToRole(createPermission(newPartnerDetails.uuid, 'DELETE'), hsOfficeRelationAdmin(newPartnerRel));
|
||||
call grantPermissionToRole(createPermission(newPartnerDetails.uuid, 'SELECT'), hsOfficeRelationAgent(newPartnerRel));
|
||||
call grantPermissionToRole(createPermission(newPartnerDetails.uuid, 'UPDATE'), hsOfficeRelationAgent(newPartnerRel));
|
||||
|
||||
call leaveTriggerForObjectUuid(NEW.uuid);
|
||||
end; $$;
|
||||
|
||||
/*
|
||||
AFTER INSERT TRIGGER to create the role+grant structure for a new hs_office_partner row.
|
||||
*/
|
||||
|
||||
create or replace function insertTriggerForHsOfficePartner_tf()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
strict as $$
|
||||
begin
|
||||
call buildRbacSystemForHsOfficePartner(NEW);
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
create trigger insertTriggerForHsOfficePartner_tg
|
||||
after insert on hs_office_partner
|
||||
for each row
|
||||
execute procedure insertTriggerForHsOfficePartner_tf();
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-partner-rbac-update-trigger:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
Called from the AFTER UPDATE TRIGGER to re-wire the grants.
|
||||
*/
|
||||
|
||||
create or replace procedure updateRbacRulesForHsOfficePartner(
|
||||
OLD hs_office_partner,
|
||||
NEW hs_office_partner
|
||||
)
|
||||
language plpgsql as $$
|
||||
|
||||
declare
|
||||
oldPartnerRel hs_office_relation;
|
||||
newPartnerRel hs_office_relation;
|
||||
oldPartnerDetails hs_office_partner_details;
|
||||
newPartnerDetails hs_office_partner_details;
|
||||
|
||||
begin
|
||||
call enterTriggerForObjectUuid(NEW.uuid);
|
||||
|
||||
SELECT * FROM hs_office_relation WHERE uuid = OLD.partnerRelUuid INTO oldPartnerRel;
|
||||
assert oldPartnerRel.uuid is not null, format('oldPartnerRel must not be null for OLD.partnerRelUuid = %s', OLD.partnerRelUuid);
|
||||
|
||||
SELECT * FROM hs_office_relation WHERE uuid = NEW.partnerRelUuid INTO newPartnerRel;
|
||||
assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.partnerRelUuid = %s', NEW.partnerRelUuid);
|
||||
|
||||
SELECT * FROM hs_office_partner_details WHERE uuid = OLD.detailsUuid INTO oldPartnerDetails;
|
||||
assert oldPartnerDetails.uuid is not null, format('oldPartnerDetails must not be null for OLD.detailsUuid = %s', OLD.detailsUuid);
|
||||
|
||||
SELECT * FROM hs_office_partner_details WHERE uuid = NEW.detailsUuid INTO newPartnerDetails;
|
||||
assert newPartnerDetails.uuid is not null, format('newPartnerDetails must not be null for NEW.detailsUuid = %s', NEW.detailsUuid);
|
||||
|
||||
|
||||
if NEW.partnerRelUuid <> OLD.partnerRelUuid then
|
||||
|
||||
call revokePermissionFromRole(getPermissionId(OLD.uuid, 'DELETE'), hsOfficeRelationAdmin(oldPartnerRel));
|
||||
call grantPermissionToRole(createPermission(NEW.uuid, 'DELETE'), hsOfficeRelationAdmin(newPartnerRel));
|
||||
|
||||
call revokePermissionFromRole(getPermissionId(OLD.uuid, 'UPDATE'), hsOfficeRelationAgent(oldPartnerRel));
|
||||
call grantPermissionToRole(createPermission(NEW.uuid, 'UPDATE'), hsOfficeRelationAgent(newPartnerRel));
|
||||
|
||||
call revokePermissionFromRole(getPermissionId(OLD.uuid, 'SELECT'), hsOfficeRelationTenant(oldPartnerRel));
|
||||
call grantPermissionToRole(createPermission(NEW.uuid, 'SELECT'), hsOfficeRelationTenant(newPartnerRel));
|
||||
|
||||
call revokePermissionFromRole(getPermissionId(oldPartnerDetails.uuid, 'DELETE'), hsOfficeRelationAdmin(oldPartnerRel));
|
||||
call grantPermissionToRole(createPermission(newPartnerDetails.uuid, 'DELETE'), hsOfficeRelationAdmin(newPartnerRel));
|
||||
|
||||
call revokePermissionFromRole(getPermissionId(oldPartnerDetails.uuid, 'UPDATE'), hsOfficeRelationAgent(oldPartnerRel));
|
||||
call grantPermissionToRole(createPermission(newPartnerDetails.uuid, 'UPDATE'), hsOfficeRelationAgent(newPartnerRel));
|
||||
|
||||
call revokePermissionFromRole(getPermissionId(oldPartnerDetails.uuid, 'SELECT'), hsOfficeRelationAgent(oldPartnerRel));
|
||||
call grantPermissionToRole(createPermission(newPartnerDetails.uuid, 'SELECT'), hsOfficeRelationAgent(newPartnerRel));
|
||||
|
||||
end if;
|
||||
|
||||
call leaveTriggerForObjectUuid(NEW.uuid);
|
||||
end; $$;
|
||||
|
||||
/*
|
||||
AFTER INSERT TRIGGER to re-wire the grant structure for a new hs_office_partner row.
|
||||
*/
|
||||
|
||||
create or replace function updateTriggerForHsOfficePartner_tf()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
strict as $$
|
||||
begin
|
||||
call updateRbacRulesForHsOfficePartner(OLD, NEW);
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
create trigger updateTriggerForHsOfficePartner_tg
|
||||
after update on hs_office_partner
|
||||
for each row
|
||||
execute procedure updateTriggerForHsOfficePartner_tf();
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-partner-rbac-INSERT:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
Creates INSERT INTO hs_office_partner permissions for the related global rows.
|
||||
*/
|
||||
do language plpgsql $$
|
||||
declare
|
||||
row global;
|
||||
permissionUuid uuid;
|
||||
roleUuid uuid;
|
||||
begin
|
||||
call defineContext('create INSERT INTO hs_office_partner permissions for the related global rows');
|
||||
|
||||
FOR row IN SELECT * FROM global
|
||||
LOOP
|
||||
roleUuid := findRoleId(globalAdmin());
|
||||
permissionUuid := createPermission(row.uuid, 'INSERT', 'hs_office_partner');
|
||||
call grantPermissionToRole(permissionUuid, roleUuid);
|
||||
END LOOP;
|
||||
END;
|
||||
$$;
|
||||
|
||||
/**
|
||||
Adds hs_office_partner INSERT permission to specified role of new global rows.
|
||||
*/
|
||||
create or replace function hs_office_partner_global_insert_tf()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
strict as $$
|
||||
begin
|
||||
call grantPermissionToRole(
|
||||
createPermission(NEW.uuid, 'INSERT', 'hs_office_partner'),
|
||||
globalAdmin());
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
-- z_... is to put it at the end of after insert triggers, to make sure the roles exist
|
||||
create trigger z_hs_office_partner_global_insert_tg
|
||||
after insert on global
|
||||
for each row
|
||||
execute procedure hs_office_partner_global_insert_tf();
|
||||
|
||||
/**
|
||||
Checks if the user or assumed roles are allowed to insert a row to hs_office_partner,
|
||||
where only global-admin has that permission.
|
||||
*/
|
||||
create or replace function hs_office_partner_insert_permission_missing_tf()
|
||||
returns trigger
|
||||
language plpgsql as $$
|
||||
begin
|
||||
raise exception '[403] insert into hs_office_partner not allowed for current subjects % (%)',
|
||||
currentSubjects(), currentSubjectsUuids();
|
||||
end; $$;
|
||||
|
||||
create trigger hs_office_partner_insert_permission_check_tg
|
||||
before insert on hs_office_partner
|
||||
for each row
|
||||
when ( not isGlobalAdmin() )
|
||||
execute procedure hs_office_partner_insert_permission_missing_tf();
|
||||
--//
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-partner-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
call generateRbacIdentityViewFromQuery('hs_office_partner',
|
||||
$idName$
|
||||
SELECT partner.partnerNumber
|
||||
|| ':' || (SELECT idName FROM hs_office_person_iv p WHERE p.uuid = partner.personUuid)
|
||||
|| '-' || (SELECT idName FROM hs_office_contact_iv c WHERE c.uuid = partner.contactUuid)
|
||||
FROM hs_office_partner AS partner
|
||||
$idName$);
|
||||
--//
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-partner-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacRestrictedView('hs_office_partner',
|
||||
$orderBy$
|
||||
SELECT partner.partnerNumber
|
||||
|| ':' || (SELECT idName FROM hs_office_person_iv p WHERE p.uuid = partner.personUuid)
|
||||
|| '-' || (SELECT idName FROM hs_office_contact_iv c WHERE c.uuid = partner.contactUuid)
|
||||
FROM hs_office_partner AS partner
|
||||
$orderBy$,
|
||||
$updates$
|
||||
partnerRelUuid = new.partnerRelUuid,
|
||||
personUuid = new.personUuid,
|
||||
contactUuid = new.contactUuid
|
||||
$updates$);
|
||||
--//
|
||||
|
@ -0,0 +1,136 @@
|
||||
### rbac partnerDetails
|
||||
|
||||
This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually.
|
||||
|
||||
```mermaid
|
||||
%%{init:{'flowchart':{'htmlLabels':false}}}%%
|
||||
flowchart TB
|
||||
|
||||
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 partnerDetails["`**partnerDetails**`"]
|
||||
direction TB
|
||||
style partnerDetails fill:#dd4901,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph partnerDetails:permissions[ ]
|
||||
style partnerDetails:permissions fill:#dd4901,stroke:white
|
||||
|
||||
perm:partnerDetails:INSERT{{partnerDetails:INSERT}}
|
||||
end
|
||||
|
||||
subgraph partnerRel["`**partnerRel**`"]
|
||||
direction TB
|
||||
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
|
||||
|
||||
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 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:partnerDetails:INSERT
|
||||
|
||||
```
|
@ -0,0 +1,164 @@
|
||||
--liquibase formatted sql
|
||||
-- This code generated was by RbacViewPostgresGenerator, do not amend manually.
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-partner-details-rbac-OBJECT:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRelatedRbacObject('hs_office_partner_details');
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-partner-details-rbac-ROLE-DESCRIPTORS:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacRoleDescriptors('hsOfficePartnerDetails', 'hs_office_partner_details');
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-partner-details-rbac-insert-trigger:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
Creates the roles, grants and permission for the AFTER INSERT TRIGGER.
|
||||
*/
|
||||
|
||||
create or replace procedure buildRbacSystemForHsOfficePartnerDetails(
|
||||
NEW hs_office_partner_details
|
||||
)
|
||||
language plpgsql as $$
|
||||
|
||||
declare
|
||||
newPartnerRel hs_office_relation;
|
||||
|
||||
begin
|
||||
call enterTriggerForObjectUuid(NEW.uuid);
|
||||
|
||||
SELECT partnerRel.*
|
||||
FROM hs_office_relation AS partnerRel
|
||||
JOIN hs_office_partner AS partner
|
||||
ON partner.detailsUuid = NEW.uuid
|
||||
WHERE partnerRel.uuid = partner.partnerRelUuid
|
||||
INTO newPartnerRel;
|
||||
assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.partnerRelUuid = %s', NEW.partnerRelUuid);
|
||||
|
||||
call leaveTriggerForObjectUuid(NEW.uuid);
|
||||
end; $$;
|
||||
|
||||
/*
|
||||
AFTER INSERT TRIGGER to create the role+grant structure for a new hs_office_partner_details row.
|
||||
*/
|
||||
|
||||
create or replace function insertTriggerForHsOfficePartnerDetails_tf()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
strict as $$
|
||||
begin
|
||||
call buildRbacSystemForHsOfficePartnerDetails(NEW);
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
create trigger insertTriggerForHsOfficePartnerDetails_tg
|
||||
after insert on hs_office_partner_details
|
||||
for each row
|
||||
execute procedure insertTriggerForHsOfficePartnerDetails_tf();
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-partner-details-rbac-INSERT:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
Creates INSERT INTO hs_office_partner_details permissions for the related global rows.
|
||||
*/
|
||||
do language plpgsql $$
|
||||
declare
|
||||
row global;
|
||||
permissionUuid uuid;
|
||||
roleUuid uuid;
|
||||
begin
|
||||
call defineContext('create INSERT INTO hs_office_partner_details permissions for the related global rows');
|
||||
|
||||
FOR row IN SELECT * FROM global
|
||||
LOOP
|
||||
roleUuid := findRoleId(globalAdmin());
|
||||
permissionUuid := createPermission(row.uuid, 'INSERT', 'hs_office_partner_details');
|
||||
call grantPermissionToRole(permissionUuid, roleUuid);
|
||||
END LOOP;
|
||||
END;
|
||||
$$;
|
||||
|
||||
/**
|
||||
Adds hs_office_partner_details INSERT permission to specified role of new global rows.
|
||||
*/
|
||||
create or replace function hs_office_partner_details_global_insert_tf()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
strict as $$
|
||||
begin
|
||||
call grantPermissionToRole(
|
||||
createPermission(NEW.uuid, 'INSERT', 'hs_office_partner_details'),
|
||||
globalAdmin());
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
-- z_... is to put it at the end of after insert triggers, to make sure the roles exist
|
||||
create trigger z_hs_office_partner_details_global_insert_tg
|
||||
after insert on global
|
||||
for each row
|
||||
execute procedure hs_office_partner_details_global_insert_tf();
|
||||
|
||||
/**
|
||||
Checks if the user or assumed roles are allowed to insert a row to hs_office_partner_details,
|
||||
where only global-admin has that permission.
|
||||
*/
|
||||
create or replace function hs_office_partner_details_insert_permission_missing_tf()
|
||||
returns trigger
|
||||
language plpgsql as $$
|
||||
begin
|
||||
raise exception '[403] insert into hs_office_partner_details not allowed for current subjects % (%)',
|
||||
currentSubjects(), currentSubjectsUuids();
|
||||
end; $$;
|
||||
|
||||
create trigger hs_office_partner_details_insert_permission_check_tg
|
||||
before insert on hs_office_partner_details
|
||||
for each row
|
||||
when ( not isGlobalAdmin() )
|
||||
execute procedure hs_office_partner_details_insert_permission_missing_tf();
|
||||
--//
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-partner-details-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
call generateRbacIdentityViewFromQuery('hs_office_partner_details',
|
||||
$idName$
|
||||
SELECT partner_iv.idName || '-details'
|
||||
FROM hs_office_partner_details AS partnerDetails
|
||||
JOIN hs_office_partner partner ON partner.detailsUuid = partnerDetails.uuid
|
||||
JOIN hs_office_partner_iv partner_iv ON partner_iv.uuid = partner.uuid
|
||||
$idName$);
|
||||
--//
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-partner-details-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacRestrictedView('hs_office_partner_details',
|
||||
$orderBy$
|
||||
SELECT partner_iv.idName || '-details'
|
||||
FROM hs_office_partner_details AS partnerDetails
|
||||
JOIN hs_office_partner partner ON partner.detailsUuid = partnerDetails.uuid
|
||||
JOIN hs_office_partner_iv partner_iv ON partner_iv.uuid = partner.uuid
|
||||
$orderBy$,
|
||||
$updates$
|
||||
registrationOffice = new.registrationOffice,
|
||||
registrationNumber = new.registrationNumber,
|
||||
birthPlace = new.birthPlace,
|
||||
birthName = new.birthName,
|
||||
birthday = new.birthday,
|
||||
dateOfDeath = new.dateOfDeath
|
||||
$updates$);
|
||||
--//
|
||||
|
@ -0,0 +1,43 @@
|
||||
### rbac bankAccount
|
||||
|
||||
This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually.
|
||||
|
||||
```mermaid
|
||||
%%{init:{'flowchart':{'htmlLabels':false}}}%%
|
||||
flowchart TB
|
||||
|
||||
subgraph bankAccount["`**bankAccount**`"]
|
||||
direction TB
|
||||
style bankAccount fill:#dd4901,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph bankAccount:roles[ ]
|
||||
style bankAccount:roles fill:#dd4901,stroke:white
|
||||
|
||||
role:bankAccount:owner[[bankAccount:owner]]
|
||||
role:bankAccount:admin[[bankAccount:admin]]
|
||||
role:bankAccount:referrer[[bankAccount:referrer]]
|
||||
end
|
||||
|
||||
subgraph bankAccount:permissions[ ]
|
||||
style bankAccount:permissions fill:#dd4901,stroke:white
|
||||
|
||||
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:bankAccount:owner ==> perm:bankAccount:DELETE
|
||||
role:bankAccount:admin ==> perm:bankAccount:UPDATE
|
||||
role:bankAccount:referrer ==> perm:bankAccount:SELECT
|
||||
|
||||
```
|
@ -0,0 +1,125 @@
|
||||
--liquibase formatted sql
|
||||
-- This code generated was by RbacViewPostgresGenerator, do not amend manually.
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-bankaccount-rbac-OBJECT:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRelatedRbacObject('hs_office_bankaccount');
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-bankaccount-rbac-ROLE-DESCRIPTORS:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacRoleDescriptors('hsOfficeBankAccount', 'hs_office_bankaccount');
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-bankaccount-rbac-insert-trigger:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
Creates the roles, grants and permission for the AFTER INSERT TRIGGER.
|
||||
*/
|
||||
|
||||
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'],
|
||||
incomingSuperRoles => array[globalAdmin()],
|
||||
userUuids => array[currentUserUuid()]
|
||||
);
|
||||
|
||||
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
|
||||
call buildRbacSystemForHsOfficeBankAccount(NEW);
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
create trigger insertTriggerForHsOfficeBankAccount_tg
|
||||
after insert on hs_office_bankaccount
|
||||
for each row
|
||||
execute procedure insertTriggerForHsOfficeBankAccount_tf();
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-bankaccount-rbac-INSERT:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
Checks if the user or assumed roles are allowed to insert a row to hs_office_bankaccount,
|
||||
where only global-admin has that permission.
|
||||
*/
|
||||
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; $$;
|
||||
|
||||
create trigger hs_office_bankaccount_insert_permission_check_tg
|
||||
before insert on hs_office_bankaccount
|
||||
for each row
|
||||
when ( not isGlobalAdmin() )
|
||||
execute procedure hs_office_bankaccount_insert_permission_missing_tf();
|
||||
--//
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-bankaccount-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
call generateRbacIdentityViewFromProjection('hs_office_bankaccount',
|
||||
$idName$
|
||||
iban || ':' || holder
|
||||
$idName$);
|
||||
--//
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-bankaccount-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacRestrictedView('hs_office_bankaccount',
|
||||
$orderBy$
|
||||
iban || ':' || holder
|
||||
$orderBy$,
|
||||
$updates$
|
||||
holder = new.holder,
|
||||
iban = new.iban,
|
||||
bic = new.bic
|
||||
$updates$);
|
||||
--//
|
||||
|
@ -0,0 +1,178 @@
|
||||
### rbac sepaMandate
|
||||
|
||||
This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually.
|
||||
|
||||
```mermaid
|
||||
%%{init:{'flowchart':{'htmlLabels':false}}}%%
|
||||
flowchart TB
|
||||
|
||||
subgraph bankAccount["`**bankAccount**`"]
|
||||
direction TB
|
||||
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 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 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
|
||||
|
||||
```
|
@ -0,0 +1,143 @@
|
||||
--liquibase formatted sql
|
||||
-- This code generated was by RbacViewPostgresGenerator, do not amend manually.
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-sepamandate-rbac-OBJECT:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRelatedRbacObject('hs_office_sepamandate');
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-sepamandate-rbac-ROLE-DESCRIPTORS:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacRoleDescriptors('hsOfficeSepaMandate', 'hs_office_sepamandate');
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-sepamandate-rbac-insert-trigger:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
Creates the roles, grants and permission for the AFTER INSERT TRIGGER.
|
||||
*/
|
||||
|
||||
create or replace procedure buildRbacSystemForHsOfficeSepaMandate(
|
||||
NEW hs_office_sepamandate
|
||||
)
|
||||
language plpgsql as $$
|
||||
|
||||
declare
|
||||
newBankAccount hs_office_bankaccount;
|
||||
newDebitorRel hs_office_relation;
|
||||
|
||||
begin
|
||||
call enterTriggerForObjectUuid(NEW.uuid);
|
||||
|
||||
SELECT * FROM hs_office_bankaccount WHERE uuid = NEW.bankAccountUuid INTO newBankAccount;
|
||||
|
||||
SELECT * FROM hs_office_relation WHERE uuid = NEW.debitorRelUuid INTO newDebitorRel;
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeSepaMandateOwner(NEW),
|
||||
permissions => array['DELETE'],
|
||||
incomingSuperRoles => array[globalAdmin()],
|
||||
userUuids => array[currentUserUuid()]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeSepaMandateAdmin(NEW),
|
||||
permissions => array['UPDATE'],
|
||||
incomingSuperRoles => array[hsOfficeSepaMandateOwner(NEW)]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeSepaMandateAgent(NEW),
|
||||
incomingSuperRoles => array[hsOfficeSepaMandateAdmin(NEW)],
|
||||
outgoingSubRoles => array[
|
||||
hsOfficeBankAccountReferrer(newBankAccount),
|
||||
hsOfficeRelationAgent(newDebitorRel)]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeSepaMandateReferrer(NEW),
|
||||
permissions => array['SELECT'],
|
||||
incomingSuperRoles => array[
|
||||
hsOfficeBankAccountAdmin(newBankAccount),
|
||||
hsOfficeRelationAgent(newDebitorRel),
|
||||
hsOfficeSepaMandateAgent(NEW)],
|
||||
outgoingSubRoles => array[hsOfficeRelationTenant(newDebitorRel)]
|
||||
);
|
||||
|
||||
call leaveTriggerForObjectUuid(NEW.uuid);
|
||||
end; $$;
|
||||
|
||||
/*
|
||||
AFTER INSERT TRIGGER to create the role+grant structure for a new hs_office_sepamandate row.
|
||||
*/
|
||||
|
||||
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 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,
|
||||
where only global-admin has that permission.
|
||||
*/
|
||||
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
|
||||
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',
|
||||
$idName$
|
||||
concat(tradeName, familyName, givenName)
|
||||
$idName$);
|
||||
--//
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-sepamandate-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacRestrictedView('hs_office_sepamandate',
|
||||
$orderBy$
|
||||
concat(tradeName, familyName, givenName)
|
||||
$orderBy$,
|
||||
$updates$
|
||||
reference = new.reference,
|
||||
agreement = new.agreement,
|
||||
validity = new.validity
|
||||
$updates$);
|
||||
--//
|
||||
|
@ -0,0 +1,275 @@
|
||||
### rbac debitor
|
||||
|
||||
This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually.
|
||||
|
||||
```mermaid
|
||||
%%{init:{'flowchart':{'htmlLabels':false}}}%%
|
||||
flowchart TB
|
||||
|
||||
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 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 debitor["`**debitor**`"]
|
||||
direction TB
|
||||
style debitor fill:#dd4901,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph debitor:permissions[ ]
|
||||
style debitor:permissions fill:#dd4901,stroke:white
|
||||
|
||||
perm:debitor:INSERT{{debitor:INSERT}}
|
||||
perm:debitor:DELETE{{debitor:DELETE}}
|
||||
perm:debitor:UPDATE{{debitor:UPDATE}}
|
||||
perm:debitor:SELECT{{debitor:SELECT}}
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
```
|
@ -0,0 +1,231 @@
|
||||
--liquibase formatted sql
|
||||
-- This code generated was by RbacViewPostgresGenerator, do not amend manually.
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-debitor-rbac-OBJECT:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRelatedRbacObject('hs_office_debitor');
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-debitor-rbac-ROLE-DESCRIPTORS:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacRoleDescriptors('hsOfficeDebitor', 'hs_office_debitor');
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-debitor-rbac-insert-trigger:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
Creates the roles, grants and permission for the AFTER INSERT TRIGGER.
|
||||
*/
|
||||
|
||||
create or replace procedure buildRbacSystemForHsOfficeDebitor(
|
||||
NEW hs_office_debitor
|
||||
)
|
||||
language plpgsql as $$
|
||||
|
||||
declare
|
||||
newPartnerRel hs_office_relation;
|
||||
newDebitorRel hs_office_relation;
|
||||
newRefundBankAccount hs_office_bankaccount;
|
||||
|
||||
begin
|
||||
call enterTriggerForObjectUuid(NEW.uuid);
|
||||
|
||||
SELECT * FROM hs_office_relation WHERE uuid = NEW.partnerRelUuid INTO newPartnerRel;
|
||||
|
||||
SELECT * FROM hs_office_relation WHERE 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_bankaccount WHERE uuid = NEW.refundBankAccountUuid INTO newRefundBankAccount;
|
||||
|
||||
call grantRoleToRole(hsOfficeBankAccountReferrer(newRefundBankAccount), hsOfficeRelationAgent(newDebitorRel));
|
||||
call grantRoleToRole(hsOfficeRelationAdmin(newDebitorRel), hsOfficeRelationAdmin(newPartnerRel));
|
||||
call grantRoleToRole(hsOfficeRelationAgent(newDebitorRel), hsOfficeBankAccountAdmin(newRefundBankAccount));
|
||||
call grantRoleToRole(hsOfficeRelationAgent(newDebitorRel), hsOfficeRelationAgent(newPartnerRel));
|
||||
call grantRoleToRole(hsOfficeRelationTenant(newPartnerRel), hsOfficeRelationAgent(newDebitorRel));
|
||||
|
||||
call grantPermissionToRole(createPermission(NEW.uuid, 'DELETE'), hsOfficeRelationOwner(newDebitorRel));
|
||||
call grantPermissionToRole(createPermission(NEW.uuid, 'SELECT'), hsOfficeRelationTenant(newDebitorRel));
|
||||
call grantPermissionToRole(createPermission(NEW.uuid, 'UPDATE'), hsOfficeRelationAdmin(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
|
||||
language plpgsql
|
||||
strict as $$
|
||||
begin
|
||||
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 $$
|
||||
begin
|
||||
|
||||
if NEW.refundBankAccountUuid is distinct from OLD.refundBankAccountUuid then
|
||||
delete from rbacgrants g where g.grantedbytriggerof = OLD.uuid;
|
||||
call buildRbacSystemForHsOfficeDebitor(NEW);
|
||||
end if;
|
||||
end; $$;
|
||||
|
||||
/*
|
||||
AFTER INSERT TRIGGER to re-wire the grant structure for a new hs_office_debitor row.
|
||||
*/
|
||||
|
||||
create or replace function updateTriggerForHsOfficeDebitor_tf()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
strict as $$
|
||||
begin
|
||||
call updateRbacRulesForHsOfficeDebitor(OLD, NEW);
|
||||
return NEW;
|
||||
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:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
Creates INSERT INTO hs_office_debitor permissions for the related global rows.
|
||||
*/
|
||||
do language plpgsql $$
|
||||
declare
|
||||
row global;
|
||||
permissionUuid uuid;
|
||||
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(
|
||||
createPermission(NEW.uuid, 'INSERT', 'hs_office_debitor'),
|
||||
globalAdmin());
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
-- z_... is to put it at the end of after insert triggers, to make sure the roles exist
|
||||
create trigger z_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,
|
||||
where only global-admin has that permission.
|
||||
*/
|
||||
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:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
call generateRbacIdentityViewFromQuery('hs_office_debitor',
|
||||
$idName$
|
||||
SELECT debitor.uuid,
|
||||
'D-' || (SELECT partner.partnerNumber
|
||||
FROM hs_office_partner partner
|
||||
JOIN hs_office_relation partnerRel
|
||||
ON partnerRel.uuid = partner.partnerRelUUid AND partnerRel.type = 'PARTNER'
|
||||
JOIN hs_office_relation debitorRel
|
||||
ON debitorRel.anchorUuid = partnerRel.holderUuid AND partnerRel.type = 'DEBITOR'
|
||||
WHERE debitorRel.uuid = debitor.debitorRelUuid)
|
||||
|| to_char(debitorNumberSuffix, 'fm00')
|
||||
from hs_office_debitor as debitor
|
||||
$idName$);
|
||||
--//
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-debitor-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacRestrictedView('hs_office_debitor',
|
||||
$orderBy$
|
||||
SELECT debitor.uuid,
|
||||
'D-' || (SELECT partner.partnerNumber
|
||||
FROM hs_office_partner partner
|
||||
JOIN hs_office_relation partnerRel
|
||||
ON partnerRel.uuid = partner.partnerRelUUid AND partnerRel.type = 'PARTNER'
|
||||
JOIN hs_office_relation debitorRel
|
||||
ON debitorRel.anchorUuid = partnerRel.holderUuid AND partnerRel.type = 'DEBITOR'
|
||||
WHERE debitorRel.uuid = debitor.debitorRelUuid)
|
||||
|| to_char(debitorNumberSuffix, 'fm00')
|
||||
from hs_office_debitor as debitor
|
||||
$orderBy$,
|
||||
$updates$
|
||||
debitorRel = new.debitorRel,
|
||||
billable = new.billable,
|
||||
debitorUuid = new.debitorUuid,
|
||||
refundBankAccountUuid = new.refundBankAccountUuid,
|
||||
vatId = new.vatId,
|
||||
vatCountryCode = new.vatCountryCode,
|
||||
vatBusiness = new.vatBusiness,
|
||||
vatReverseCharge = new.vatReverseCharge,
|
||||
defaultPrefix = new.defaultPrefix
|
||||
$updates$);
|
||||
--//
|
||||
|
@ -11,6 +11,8 @@ databaseChangeLog:
|
||||
file: db/changelog/005-uuid-ossp-extension.sql
|
||||
- include:
|
||||
file: db/changelog/006-numeric-hash-functions.sql
|
||||
- include:
|
||||
file: db/changelog/007-table-columns.sql
|
||||
- include:
|
||||
file: db/changelog/009-check-environment.sql
|
||||
- include:
|
||||
|
@ -462,7 +462,7 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
|
||||
RestAssured // @formatter:off
|
||||
.given()
|
||||
.header("current-user", "superuser-alex@hostsharing.net")
|
||||
.header("assumed-roles", "hs_office_partner#FirstGmbH-firstcontact.agent")
|
||||
.header("assumed-roles", "hs_office_partner#10001:FirstGmbH-firstcontact.admin")
|
||||
.port(port)
|
||||
.when()
|
||||
.delete("http://localhost/api/hs/office/memberships/" + givenMembership.getUuid())
|
||||
|
@ -204,7 +204,7 @@ class TestCustomerControllerAcceptanceTest {
|
||||
.statusCode(403)
|
||||
.contentType(ContentType.JSON)
|
||||
.statusCode(403)
|
||||
.body("message", containsString("insert into test_customer not allowed for current subjects {customer-admin@yyy.example.com}"));
|
||||
.body("message", containsString("ERROR: [403] insert into test_customer not allowed for current subjects {customer-admin@yyy.example.com}"));
|
||||
// @formatter:on
|
||||
|
||||
// finally, the new customer was not created
|
||||
|
@ -29,6 +29,7 @@ class TestCustomerEntityUnitTest {
|
||||
subgraph customer:permissions[ ]
|
||||
style customer:permissions fill:#dd4901,stroke:white
|
||||
|
||||
perm:customer:INSERT{{customer:INSERT}}
|
||||
perm:customer:DELETE{{customer:DELETE}}
|
||||
perm:customer:UPDATE{{customer:UPDATE}}
|
||||
perm:customer:SELECT{{customer:SELECT}}
|
||||
@ -44,6 +45,7 @@ class TestCustomerEntityUnitTest {
|
||||
role:customer:admin ==> role:customer:tenant
|
||||
|
||||
%% granting permissions to roles
|
||||
role:global:admin ==> perm:customer:INSERT
|
||||
role:customer:owner ==> perm:customer:DELETE
|
||||
role:customer:admin ==> perm:customer:UPDATE
|
||||
role:customer:tenant ==> perm:customer:SELECT
|
||||
|
@ -4,8 +4,9 @@ spring:
|
||||
platform: postgres
|
||||
|
||||
datasource:
|
||||
url: jdbc:tc:postgresql:15.5-bookworm:///spring_boot_testcontainers
|
||||
url-tc: jdbc:tc:postgresql:15.5-bookworm:///spring_boot_testcontainers
|
||||
url-local: jdbc:postgresql://localhost:5432/postgres
|
||||
url: ${spring.datasource.url-tc}
|
||||
username: postgres
|
||||
password: password
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user