improved RBAC generators #26
@ -19,9 +19,10 @@ import java.util.Optional;
|
|||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
|
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
|
||||||
|
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NULLABLE;
|
||||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
|
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
|
||||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
|
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
|
||||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.fetchedBySql;
|
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.directlyFetchedByDependsOnColumn;
|
||||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
|
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
|
||||||
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
||||||
|
|
||||||
@ -131,36 +132,26 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
|
|||||||
"vatBusiness",
|
"vatBusiness",
|
||||||
"vatReverseCharge",
|
"vatReverseCharge",
|
||||||
"defaultPrefix" /* TODO: do we want that updatable? */)
|
"defaultPrefix" /* TODO: do we want that updatable? */)
|
||||||
.createPermission(custom("new-debitor")).grantedTo("global", ADMIN)
|
.createPermission(INSERT).grantedTo("global", ADMIN)
|
||||||
|
|
||||||
.importRootEntityAliasProxy("debitorRel", HsOfficeRelationEntity.class,
|
.importRootEntityAliasProxy("debitorRel", HsOfficeRelationEntity.class,
|
||||||
fetchedBySql("""
|
directlyFetchedByDependsOnColumn(),
|
||||||
SELECT *
|
|
||||||
FROM hs_office_relation AS r
|
|
||||||
WHERE r.type = 'DEBITOR' AND r.holderUuid = ${REF}.debitorRelUuid
|
|
||||||
"""),
|
|
||||||
dependsOnColumn("debitorRelUuid"))
|
dependsOnColumn("debitorRelUuid"))
|
||||||
.createPermission(DELETE).grantedTo("debitorRel", OWNER)
|
.createPermission(DELETE).grantedTo("debitorRel", OWNER)
|
||||||
.createPermission(UPDATE).grantedTo("debitorRel", ADMIN)
|
.createPermission(UPDATE).grantedTo("debitorRel", ADMIN)
|
||||||
.createPermission(SELECT).grantedTo("debitorRel", TENANT)
|
.createPermission(SELECT).grantedTo("debitorRel", TENANT)
|
||||||
|
|
||||||
.importEntityAlias("refundBankAccount", HsOfficeBankAccountEntity.class,
|
.importEntityAlias("refundBankAccount", HsOfficeBankAccountEntity.class,
|
||||||
dependsOnColumn("refundBankAccountUuid"), fetchedBySql("""
|
dependsOnColumn("refundBankAccountUuid"),
|
||||||
SELECT *
|
directlyFetchedByDependsOnColumn(),
|
||||||
FROM hs_office_relation AS r
|
NULLABLE)
|
||||||
WHERE r.type = 'DEBITOR' AND r.holderUuid = ${REF}.debitorRelUuid
|
|
||||||
""")
|
|
||||||
)
|
|
||||||
.toRole("refundBankAccount", ADMIN).grantRole("debitorRel", AGENT)
|
.toRole("refundBankAccount", ADMIN).grantRole("debitorRel", AGENT)
|
||||||
.toRole("debitorRel", AGENT).grantRole("refundBankAccount", REFERRER)
|
.toRole("debitorRel", AGENT).grantRole("refundBankAccount", REFERRER)
|
||||||
|
|
||||||
.importEntityAlias("partnerRel", HsOfficeRelationEntity.class,
|
.importEntityAlias("partnerRel", HsOfficeRelationEntity.class,
|
||||||
dependsOnColumn("partnerRelUuid"), fetchedBySql("""
|
dependsOnColumn("partnerRelUuid"),
|
||||||
SELECT *
|
directlyFetchedByDependsOnColumn(),
|
||||||
FROM hs_office_relation AS partnerRel
|
NULLABLE)
|
||||||
WHERE ${debitorRel}.anchorUuid = partnerRel.holderUuid
|
|
||||||
""")
|
|
||||||
)
|
|
||||||
.toRole("partnerRel", ADMIN).grantRole("debitorRel", ADMIN)
|
.toRole("partnerRel", ADMIN).grantRole("debitorRel", ADMIN)
|
||||||
.toRole("partnerRel", AGENT).grantRole("debitorRel", AGENT)
|
.toRole("partnerRel", AGENT).grantRole("debitorRel", AGENT)
|
||||||
.toRole("debitorRel", AGENT).grantRole("partnerRel", TENANT)
|
.toRole("debitorRel", AGENT).grantRole("partnerRel", TENANT)
|
||||||
|
@ -84,11 +84,11 @@ public class HsOfficePartnerDetailsEntity implements HasUuid, Stringifyable {
|
|||||||
"birthName",
|
"birthName",
|
||||||
"birthday",
|
"birthday",
|
||||||
"dateOfDeath")
|
"dateOfDeath")
|
||||||
.createPermission(custom("new-partner-details")).grantedTo("global", ADMIN)
|
.createPermission(INSERT).grantedTo("global", ADMIN)
|
||||||
|
|
||||||
.importRootEntityAliasProxy("partnerRel", HsOfficeRelationEntity.class,
|
.importRootEntityAliasProxy("partnerRel", HsOfficeRelationEntity.class,
|
||||||
fetchedBySql("""
|
fetchedBySql("""
|
||||||
SELECT partnerRel.*
|
SELECT ${columns}
|
||||||
FROM hs_office_relation AS partnerRel
|
FROM hs_office_relation AS partnerRel
|
||||||
JOIN hs_office_partner AS partner
|
JOIN hs_office_partner AS partner
|
||||||
ON partner.detailsUuid = ${ref}.uuid
|
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.*;
|
||||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.SELECT;
|
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.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.rbac.rbacdef.RbacView.rbacViewFor;
|
||||||
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
||||||
|
|
||||||
@ -90,17 +90,17 @@ public class HsOfficePartnerEntity implements Stringifyable, HasUuid {
|
|||||||
"partnerRelUuid",
|
"partnerRelUuid",
|
||||||
"personUuid",
|
"personUuid",
|
||||||
"contactUuid")
|
"contactUuid")
|
||||||
.createPermission(custom("new-partner")).grantedTo("global", ADMIN)
|
.createPermission(INSERT).grantedTo("global", ADMIN)
|
||||||
|
|
||||||
.importRootEntityAliasProxy("partnerRel", HsOfficeRelationEntity.class,
|
.importRootEntityAliasProxy("partnerRel", HsOfficeRelationEntity.class,
|
||||||
fetchedBySql("SELECT * FROM hs_office_relation AS r WHERE r.uuid = ${ref}.partnerRelUuid"),
|
directlyFetchedByDependsOnColumn(),
|
||||||
dependsOnColumn("partnerRelUuid"))
|
dependsOnColumn("partnerRelUuid"))
|
||||||
.createPermission(DELETE).grantedTo("partnerRel", ADMIN)
|
.createPermission(DELETE).grantedTo("partnerRel", ADMIN)
|
||||||
.createPermission(UPDATE).grantedTo("partnerRel", AGENT)
|
.createPermission(UPDATE).grantedTo("partnerRel", AGENT)
|
||||||
.createPermission(SELECT).grantedTo("partnerRel", TENANT)
|
.createPermission(SELECT).grantedTo("partnerRel", TENANT)
|
||||||
|
|
||||||
.importSubEntityAlias("partnerDetails", HsOfficePartnerDetailsEntity.class,
|
.importSubEntityAlias("partnerDetails", HsOfficePartnerDetailsEntity.class,
|
||||||
fetchedBySql("SELECT * FROM hs_office_partner_details AS d WHERE d.uuid = ${ref}.detailsUuid"),
|
directlyFetchedByDependsOnColumn(),
|
||||||
dependsOnColumn("detailsUuid"))
|
dependsOnColumn("detailsUuid"))
|
||||||
.createPermission("partnerDetails", DELETE).grantedTo("partnerRel", ADMIN)
|
.createPermission("partnerDetails", DELETE).grantedTo("partnerRel", ADMIN)
|
||||||
.createPermission("partnerDetails", UPDATE).grantedTo("partnerRel", AGENT)
|
.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.Column.dependsOnColumn;
|
||||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.GLOBAL;
|
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.Permission.*;
|
||||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR;
|
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR;
|
||||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
|
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
|
||||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.fetchedBySql;
|
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.directlyFetchedByDependsOnColumn;
|
||||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
|
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
|
||||||
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
||||||
|
|
||||||
@ -90,16 +91,16 @@ public class HsOfficeRelationEntity implements HasUuid, Stringifyable {
|
|||||||
.withUpdatableColumns("contactUuid")
|
.withUpdatableColumns("contactUuid")
|
||||||
.importEntityAlias("anchorPerson", HsOfficePersonEntity.class,
|
.importEntityAlias("anchorPerson", HsOfficePersonEntity.class,
|
||||||
dependsOnColumn("anchorUuid"),
|
dependsOnColumn("anchorUuid"),
|
||||||
fetchedBySql("select * from hs_office_person as p where p.uuid = ${REF}.anchorUuid")
|
directlyFetchedByDependsOnColumn(),
|
||||||
)
|
NULLABLE)
|
||||||
.importEntityAlias("holderPerson", HsOfficePersonEntity.class,
|
.importEntityAlias("holderPerson", HsOfficePersonEntity.class,
|
||||||
dependsOnColumn("holderUuid"),
|
dependsOnColumn("holderUuid"),
|
||||||
fetchedBySql("select * from hs_office_person as p where p.uuid = ${REF}.holderUuid")
|
directlyFetchedByDependsOnColumn(),
|
||||||
)
|
NULLABLE)
|
||||||
.importEntityAlias("contact", HsOfficeContactEntity.class,
|
.importEntityAlias("contact", HsOfficeContactEntity.class,
|
||||||
dependsOnColumn("contactUuid"),
|
dependsOnColumn("contactUuid"),
|
||||||
fetchedBySql("select * from hs_office_contact as c where c.uuid = ${REF}.contactUuid")
|
directlyFetchedByDependsOnColumn(),
|
||||||
)
|
NULLABLE)
|
||||||
.createRole(OWNER, (with) -> {
|
.createRole(OWNER, (with) -> {
|
||||||
with.owningUser(CREATOR);
|
with.owningUser(CREATOR);
|
||||||
with.incomingSuperRole(GLOBAL, ADMIN);
|
with.incomingSuperRole(GLOBAL, ADMIN);
|
||||||
|
@ -4,6 +4,7 @@ import java.util.Optional;
|
|||||||
import java.util.function.BinaryOperator;
|
import java.util.function.BinaryOperator;
|
||||||
import java.util.stream.Stream;
|
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.Permission.INSERT;
|
||||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacGrantDefinition.GrantType.PERM_TO_ROLE;
|
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacGrantDefinition.GrantType.PERM_TO_ROLE;
|
||||||
import static net.hostsharing.hsadminng.rbac.rbacdef.StringWriter.with;
|
import static net.hostsharing.hsadminng.rbac.rbacdef.StringWriter.with;
|
||||||
@ -22,7 +23,7 @@ public class InsertTriggerGenerator {
|
|||||||
|
|
||||||
void generateTo(final StringWriter plPgSql) {
|
void generateTo(final StringWriter plPgSql) {
|
||||||
generateLiquibaseChangesetHeader(plPgSql);
|
generateLiquibaseChangesetHeader(plPgSql);
|
||||||
generateGrantInsertRoleToExistingCustomers(plPgSql);
|
generateGrantInsertRoleToExistingObjects(plPgSql);
|
||||||
generateInsertPermissionGrantTrigger(plPgSql);
|
generateInsertPermissionGrantTrigger(plPgSql);
|
||||||
generateInsertCheckTrigger(plPgSql);
|
generateInsertCheckTrigger(plPgSql);
|
||||||
plPgSql.writeLn("--//");
|
plPgSql.writeLn("--//");
|
||||||
@ -37,7 +38,7 @@ public class InsertTriggerGenerator {
|
|||||||
with("liquibaseTagPrefix", liquibaseTagPrefix));
|
with("liquibaseTagPrefix", liquibaseTagPrefix));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void generateGrantInsertRoleToExistingCustomers(final StringWriter plPgSql) {
|
private void generateGrantInsertRoleToExistingObjects(final StringWriter plPgSql) {
|
||||||
getOptionalInsertSuperRole().ifPresent( superRoleDef -> {
|
getOptionalInsertSuperRole().ifPresent( superRoleDef -> {
|
||||||
plPgSql.writeLn("""
|
plPgSql.writeLn("""
|
||||||
/*
|
/*
|
||||||
@ -53,16 +54,16 @@ public class InsertTriggerGenerator {
|
|||||||
|
|
||||||
FOR row IN SELECT * FROM ${rawSuperTableName}
|
FOR row IN SELECT * FROM ${rawSuperTableName}
|
||||||
LOOP
|
LOOP
|
||||||
roleUuid := findRoleId(${rawSuperRoleDescriptor}(row));
|
roleUuid := findRoleId(${rawSuperRoleDescriptor});
|
||||||
permissionUuid := createPermission(row.uuid, 'INSERT', '${rawSubTableName}');
|
permissionUuid := createPermission(row.uuid, 'INSERT', '${rawSubTableName}');
|
||||||
call grantPermissionToRole(roleUuid, permissionUuid);
|
call grantPermissionToRole(permissionUuid, roleUuid);
|
||||||
END LOOP;
|
END LOOP;
|
||||||
END;
|
END;
|
||||||
$$;
|
$$;
|
||||||
""",
|
""",
|
||||||
with("rawSubTableName", rbacDef.getRootEntityAlias().getRawTableName()),
|
with("rawSubTableName", rbacDef.getRootEntityAlias().getRawTableName()),
|
||||||
with("rawSuperTableName", superRoleDef.getEntityAlias().getRawTableName()),
|
with("rawSuperTableName", superRoleDef.getEntityAlias().getRawTableName()),
|
||||||
with("rawSuperRoleDescriptor", toVar(superRoleDef))
|
with("rawSuperRoleDescriptor", toRoleDescriptor(superRoleDef, "row"))
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -79,27 +80,60 @@ public class InsertTriggerGenerator {
|
|||||||
strict as $$
|
strict as $$
|
||||||
begin
|
begin
|
||||||
call grantPermissionToRole(
|
call grantPermissionToRole(
|
||||||
${rawSuperRoleDescriptor}(NEW),
|
createPermission(NEW.uuid, 'INSERT', '${rawSubTableName}'),
|
||||||
createPermission(NEW.uuid, 'INSERT', '${rawSubTableName}'));
|
${rawSuperRoleDescriptor});
|
||||||
return NEW;
|
return NEW;
|
||||||
end; $$;
|
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}
|
after insert on ${rawSuperTableName}
|
||||||
for each row
|
for each row
|
||||||
execute procedure ${rawSubTableName}_${rawSuperTableName}_insert_tf();
|
execute procedure ${rawSubTableName}_${rawSuperTableName}_insert_tf();
|
||||||
""",
|
""",
|
||||||
with("rawSubTableName", rbacDef.getRootEntityAlias().getRawTableName()),
|
with("rawSubTableName", rbacDef.getRootEntityAlias().getRawTableName()),
|
||||||
with("rawSuperTableName", superRoleDef.getEntityAlias().getRawTableName()),
|
with("rawSuperTableName", superRoleDef.getEntityAlias().getRawTableName()),
|
||||||
with("rawSuperRoleDescriptor", toVar(superRoleDef))
|
with("rawSuperRoleDescriptor", toRoleDescriptor(superRoleDef, NEW.name()))
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void generateInsertCheckTrigger(final StringWriter plPgSql) {
|
private void generateInsertCheckTrigger(final StringWriter plPgSql) {
|
||||||
|
getOptionalInsertGrant().ifPresentOrElse(g -> {
|
||||||
|
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("""
|
plPgSql.writeLn("""
|
||||||
/**
|
/**
|
||||||
Checks if the user or assumed roles are allowed to insert a row to ${rawSubTable}.
|
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()
|
create or replace function ${rawSubTable}_insert_permission_missing_tf()
|
||||||
returns trigger
|
returns trigger
|
||||||
@ -108,10 +142,7 @@ public class InsertTriggerGenerator {
|
|||||||
raise exception '[403] insert into ${rawSubTable} not allowed for current subjects % (%)',
|
raise exception '[403] insert into ${rawSubTable} not allowed for current subjects % (%)',
|
||||||
currentSubjects(), currentSubjectsUuids();
|
currentSubjects(), currentSubjectsUuids();
|
||||||
end; $$;
|
end; $$;
|
||||||
""",
|
|
||||||
with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableName()));
|
|
||||||
getOptionalInsertGrant().ifPresentOrElse(g -> {
|
|
||||||
plPgSql.writeLn("""
|
|
||||||
create trigger ${rawSubTable}_insert_permission_check_tg
|
create trigger ${rawSubTable}_insert_permission_check_tg
|
||||||
before insert on ${rawSubTable}
|
before insert on ${rawSubTable}
|
||||||
for each row
|
for each row
|
||||||
@ -120,19 +151,77 @@ public class InsertTriggerGenerator {
|
|||||||
""",
|
""",
|
||||||
with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableName()),
|
with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableName()),
|
||||||
with("referenceColumn", g.getSuperRoleDef().getEntityAlias().dependsOnColumName()));
|
with("referenceColumn", g.getSuperRoleDef().getEntityAlias().dependsOnColumName()));
|
||||||
},
|
}
|
||||||
() -> {
|
|
||||||
|
private void generateInsertPermissionTriggerAllowByRoleOfIndirectForeignKey(
|
||||||
|
final StringWriter plPgSql,
|
||||||
|
final RbacView.RbacGrantDefinition g) {
|
||||||
plPgSql.writeLn("""
|
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()
|
||||||
hsh-michaelhoennig marked this conversation as resolved
Outdated
|
|||||||
|
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
|
||||||
|
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
|
create trigger ${rawSubTable}_insert_permission_check_tg
|
||||||
before insert on ${rawSubTable}
|
before insert on ${rawSubTable}
|
||||||
for each row
|
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() )
|
when ( not isGlobalAdmin() )
|
||||||
execute procedure ${rawSubTable}_insert_permission_missing_tf();
|
execute procedure ${rawSubTable}_insert_permission_missing_tf();
|
||||||
""",
|
""",
|
||||||
with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableName()));
|
with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableName()));
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Stream<RbacView.RbacGrantDefinition> getInsertGrants() {
|
private Stream<RbacView.RbacGrantDefinition> getInsertGrants() {
|
||||||
@ -162,4 +251,12 @@ public class InsertTriggerGenerator {
|
|||||||
return uncapitalize(roleDef.getEntityAlias().simpleName()) + capitalize(roleDef.getRole().roleName());
|
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(
|
plPgSql.writeLn(
|
||||||
switch (rbacDef.getIdentityViewSqlQuery().part) {
|
switch (rbacDef.getIdentityViewSqlQuery().part) {
|
||||||
case SQL_PROJECTION -> """
|
case SQL_PROJECTION -> """
|
||||||
call generateRbacIdentityViewFromProjection('${rawTableName}', $idName$
|
call generateRbacIdentityViewFromProjection('${rawTableName}',
|
||||||
|
$idName$
|
||||||
${identityViewSqlPart}
|
${identityViewSqlPart}
|
||||||
$idName$);
|
$idName$);
|
||||||
""";
|
""";
|
||||||
case SQL_QUERY -> """
|
case SQL_QUERY -> """
|
||||||
call generateRbacIdentityViewFromProjection('${rawTableName}', $idName$
|
call generateRbacIdentityViewFromQuery('${rawTableName}',
|
||||||
|
$idName$
|
||||||
${identityViewSqlPart}
|
${identityViewSqlPart}
|
||||||
$idName$);
|
$idName$);
|
||||||
""";
|
""";
|
||||||
default -> throw new IllegalStateException("illegal SQL part given");
|
default -> throw new IllegalStateException("illegal SQL part given");
|
||||||
},
|
},
|
||||||
with("identityViewSqlPart", rbacDef.getIdentityViewSqlQuery().sql),
|
with("identityViewSqlPart", StringWriter.indented(2, rbacDef.getIdentityViewSqlQuery().sql)),
|
||||||
with("rawTableName", rawTableName));
|
with("rawTableName", rawTableName));
|
||||||
|
|
||||||
plPgSql.writeLn("--//");
|
plPgSql.writeLn("--//");
|
||||||
|
@ -8,13 +8,11 @@ import static net.hostsharing.hsadminng.rbac.rbacdef.StringWriter.with;
|
|||||||
public class RbacRestrictedViewGenerator {
|
public class RbacRestrictedViewGenerator {
|
||||||
private final RbacView rbacDef;
|
private final RbacView rbacDef;
|
||||||
private final String liquibaseTagPrefix;
|
private final String liquibaseTagPrefix;
|
||||||
private final String simpleEntityVarName;
|
|
||||||
private final String rawTableName;
|
private final String rawTableName;
|
||||||
|
|
||||||
public RbacRestrictedViewGenerator(final RbacView rbacDef, final String liquibaseTagPrefix) {
|
public RbacRestrictedViewGenerator(final RbacView rbacDef, final String liquibaseTagPrefix) {
|
||||||
this.rbacDef = rbacDef;
|
this.rbacDef = rbacDef;
|
||||||
this.liquibaseTagPrefix = liquibaseTagPrefix;
|
this.liquibaseTagPrefix = liquibaseTagPrefix;
|
||||||
this.simpleEntityVarName = rbacDef.getRootEntityAlias().simpleName();
|
|
||||||
this.rawTableName = rbacDef.getRootEntityAlias().getRawTableName();
|
this.rawTableName = rbacDef.getRootEntityAlias().getRawTableName();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -24,7 +22,9 @@ public class RbacRestrictedViewGenerator {
|
|||||||
--changeset ${liquibaseTagPrefix}-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
--changeset ${liquibaseTagPrefix}-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
call generateRbacRestrictedView('${rawTableName}',
|
call generateRbacRestrictedView('${rawTableName}',
|
||||||
'${orderBy}',
|
$orderBy$
|
||||||
|
${orderBy}
|
||||||
|
$orderBy$,
|
||||||
$updates$
|
$updates$
|
||||||
${updates}
|
${updates}
|
||||||
$updates$);
|
$updates$);
|
||||||
@ -32,10 +32,10 @@ public class RbacRestrictedViewGenerator {
|
|||||||
|
|
||||||
""",
|
""",
|
||||||
with("liquibaseTagPrefix", liquibaseTagPrefix),
|
with("liquibaseTagPrefix", liquibaseTagPrefix),
|
||||||
with("orderBy", rbacDef.getOrderBySqlExpression().sql),
|
with("orderBy", indented(2, rbacDef.getOrderBySqlExpression().sql)),
|
||||||
with("updates", indented(rbacDef.getUpdatableColumns().stream()
|
with("updates", indented(2, rbacDef.getUpdatableColumns().stream()
|
||||||
.map(c -> c + " = new." + c)
|
.map(c -> c + " = new." + c)
|
||||||
.collect(joining(",\n")), 2)),
|
.collect(joining(",\n")))),
|
||||||
with("rawTableName", rawTableName));
|
with("rawTableName", rawTableName));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,8 +32,10 @@ import java.util.stream.Stream;
|
|||||||
import static java.lang.reflect.Modifier.isStatic;
|
import static java.lang.reflect.Modifier.isStatic;
|
||||||
import static java.util.Arrays.stream;
|
import static java.util.Arrays.stream;
|
||||||
import static java.util.Optional.ofNullable;
|
import static java.util.Optional.ofNullable;
|
||||||
|
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL;
|
||||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR;
|
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR;
|
||||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.autoFetched;
|
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.Part.AUTO_FETCH;
|
||||||
|
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.directlyFetchedByDependsOnColumn;
|
||||||
import static org.apache.commons.lang3.StringUtils.uncapitalize;
|
import static org.apache.commons.lang3.StringUtils.uncapitalize;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@ -65,6 +67,21 @@ public class RbacView {
|
|||||||
private EntityAlias rootEntityAliasProxy;
|
private EntityAlias rootEntityAliasProxy;
|
||||||
private RbacRoleDefinition previousRoleDef;
|
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) {
|
public static <E extends RbacObject> RbacView rbacViewFor(final String alias, final Class<E> entityClass) {
|
||||||
return new RbacView(alias, entityClass);
|
return new RbacView(alias, entityClass);
|
||||||
}
|
}
|
||||||
@ -76,22 +93,71 @@ public class RbacView {
|
|||||||
entityAliases.put("global", new EntityAlias("global"));
|
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) {
|
public RbacView withUpdatableColumns(final String... columnNames) {
|
||||||
Collections.addAll(updatableColumns, columnNames);
|
Collections.addAll(updatableColumns, columnNames);
|
||||||
verifyVersionColumnExists();
|
verifyVersionColumnExists();
|
||||||
return this;
|
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) {
|
public RbacView withIdentityView(final SQL sqlExpression) {
|
||||||
this.identityViewSqlQuery = sqlExpression;
|
this.identityViewSqlQuery = sqlExpression;
|
||||||
return this;
|
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) {
|
public RbacView withRestrictedViewOrderBy(final SQL orderBySqlExpression) {
|
||||||
this.orderBySqlExpression = orderBySqlExpression;
|
this.orderBySqlExpression = orderBySqlExpression;
|
||||||
return this;
|
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) {
|
public RbacView createRole(final Role role, final Consumer<RbacRoleDefinition> with) {
|
||||||
final RbacRoleDefinition newRoleDef = findRbacRole(rootEntityAlias, role).toCreate();
|
final RbacRoleDefinition newRoleDef = findRbacRole(rootEntityAlias, role).toCreate();
|
||||||
with.accept(newRoleDef);
|
with.accept(newRoleDef);
|
||||||
@ -99,6 +165,15 @@ public class RbacView {
|
|||||||
return this;
|
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) {
|
public RbacView createSubRole(final Role role) {
|
||||||
final RbacRoleDefinition newRoleDef = findRbacRole(rootEntityAlias, role).toCreate();
|
final RbacRoleDefinition newRoleDef = findRbacRole(rootEntityAlias, role).toCreate();
|
||||||
findOrCreateGrantDef(newRoleDef, previousRoleDef).toCreate();
|
findOrCreateGrantDef(newRoleDef, previousRoleDef).toCreate();
|
||||||
@ -106,6 +181,19 @@ public class RbacView {
|
|||||||
return this;
|
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) {
|
public RbacView createSubRole(final Role role, final Consumer<RbacRoleDefinition> with) {
|
||||||
final RbacRoleDefinition newRoleDef = findRbacRole(rootEntityAlias, role).toCreate();
|
final RbacRoleDefinition newRoleDef = findRbacRole(rootEntityAlias, role).toCreate();
|
||||||
findOrCreateGrantDef(newRoleDef, previousRoleDef).toCreate();
|
findOrCreateGrantDef(newRoleDef, previousRoleDef).toCreate();
|
||||||
@ -114,10 +202,38 @@ public class RbacView {
|
|||||||
return this;
|
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) {
|
public RbacPermissionDefinition createPermission(final Permission permission) {
|
||||||
return createPermission(rootEntityAlias, 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) {
|
public RbacPermissionDefinition createPermission(final String entityAliasName, final Permission permission) {
|
||||||
return createPermission(findEntityAlias(entityAliasName), permission);
|
return createPermission(findEntityAlias(entityAliasName), permission);
|
||||||
}
|
}
|
||||||
@ -133,6 +249,32 @@ public class RbacView {
|
|||||||
return this;
|
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(
|
public <EC extends RbacObject> RbacView importRootEntityAliasProxy(
|
||||||
final String aliasName,
|
final String aliasName,
|
||||||
final Class<? extends HasUuid> entityClass,
|
final Class<? extends HasUuid> entityClass,
|
||||||
@ -141,35 +283,75 @@ public class RbacView {
|
|||||||
if (rootEntityAliasProxy != null) {
|
if (rootEntityAliasProxy != null) {
|
||||||
throw new IllegalStateException("there is already an entityAliasProxy: " + rootEntityAliasProxy);
|
throw new IllegalStateException("there is already an entityAliasProxy: " + rootEntityAliasProxy);
|
||||||
}
|
}
|
||||||
rootEntityAliasProxy = importEntityAliasImpl(aliasName, entityClass, fetchSql, dependsOnColum, false);
|
rootEntityAliasProxy = importEntityAliasImpl(aliasName, entityClass, fetchSql, dependsOnColum, false, NOT_NULL);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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(
|
public RbacView importSubEntityAlias(
|
||||||
final String aliasName, final Class<? extends HasUuid> entityClass,
|
final String aliasName, final Class<? extends HasUuid> entityClass,
|
||||||
final SQL fetchSql, final Column dependsOnColum) {
|
final SQL fetchSql, final Column dependsOnColum) {
|
||||||
importEntityAliasImpl(aliasName, entityClass, fetchSql, dependsOnColum, true);
|
importEntityAliasImpl(aliasName, entityClass, fetchSql, dependsOnColum, true, NOT_NULL);
|
||||||
return this;
|
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(
|
public RbacView importEntityAlias(
|
||||||
final String aliasName, final Class<? extends HasUuid> entityClass,
|
final String aliasName, final Class<? extends HasUuid> entityClass,
|
||||||
final Column dependsOnColum, final SQL fetchSql) {
|
final Column dependsOnColum, final SQL fetchSql, final Nullable nullable) {
|
||||||
importEntityAliasImpl(aliasName, entityClass, fetchSql, dependsOnColum, false);
|
importEntityAliasImpl(aliasName, entityClass, fetchSql, dependsOnColum, false, nullable);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: remove once it's not used in HsOffice...Entity anymore
|
||||||
public RbacView importEntityAlias(
|
public RbacView importEntityAlias(
|
||||||
final String aliasName, final Class<? extends HasUuid> entityClass,
|
final String aliasName, final Class<? extends HasUuid> entityClass,
|
||||||
final Column dependsOnColum) {
|
final Column dependsOnColum) {
|
||||||
importEntityAliasImpl(aliasName, entityClass, autoFetched(), dependsOnColum, false);
|
importEntityAliasImpl(aliasName, entityClass, directlyFetchedByDependsOnColumn(), dependsOnColum, false, null);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
private EntityAlias importEntityAliasImpl(
|
private EntityAlias importEntityAliasImpl(
|
||||||
final String aliasName, final Class<? extends HasUuid> entityClass,
|
final String aliasName, final Class<? extends HasUuid> entityClass,
|
||||||
final SQL fetchSql, final Column dependsOnColum, boolean asSubEntity) {
|
final SQL fetchSql, final Column dependsOnColum, boolean asSubEntity, final Nullable nullable) {
|
||||||
final var entityAlias = new EntityAlias(aliasName, entityClass, fetchSql, dependsOnColum, asSubEntity);
|
final var entityAlias = new EntityAlias(aliasName, entityClass, fetchSql, dependsOnColum, asSubEntity, nullable);
|
||||||
entityAliases.put(aliasName, entityAlias);
|
entityAliases.put(aliasName, entityAlias);
|
||||||
try {
|
try {
|
||||||
importAsAlias(aliasName, rbacDefinition(entityClass), asSubEntity);
|
importAsAlias(aliasName, rbacDefinition(entityClass), asSubEntity);
|
||||||
@ -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) {
|
public RbacGrantBuilder toRole(final String entityAlias, final Role role) {
|
||||||
return new RbacGrantBuilder(entityAlias, role);
|
return new RbacGrantBuilder(entityAlias, role);
|
||||||
}
|
}
|
||||||
@ -281,15 +473,19 @@ public class RbacView {
|
|||||||
return RbacView.this;
|
return RbacView.this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public RbacView grantPermission(final String entityAliasName, final Permission perm) {
|
public RbacView grantPermission(final Permission perm) {
|
||||||
final var entityAlias = findEntityAlias(entityAliasName);
|
final var forTable = rootEntityAlias.getRawTableName();
|
||||||
final var forTable = entityAlias.getRawTableName();
|
findOrCreateGrantDef(findRbacPerm(rootEntityAlias, perm, forTable), superRoleDef).toCreate();
|
||||||
findOrCreateGrantDef(findRbacPerm(entityAlias, perm, forTable), superRoleDef).toCreate();
|
|
||||||
return RbacView.this;
|
return RbacView.this;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum Nullable {
|
||||||
|
NOT_NULL, // DEFAULT
|
||||||
|
NULLABLE
|
||||||
|
}
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@EqualsAndHashCode
|
@EqualsAndHashCode
|
||||||
public class RbacGrantDefinition {
|
public class RbacGrantDefinition {
|
||||||
@ -418,6 +614,16 @@ public class RbacView {
|
|||||||
permDefs.add(this);
|
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) {
|
public RbacView grantedTo(final String entityAlias, final Role role) {
|
||||||
findOrCreateGrantDef(this, findRbacRole(entityAlias, role)).toCreate();
|
findOrCreateGrantDef(this, findRbacRole(entityAlias, role)).toCreate();
|
||||||
return RbacView.this;
|
return RbacView.this;
|
||||||
@ -448,19 +654,61 @@ public class RbacView {
|
|||||||
return this;
|
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) {
|
public RbacGrantDefinition owningUser(final RbacUserReference.UserRole userRole) {
|
||||||
return grantRoleToUser(this, findUserRef(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) {
|
public RbacGrantDefinition permission(final Permission permission) {
|
||||||
return grantPermissionToRole(createPermission(entityAlias, permission), this);
|
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) {
|
public RbacGrantDefinition incomingSuperRole(final String entityAlias, final Role role) {
|
||||||
final var incomingSuperRole = findRbacRole(entityAlias, role);
|
final var incomingSuperRole = findRbacRole(entityAlias, role);
|
||||||
return grantSubRoleToSuperRole(this, incomingSuperRole);
|
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) {
|
public RbacGrantDefinition outgoingSubRole(final String entityAlias, final Role role) {
|
||||||
final var outgoingSubRole = findRbacRole(entityAlias, role);
|
final var outgoingSubRole = findRbacRole(entityAlias, role);
|
||||||
return grantSubRoleToSuperRole(outgoingSubRole, this);
|
return grantSubRoleToSuperRole(outgoingSubRole, this);
|
||||||
@ -560,14 +808,14 @@ public class RbacView {
|
|||||||
.orElseGet(() -> new RbacGrantDefinition(subRoleDefinition, superRoleDefinition));
|
.orElseGet(() -> new RbacGrantDefinition(subRoleDefinition, superRoleDefinition));
|
||||||
}
|
}
|
||||||
|
|
||||||
record EntityAlias(String aliasName, Class<? extends RbacObject> entityClass, SQL fetchSql, Column dependsOnColum, boolean isSubEntity) {
|
record EntityAlias(String aliasName, Class<? extends RbacObject> entityClass, SQL fetchSql, Column dependsOnColum, boolean isSubEntity, Nullable nullable) {
|
||||||
|
|
||||||
public EntityAlias(final String aliasName) {
|
public EntityAlias(final String aliasName) {
|
||||||
this(aliasName, null, null, null, false);
|
this(aliasName, null, null, null, false, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public EntityAlias(final String aliasName, final Class<? extends RbacObject> entityClass) {
|
public EntityAlias(final String aliasName, final Class<? extends RbacObject> entityClass) {
|
||||||
this(aliasName, entityClass, null, null, false);
|
this(aliasName, entityClass, null, null, false, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isGlobal() {
|
boolean isGlobal() {
|
||||||
@ -592,8 +840,8 @@ public class RbacView {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasFetchSql() {
|
boolean isFetchedByDirectForeignKey() {
|
||||||
return fetchSql != null;
|
return fetchSql != null && fetchSql.part == AUTO_FETCH;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String withoutEntitySuffix(final String simpleEntityName) {
|
private String withoutEntitySuffix(final String simpleEntityName) {
|
||||||
@ -626,39 +874,35 @@ public class RbacView {
|
|||||||
return tableName.substring(0, tableName.length() - "_rv".length());
|
return tableName.substring(0, tableName.length() - "_rv".length());
|
||||||
}
|
}
|
||||||
|
|
||||||
public record Role(String roleName) {
|
public enum Role {
|
||||||
|
|
||||||
public static final Role OWNER = new Role("owner");
|
OWNER,
|
||||||
public static final Role ADMIN = new Role("admin");
|
ADMIN,
|
||||||
public static final Role AGENT = new Role("agent");
|
AGENT,
|
||||||
public static final Role TENANT = new Role("tenant");
|
TENANT,
|
||||||
public static final Role REFERRER = new Role("referrer");
|
REFERRER,
|
||||||
|
|
||||||
|
GUEST;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return ":" + roleName;
|
return ":" + roleName();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
String roleName() {
|
||||||
public boolean equals(final Object obj) {
|
return name().toLowerCase();
|
||||||
return ((obj instanceof Role) && ((Role) obj).roleName.equals(this.roleName));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public record Permission(String permission) {
|
public enum Permission {
|
||||||
|
INSERT,
|
||||||
public static final Permission INSERT = new Permission("INSERT");
|
DELETE,
|
||||||
public static final Permission DELETE = new Permission("DELETE");
|
UPDATE,
|
||||||
public static final Permission UPDATE = new Permission("UPDATE");
|
SELECT;
|
||||||
public static final Permission SELECT = new Permission("SELECT");
|
|
||||||
|
|
||||||
public static Permission custom(final String permission) {
|
|
||||||
return new Permission(permission);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return ":" + permission;
|
return ":" + name();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -666,14 +910,25 @@ public class RbacView {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* DSL method to specify an SQL SELECT expression which fetches the related entity,
|
* DSL method to specify an SQL SELECT expression which fetches the related entity,
|
||||||
* using the reference `${ref}` of the root entity.
|
* using the reference `${ref}` of the root entity and `${columns}` for the projection.
|
||||||
* `${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>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 ';)
|
* @param sql an SQL SELECT expression (not ending with ';)
|
||||||
* @return the wrapped SQL expression
|
* @return the wrapped SQL expression
|
||||||
*/
|
*/
|
||||||
public static SQL fetchedBySql(final String sql) {
|
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);
|
validateExpression(sql);
|
||||||
return new SQL(sql, Part.SQL_QUERY);
|
return new SQL(sql, Part.SQL_QUERY);
|
||||||
}
|
}
|
||||||
@ -685,8 +940,8 @@ public class RbacView {
|
|||||||
*
|
*
|
||||||
* @return the wrapped SQL definition object
|
* @return the wrapped SQL definition object
|
||||||
*/
|
*/
|
||||||
public static SQL autoFetched() {
|
public static SQL directlyFetchedByDependsOnColumn() {
|
||||||
return new SQL(null, Part.AUTO_FETCH);
|
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) {
|
public static void main(String[] args) {
|
||||||
Stream.of(
|
Stream.of(
|
||||||
TestCustomerEntity.class,
|
TestCustomerEntity.class,
|
||||||
@ -810,21 +1085,6 @@ public class RbacView {
|
|||||||
HsOfficeSepaMandateEntity.class,
|
HsOfficeSepaMandateEntity.class,
|
||||||
HsOfficeCoopSharesTransactionEntity.class,
|
HsOfficeCoopSharesTransactionEntity.class,
|
||||||
HsOfficeMembershipEntity.class
|
HsOfficeMembershipEntity.class
|
||||||
).forEach(c -> {
|
).forEach(RbacView::generateRbacView);
|
||||||
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());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@ import lombok.SneakyThrows;
|
|||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import java.nio.file.*;
|
import java.nio.file.*;
|
||||||
import java.time.LocalDateTime;
|
|
||||||
|
|
||||||
import static java.util.stream.Collectors.joining;
|
import static java.util.stream.Collectors.joining;
|
||||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacGrantDefinition.GrantType.*;
|
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacGrantDefinition.GrantType.*;
|
||||||
@ -149,14 +148,13 @@ public class RbacViewMermaidFlowchartGenerator {
|
|||||||
"""
|
"""
|
||||||
### rbac %{entityAlias}
|
### rbac %{entityAlias}
|
||||||
|
|
||||||
This code generated was by RbacViewMermaidFlowchartGenerator at %{timestamp}.
|
This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually.
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
%{flowchart}
|
%{flowchart}
|
||||||
```
|
```
|
||||||
"""
|
"""
|
||||||
.replace("%{entityAlias}", rbacDef.getRootEntityAlias().aliasName())
|
.replace("%{entityAlias}", rbacDef.getRootEntityAlias().aliasName())
|
||||||
.replace("%{timestamp}", LocalDateTime.now().toString())
|
|
||||||
.replace("%{flowchart}", flowchart.toString()),
|
.replace("%{flowchart}", flowchart.toString()),
|
||||||
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
|
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
|
||||||
System.out.println("Markdown-File: " + path.toAbsolutePath());
|
System.out.println("Markdown-File: " + path.toAbsolutePath());
|
||||||
|
@ -5,7 +5,6 @@ import lombok.SneakyThrows;
|
|||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.StandardOpenOption;
|
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.PostgresTriggerReference.NEW;
|
||||||
import static net.hostsharing.hsadminng.rbac.rbacdef.StringWriter.with;
|
import static net.hostsharing.hsadminng.rbac.rbacdef.StringWriter.with;
|
||||||
@ -21,10 +20,9 @@ public class RbacViewPostgresGenerator {
|
|||||||
liqibaseTagPrefix = rbacDef.getRootEntityAlias().getRawTableName().replace("_", "-");
|
liqibaseTagPrefix = rbacDef.getRootEntityAlias().getRawTableName().replace("_", "-");
|
||||||
plPgSql.writeLn("""
|
plPgSql.writeLn("""
|
||||||
--liquibase formatted sql
|
--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("generator", getClass().getSimpleName()),
|
||||||
with("timestamp", LocalDateTime.now().toString()),
|
|
||||||
with("ref", NEW.name()));
|
with("ref", NEW.name()));
|
||||||
|
|
||||||
new RbacObjectGenerator(rbacDef, liqibaseTagPrefix).generateTo(plPgSql);
|
new RbacObjectGenerator(rbacDef, liqibaseTagPrefix).generateTo(plPgSql);
|
||||||
@ -37,7 +35,10 @@ public class RbacViewPostgresGenerator {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return plPgSql.toString();
|
return plPgSql.toString()
|
||||||
|
.replace("\n\n\n", "\n\n")
|
||||||
|
.replace("-- ====", "\n-- ====")
|
||||||
|
.replace("\n\n--//", "\n--//");
|
||||||
}
|
}
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
|
@ -7,6 +7,7 @@ import java.util.List;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static java.util.Optional.ofNullable;
|
||||||
import static java.util.stream.Collectors.joining;
|
import static java.util.stream.Collectors.joining;
|
||||||
import static java.util.stream.Collectors.toSet;
|
import static java.util.stream.Collectors.toSet;
|
||||||
import static net.hostsharing.hsadminng.rbac.rbacdef.PostgresTriggerReference.NEW;
|
import static net.hostsharing.hsadminng.rbac.rbacdef.PostgresTriggerReference.NEW;
|
||||||
@ -82,6 +83,7 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
plPgSql.writeLn("begin");
|
plPgSql.writeLn("begin");
|
||||||
plPgSql.indented(() -> {
|
plPgSql.indented(() -> {
|
||||||
plPgSql.writeLn("call enterTriggerForObjectUuid(NEW.uuid);");
|
plPgSql.writeLn("call enterTriggerForObjectUuid(NEW.uuid);");
|
||||||
|
plPgSql.writeLn();
|
||||||
generateCreateRolesAndGrantsAfterInsert(plPgSql);
|
generateCreateRolesAndGrantsAfterInsert(plPgSql);
|
||||||
plPgSql.ensureSingleEmptyLine();
|
plPgSql.ensureSingleEmptyLine();
|
||||||
plPgSql.writeLn("call leaveTriggerForObjectUuid(NEW.uuid);");
|
plPgSql.writeLn("call leaveTriggerForObjectUuid(NEW.uuid);");
|
||||||
@ -90,6 +92,37 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
plPgSql.writeLn();
|
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) {
|
private void generateUpdateTriggerFunction(final StringWriter plPgSql) {
|
||||||
plPgSql.writeLn("""
|
plPgSql.writeLn("""
|
||||||
/*
|
/*
|
||||||
@ -109,7 +142,7 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
|
|
||||||
plPgSql.chopEmptyLines();
|
plPgSql.chopEmptyLines();
|
||||||
plPgSql.indented(() -> {
|
plPgSql.indented(() -> {
|
||||||
updatableEntityAliases()
|
referencedEntityAliases()
|
||||||
.forEach((ea) -> {
|
.forEach((ea) -> {
|
||||||
plPgSql.writeLn(entityRefVar(OLD, ea) + " " + ea.getRawTableName() + ";");
|
plPgSql.writeLn(entityRefVar(OLD, ea) + " " + ea.getRawTableName() + ";");
|
||||||
plPgSql.writeLn(entityRefVar(NEW, ea) + " " + ea.getRawTableName() + ";");
|
plPgSql.writeLn(entityRefVar(NEW, ea) + " " + ea.getRawTableName() + ";");
|
||||||
@ -120,6 +153,7 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
plPgSql.writeLn("begin");
|
plPgSql.writeLn("begin");
|
||||||
plPgSql.indented(() -> {
|
plPgSql.indented(() -> {
|
||||||
plPgSql.writeLn("call enterTriggerForObjectUuid(NEW.uuid);");
|
plPgSql.writeLn("call enterTriggerForObjectUuid(NEW.uuid);");
|
||||||
|
plPgSql.writeLn();
|
||||||
generateUpdateRolesAndGrantsAfterUpdate(plPgSql);
|
generateUpdateRolesAndGrantsAfterUpdate(plPgSql);
|
||||||
plPgSql.ensureSingleEmptyLine();
|
plPgSql.ensureSingleEmptyLine();
|
||||||
plPgSql.writeLn("call leaveTriggerForObjectUuid(NEW.uuid);");
|
plPgSql.writeLn("call leaveTriggerForObjectUuid(NEW.uuid);");
|
||||||
@ -132,11 +166,18 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
return updatableEntityAliases().anyMatch(e -> true);
|
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) {
|
private void generateCreateRolesAndGrantsAfterInsert(final StringWriter plPgSql) {
|
||||||
referencedEntityAliases()
|
referencedEntityAliases()
|
||||||
.forEach((ea) -> plPgSql.writeLn(
|
.forEach((ea) -> {
|
||||||
ea.fetchSql().sql + " into " + entityRefVar(NEW, ea) + ";",
|
generateFetchedVars(plPgSql, ea, NEW);
|
||||||
with("ref", NEW.name())));
|
plPgSql.writeLn();
|
||||||
|
});
|
||||||
|
|
||||||
createRolesWithGrantsSql(plPgSql, OWNER);
|
createRolesWithGrantsSql(plPgSql, OWNER);
|
||||||
createRolesWithGrantsSql(plPgSql, ADMIN);
|
createRolesWithGrantsSql(plPgSql, ADMIN);
|
||||||
@ -165,14 +206,11 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
private void generateUpdateRolesAndGrantsAfterUpdate(final StringWriter plPgSql) {
|
private void generateUpdateRolesAndGrantsAfterUpdate(final StringWriter plPgSql) {
|
||||||
plPgSql.ensureSingleEmptyLine();
|
plPgSql.ensureSingleEmptyLine();
|
||||||
|
|
||||||
updatableEntityAliases()
|
referencedEntityAliases()
|
||||||
.forEach((ea) -> {
|
.forEach((ea) -> {
|
||||||
plPgSql.writeLn(
|
generateFetchedVars(plPgSql, ea, OLD);
|
||||||
ea.fetchSql().sql + " into " + entityRefVar(OLD, ea) + ";",
|
generateFetchedVars(plPgSql, ea, NEW);
|
||||||
with("ref", OLD.name()));
|
plPgSql.writeLn();
|
||||||
plPgSql.writeLn(
|
|
||||||
ea.fetchSql().sql + " into " + entityRefVar(NEW, ea) + ";",
|
|
||||||
with("ref", NEW.name()));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
updatableEntityAliases()
|
updatableEntityAliases()
|
||||||
@ -190,14 +228,29 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isUpdatable(final RbacView.Column c) {
|
private void generateFetchedVars(
|
||||||
return rbacDef.getUpdatableColumns().contains(c);
|
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) {
|
private void updateGrantsDependingOn(final StringWriter plPgSql, final String columnName) {
|
||||||
rbacDef.getGrantDefs().stream()
|
rbacDef.getGrantDefs().stream()
|
||||||
.filter(RbacView.RbacGrantDefinition::isToCreate)
|
.filter(RbacView.RbacGrantDefinition::isToCreate)
|
||||||
.filter(g -> g.dependsOnColumn(columnName))
|
.filter(g -> g.dependsOnColumn(columnName))
|
||||||
|
.filter(g -> !isInsertPermissionGrant(g))
|
||||||
.forEach(g -> {
|
.forEach(g -> {
|
||||||
plPgSql.ensureSingleEmptyLine();
|
plPgSql.ensureSingleEmptyLine();
|
||||||
plPgSql.writeLn(generateRevoke(g));
|
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) {
|
private void generateGrants(final StringWriter plPgSql, final RbacView.RbacGrantDefinition.GrantType grantType) {
|
||||||
plPgSql.ensureSingleEmptyLine();
|
plPgSql.ensureSingleEmptyLine();
|
||||||
rbacGrants.stream()
|
rbacGrants.stream()
|
||||||
@ -222,7 +280,7 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
.replace("${subRoleRef}", roleRef(OLD, grantDef.getSubRoleDef()))
|
.replace("${subRoleRef}", roleRef(OLD, grantDef.getSubRoleDef()))
|
||||||
.replace("${superRoleRef}", roleRef(OLD, grantDef.getSuperRoleDef()));
|
.replace("${superRoleRef}", roleRef(OLD, grantDef.getSuperRoleDef()));
|
||||||
case PERM_TO_ROLE -> "call revokePermissionFromRole(${permRef}, ${superRoleRef});"
|
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()));
|
.replace("${superRoleRef}", roleRef(OLD, grantDef.getSuperRoleDef()));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -246,6 +304,10 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
return permRef("findPermissionId", ref, permDef);
|
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) {
|
private String createPerm(final PostgresTriggerReference ref, final RbacPermissionDefinition permDef) {
|
||||||
return permRef("createPermission", ref, permDef);
|
return permRef("createPermission", ref, permDef);
|
||||||
}
|
}
|
||||||
@ -256,7 +318,7 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
.replace("${entityRef}", rbacDef.isRootEntityAlias(permDef.entityAlias)
|
.replace("${entityRef}", rbacDef.isRootEntityAlias(permDef.entityAlias)
|
||||||
? ref.name()
|
? ref.name()
|
||||||
: refVarName(ref, permDef.entityAlias))
|
: refVarName(ref, permDef.entityAlias))
|
||||||
.replace("${perm}", permDef.permission.permission());
|
.replace("${perm}", permDef.permission.name());
|
||||||
}
|
}
|
||||||
|
|
||||||
private String refVarName(final PostgresTriggerReference ref, final RbacView.EntityAlias entityAlias) {
|
private String refVarName(final PostgresTriggerReference ref, final RbacView.EntityAlias entityAlias) {
|
||||||
@ -301,12 +363,12 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
|
|
||||||
generatePermissionsForRole(plPgSql, role);
|
generatePermissionsForRole(plPgSql, role);
|
||||||
|
|
||||||
generateUserGrantsForRole(plPgSql, role);
|
|
||||||
|
|
||||||
generateIncomingSuperRolesForRole(plPgSql, role);
|
generateIncomingSuperRolesForRole(plPgSql, role);
|
||||||
|
|
||||||
generateOutgoingSubRolesForRole(plPgSql, role);
|
generateOutgoingSubRolesForRole(plPgSql, role);
|
||||||
|
|
||||||
|
generateUserGrantsForRole(plPgSql, role);
|
||||||
|
|
||||||
plPgSql.chopTail(",\n");
|
plPgSql.chopTail(",\n");
|
||||||
plPgSql.writeLn();
|
plPgSql.writeLn();
|
||||||
});
|
});
|
||||||
@ -333,7 +395,7 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
final var arrayElements = permissionGrantsForRole.stream()
|
final var arrayElements = permissionGrantsForRole.stream()
|
||||||
.map(RbacView.RbacGrantDefinition::getPermDef)
|
.map(RbacView.RbacGrantDefinition::getPermDef)
|
||||||
.map(RbacPermissionDefinition::getPermission)
|
.map(RbacPermissionDefinition::getPermission)
|
||||||
.map(RbacView.Permission::permission)
|
.map(RbacView.Permission::name)
|
||||||
.map(p -> "'" + p + "'")
|
.map(p -> "'" + p + "'")
|
||||||
.sorted()
|
.sorted()
|
||||||
.toList();
|
.toList();
|
||||||
@ -348,7 +410,7 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
if (!incomingGrants.isEmpty()) {
|
if (!incomingGrants.isEmpty()) {
|
||||||
final var arrayElements = incomingGrants.stream()
|
final var arrayElements = incomingGrants.stream()
|
||||||
.map(g -> toPlPgSqlReference(NEW, g.getSuperRoleDef(), g.isAssumed()))
|
.map(g -> toPlPgSqlReference(NEW, g.getSuperRoleDef(), g.isAssumed()))
|
||||||
.toList();
|
.sorted().toList();
|
||||||
plPgSql.indented(() ->
|
plPgSql.indented(() ->
|
||||||
plPgSql.writeLn("incomingSuperRoles => array[" + joinArrayElements(arrayElements, 1) + "],\n"));
|
plPgSql.writeLn("incomingSuperRoles => array[" + joinArrayElements(arrayElements, 1) + "],\n"));
|
||||||
rbacGrants.removeAll(incomingGrants);
|
rbacGrants.removeAll(incomingGrants);
|
||||||
@ -360,7 +422,7 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
if (!outgoingGrants.isEmpty()) {
|
if (!outgoingGrants.isEmpty()) {
|
||||||
final var arrayElements = outgoingGrants.stream()
|
final var arrayElements = outgoingGrants.stream()
|
||||||
.map(g -> toPlPgSqlReference(NEW, g.getSubRoleDef(), g.isAssumed()))
|
.map(g -> toPlPgSqlReference(NEW, g.getSubRoleDef(), g.isAssumed()))
|
||||||
.toList();
|
.sorted().toList();
|
||||||
plPgSql.indented(() ->
|
plPgSql.indented(() ->
|
||||||
plPgSql.writeLn("outgoingSubRoles => array[" + joinArrayElements(arrayElements, 1) + "],\n"));
|
plPgSql.writeLn("outgoingSubRoles => array[" + joinArrayElements(arrayElements, 1) + "],\n"));
|
||||||
rbacGrants.removeAll(outgoingGrants);
|
rbacGrants.removeAll(outgoingGrants);
|
||||||
@ -444,7 +506,11 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
private void generateUpdateTrigger(final StringWriter plPgSql) {
|
private void generateUpdateTrigger(final StringWriter plPgSql) {
|
||||||
|
|
||||||
generateHeader(plPgSql, "update");
|
generateHeader(plPgSql, "update");
|
||||||
|
if ( hasAnyUpdatableAndNullableEntityAliases() ) {
|
||||||
|
generateSimplifiedUpdateTriggerFunction(plPgSql);
|
||||||
|
} else {
|
||||||
generateUpdateTriggerFunction(plPgSql);
|
generateUpdateTriggerFunction(plPgSql);
|
||||||
|
}
|
||||||
|
|
||||||
plPgSql.writeLn("""
|
plPgSql.writeLn("""
|
||||||
/*
|
/*
|
||||||
|
@ -38,12 +38,26 @@ public class StringWriter {
|
|||||||
--indentLevel;
|
--indentLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void indent(int levels) {
|
||||||
|
indentLevel += levels;
|
||||||
|
}
|
||||||
|
|
||||||
|
void unindent(int levels) {
|
||||||
|
indentLevel -= levels;
|
||||||
|
}
|
||||||
|
|
||||||
void indented(final Runnable indented) {
|
void indented(final Runnable indented) {
|
||||||
indent();
|
indent();
|
||||||
indented.run();
|
indented.run();
|
||||||
unindent();
|
unindent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void indented(int levels, final Runnable indented) {
|
||||||
|
indent(levels);
|
||||||
|
indented.run();
|
||||||
|
unindent(levels);
|
||||||
|
}
|
||||||
|
|
||||||
boolean chopTail(final String tail) {
|
boolean chopTail(final String tail) {
|
||||||
if (string.toString().endsWith(tail)) {
|
if (string.toString().endsWith(tail)) {
|
||||||
string.setLength(string.length() - tail.length());
|
string.setLength(string.length() - tail.length());
|
||||||
@ -68,7 +82,7 @@ public class StringWriter {
|
|||||||
return string.toString();
|
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 indentation = StringUtils.repeat(" ", indentLevel);
|
||||||
final var indented = stream(text.split("\n"))
|
final var indented = stream(text.split("\n"))
|
||||||
.map(line -> line.trim().isBlank() ? "" : indentation + line)
|
.map(line -> line.trim().isBlank() ? "" : indentation + line)
|
||||||
@ -80,7 +94,7 @@ public class StringWriter {
|
|||||||
if ( indentLevel == 0) {
|
if ( indentLevel == 0) {
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
return indented(text, indentLevel);
|
return indented(indentLevel, text);
|
||||||
}
|
}
|
||||||
|
|
||||||
record VarDef(String name, String value){}
|
record VarDef(String name, String value){}
|
||||||
@ -95,7 +109,6 @@ public class StringWriter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String apply(final String textToAppend) {
|
String apply(final String textToAppend) {
|
||||||
try {
|
|
||||||
text = textToAppend;
|
text = textToAppend;
|
||||||
stream(varDefs).forEach(varDef -> {
|
stream(varDefs).forEach(varDef -> {
|
||||||
final var pattern = Pattern.compile("\\$\\{" + varDef.name() + "}", Pattern.CASE_INSENSITIVE);
|
final var pattern = Pattern.compile("\\$\\{" + varDef.name() + "}", Pattern.CASE_INSENSITIVE);
|
||||||
@ -103,9 +116,6 @@ public class StringWriter {
|
|||||||
text = matcher.replaceAll(varDef.value());
|
text = matcher.replaceAll(varDef.value());
|
||||||
});
|
});
|
||||||
return text;
|
return text;
|
||||||
} catch (Exception exc) {
|
|
||||||
throw exc;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -94,4 +94,17 @@ public class RbacGrantController implements RbacGrantsApi {
|
|||||||
|
|
||||||
return ResponseEntity.noContent().build();
|
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
|
@Service
|
||||||
public class RbacGrantsDiagramService {
|
public class RbacGrantsDiagramService {
|
||||||
|
|
||||||
|
private static final int GRANT_LIMIT = 500;
|
||||||
|
|
||||||
public static void writeToFile(final String title, final String graph, final String fileName) {
|
public static void writeToFile(final String title, final String graph, final String fileName) {
|
||||||
|
|
||||||
try (BufferedWriter writer = new BufferedWriter(new FileWriter(fileName))) {
|
try (BufferedWriter writer = new BufferedWriter(new FileWriter(fileName))) {
|
||||||
@ -42,7 +44,11 @@ public class RbacGrantsDiagramService {
|
|||||||
PERMISSIONS,
|
PERMISSIONS,
|
||||||
NOT_ASSUMED,
|
NOT_ASSUMED,
|
||||||
TEST_ENTITIES,
|
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
|
@Autowired
|
||||||
@ -55,7 +61,7 @@ public class RbacGrantsDiagramService {
|
|||||||
private EntityManager em;
|
private EntityManager em;
|
||||||
|
|
||||||
public String allGrantsToCurrentUser(final EnumSet<Include> includes) {
|
public String allGrantsToCurrentUser(final EnumSet<Include> includes) {
|
||||||
final var graph = new HashSet<RawRbacGrantEntity>();
|
final var graph = new LimitedHashSet<RawRbacGrantEntity>();
|
||||||
for ( UUID subjectUuid: context.currentSubjectsUuids() ) {
|
for ( UUID subjectUuid: context.currentSubjectsUuids() ) {
|
||||||
traverseGrantsTo(graph, subjectUuid, includes);
|
traverseGrantsTo(graph, subjectUuid, includes);
|
||||||
}
|
}
|
||||||
@ -88,7 +94,7 @@ public class RbacGrantsDiagramService {
|
|||||||
.setParameter("targetObject", targetObject)
|
.setParameter("targetObject", targetObject)
|
||||||
.setParameter("op", op)
|
.setParameter("op", op)
|
||||||
.getSingleResult();
|
.getSingleResult();
|
||||||
final var graph = new HashSet<RawRbacGrantEntity>();
|
final var graph = new LimitedHashSet<RawRbacGrantEntity>();
|
||||||
traverseGrantsFrom(graph, refUuid, includes);
|
traverseGrantsFrom(graph, refUuid, includes);
|
||||||
return toMermaidFlowchart(graph, includes);
|
return toMermaidFlowchart(graph, includes);
|
||||||
}
|
}
|
||||||
@ -116,7 +122,7 @@ public class RbacGrantsDiagramService {
|
|||||||
)
|
)
|
||||||
.collect(groupingBy(RbacGrantsDiagramService::renderEntityIdName))
|
.collect(groupingBy(RbacGrantsDiagramService::renderEntityIdName))
|
||||||
.entrySet().stream()
|
.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()
|
+ entity.getValue().stream()
|
||||||
.map(n -> renderNode(n.idName(), n.uuid()).replace("\n", "\n "))
|
.map(n -> renderNode(n.idName(), n.uuid()).replace("\n", "\n "))
|
||||||
.sorted()
|
.sorted()
|
||||||
@ -127,14 +133,15 @@ public class RbacGrantsDiagramService {
|
|||||||
: "";
|
: "";
|
||||||
|
|
||||||
final var grants = graph.stream()
|
final var grants = graph.stream()
|
||||||
.map(g -> quoted(g.getAscendantIdName())
|
.map(g -> cleanId(g.getAscendantIdName())
|
||||||
+ " -->" + (g.isAssumed() ? " " : "|XX| ")
|
+ " -->" + (g.isAssumed() ? " " : "|XX| ")
|
||||||
+ quoted(g.getDescendantIdName()))
|
+ cleanId(g.getDescendantIdName()))
|
||||||
.sorted()
|
.sorted()
|
||||||
.collect(joining("\n"));
|
.collect(joining("\n"));
|
||||||
|
|
||||||
final var avoidCroppedNodeLabels = "%%{init:{'flowchart':{'htmlLabels':false}}}%%\n\n";
|
final var avoidCroppedNodeLabels = "%%{init:{'flowchart':{'htmlLabels':false}}}%%\n\n";
|
||||||
return (includes.contains(DETAILS) ? avoidCroppedNodeLabels : "")
|
return (includes.contains(DETAILS) ? avoidCroppedNodeLabels : "")
|
||||||
|
+ (graph.size() >= GRANT_LIMIT ? "%% too many grants, graph is cropped\n" : "")
|
||||||
+ "flowchart TB\n\n"
|
+ "flowchart TB\n\n"
|
||||||
+ entities
|
+ entities
|
||||||
+ grants;
|
+ grants;
|
||||||
@ -151,7 +158,7 @@ public class RbacGrantsDiagramService {
|
|||||||
// }
|
// }
|
||||||
// return "[" + table + "\n" + entity + "]";
|
// return "[" + table + "\n" + entity + "]";
|
||||||
// }
|
// }
|
||||||
return "[" + entityId + "]";
|
return "[" + cleanId(entityId) + "]";
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String renderEntityIdName(final Node node) {
|
private static String renderEntityIdName(final Node node) {
|
||||||
@ -170,7 +177,7 @@ public class RbacGrantsDiagramService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String renderNode(final String idName, final UUID uuid) {
|
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) {
|
private String renderNodeContent(final String idName, final UUID uuid) {
|
||||||
@ -196,9 +203,24 @@ public class RbacGrantsDiagramService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private static String quoted(final String idName) {
|
private static String cleanId(final String idName) {
|
||||||
return idName.replace(" ", ":").replaceAll("@.*", "");
|
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) {
|
record Node(String idName, UUID uuid) {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
package net.hostsharing.hsadminng.rbac.rbacrole;
|
package net.hostsharing.hsadminng.rbac.rbacrole;
|
||||||
|
|
||||||
public enum RbacRoleType {
|
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"))
|
.withIdentityView(SQL.projection("prefix"))
|
||||||
.withRestrictedViewOrderBy(SQL.expression("reference"))
|
.withRestrictedViewOrderBy(SQL.expression("reference"))
|
||||||
.withUpdatableColumns("reference", "prefix", "adminUserName")
|
.withUpdatableColumns("reference", "prefix", "adminUserName")
|
||||||
// TODO: do we want explicit specification of parent-independent insert permissions?
|
.toRole("global", ADMIN).grantPermission(INSERT)
|
||||||
// .toRole("global", ADMIN).grantPermission("customer", INSERT)
|
|
||||||
|
|
||||||
.createRole(OWNER, (with) -> {
|
.createRole(OWNER, (with) -> {
|
||||||
with.owningUser(CREATOR).unassumed();
|
with.owningUser(CREATOR).unassumed();
|
||||||
|
@ -14,9 +14,10 @@ import java.io.IOException;
|
|||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
|
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
|
||||||
|
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.Permission.*;
|
||||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
|
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
|
||||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.fetchedBySql;
|
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.directlyFetchedByDependsOnColumn;
|
||||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
|
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@ -49,11 +50,9 @@ public class TestDomainEntity implements HasUuid {
|
|||||||
|
|
||||||
.importEntityAlias("package", TestPackageEntity.class,
|
.importEntityAlias("package", TestPackageEntity.class,
|
||||||
dependsOnColumn("packageUuid"),
|
dependsOnColumn("packageUuid"),
|
||||||
fetchedBySql("""
|
directlyFetchedByDependsOnColumn(),
|
||||||
SELECT * FROM test_package p
|
NOT_NULL)
|
||||||
WHERE p.uuid= ${ref}.packageUuid
|
.toRole("package", ADMIN).grantPermission(INSERT)
|
||||||
"""))
|
|
||||||
.toRole("package", ADMIN).grantPermission("domain", INSERT)
|
|
||||||
|
|
||||||
.createRole(OWNER, (with) -> {
|
.createRole(OWNER, (with) -> {
|
||||||
with.incomingSuperRole("package", ADMIN);
|
with.incomingSuperRole("package", ADMIN);
|
||||||
|
@ -14,6 +14,7 @@ import java.io.IOException;
|
|||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
|
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
|
||||||
|
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.Permission.*;
|
||||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
|
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
|
||||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.*;
|
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.*;
|
||||||
@ -50,11 +51,9 @@ public class TestPackageEntity implements HasUuid {
|
|||||||
|
|
||||||
.importEntityAlias("customer", TestCustomerEntity.class,
|
.importEntityAlias("customer", TestCustomerEntity.class,
|
||||||
dependsOnColumn("customerUuid"),
|
dependsOnColumn("customerUuid"),
|
||||||
fetchedBySql("""
|
directlyFetchedByDependsOnColumn(),
|
||||||
SELECT * FROM test_customer c
|
NOT_NULL)
|
||||||
WHERE c.uuid= ${ref}.customerUuid
|
.toRole("customer", ADMIN).grantPermission(INSERT)
|
||||||
"""))
|
|
||||||
.toRole("customer", ADMIN).grantPermission("package", INSERT)
|
|
||||||
|
|
||||||
.createRole(OWNER, (with) -> {
|
.createRole(OWNER, (with) -> {
|
||||||
with.incomingSuperRole("customer", ADMIN);
|
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
|
declare
|
||||||
cleanIdentifier varchar;
|
cleanIdentifier varchar;
|
||||||
begin
|
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');
|
cleanIdentifier := regexp_replace(rawIdentifier, '[^A-Za-z0-9\-._:]+', '', 'g');
|
||||||
return cleanIdentifier;
|
return cleanIdentifier;
|
||||||
end; $$;
|
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
|
create table RbacRole
|
||||||
(
|
(
|
||||||
@ -373,10 +373,12 @@ create table RbacPermission
|
|||||||
uuid uuid primary key references RbacReference (uuid) on delete cascade,
|
uuid uuid primary key references RbacReference (uuid) on delete cascade,
|
||||||
objectUuid uuid not null references RbacObject,
|
objectUuid uuid not null references RbacObject,
|
||||||
op RbacOp not null,
|
op RbacOp not null,
|
||||||
opTableName varchar(60),
|
opTableName varchar(60)
|
||||||
unique (objectUuid, op)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
ALTER TABLE RbacPermission
|
||||||
|
ADD CONSTRAINT RbacPermission_uc UNIQUE NULLS NOT DISTINCT (objectUuid, op, opTableName);
|
||||||
|
|
||||||
call create_journal('RbacPermission');
|
call create_journal('RbacPermission');
|
||||||
|
|
||||||
create or replace function createPermission(forObjectUuid uuid, forOp RbacOp, forOpTableName text = null)
|
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
|
raise exception 'forOpTableName must only be specified for ops: [INSERT]'; -- currently no other
|
||||||
end if;
|
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
|
if (permissionUuid is null) then
|
||||||
insert into RbacReference ("type")
|
insert into RbacReference ("type")
|
||||||
values ('RbacPermission')
|
values ('RbacPermission')
|
||||||
@ -466,8 +471,44 @@ select uuid
|
|||||||
and p.op = forOp
|
and p.op = forOp
|
||||||
and p.opTableName = forOpTableName
|
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:--//
|
--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 $$
|
language plpgsql as $$
|
||||||
begin
|
begin
|
||||||
perform assertReferenceType('roleId (ascendant)', roleUuid, 'RbacRole');
|
perform assertReferenceType('roleId (ascendant)', roleUuid, 'RbacRole');
|
||||||
@ -601,10 +642,10 @@ begin
|
|||||||
end;
|
end;
|
||||||
$$;
|
$$;
|
||||||
|
|
||||||
create or replace procedure grantPermissionToRole(roleDesc RbacRoleDescriptor, permissionUuid uuid)
|
create or replace procedure grantPermissionToRole(permissionUuid uuid, roleDesc RbacRoleDescriptor)
|
||||||
language plpgsql as $$
|
language plpgsql as $$
|
||||||
begin
|
begin
|
||||||
call grantPermissionToRole(findRoleId(roleDesc), permissionUuid);
|
call grantPermissionToRole(permissionUuid, findRoleId(roleDesc));
|
||||||
end;
|
end;
|
||||||
$$;
|
$$;
|
||||||
|
|
||||||
@ -634,7 +675,7 @@ begin
|
|||||||
perform assertReferenceType('subRoleId (descendant)', subRoleId, 'RbacRole');
|
perform assertReferenceType('subRoleId (descendant)', subRoleId, 'RbacRole');
|
||||||
|
|
||||||
if isGranted(subRoleId, superRoleId) then
|
if isGranted(subRoleId, superRoleId) then
|
||||||
raise exception '[400] Cyclic role grant detected between % and %', subRoleId, superRoleId;
|
call raiseDuplicateRoleGrantException(subRoleId, superRoleId);
|
||||||
end if;
|
end if;
|
||||||
|
|
||||||
insert
|
insert
|
||||||
@ -650,6 +691,11 @@ declare
|
|||||||
superRoleId uuid;
|
superRoleId uuid;
|
||||||
subRoleId uuid;
|
subRoleId uuid;
|
||||||
begin
|
begin
|
||||||
|
-- TODO: maybe separate method grantRoleToRoleIfNotNull(...) for NULLABLE references
|
||||||
hsh-michaelhoennig marked this conversation as resolved
Outdated
|
|||||||
|
if superRole.objectUuid is null or subRole.objectuuid is null then
|
||||||
|
return;
|
||||||
|
end if;
|
||||||
|
|
||||||
superRoleId := findRoleId(superRole);
|
superRoleId := findRoleId(superRole);
|
||||||
subRoleId := findRoleId(subRole);
|
subRoleId := findRoleId(subRole);
|
||||||
|
|
||||||
@ -657,7 +703,7 @@ begin
|
|||||||
perform assertReferenceType('subRoleId (descendant)', subRoleId, 'RbacRole');
|
perform assertReferenceType('subRoleId (descendant)', subRoleId, 'RbacRole');
|
||||||
|
|
||||||
if isGranted(subRoleId, superRoleId) then
|
if isGranted(subRoleId, superRoleId) then
|
||||||
raise exception '[400] Cyclic role grant detected between % and %', subRoleId, superRoleId;
|
call raiseDuplicateRoleGrantException(subRoleId, superRoleId);
|
||||||
end if;
|
end if;
|
||||||
|
|
||||||
insert
|
insert
|
||||||
@ -672,6 +718,7 @@ declare
|
|||||||
superRoleId uuid;
|
superRoleId uuid;
|
||||||
subRoleId uuid;
|
subRoleId uuid;
|
||||||
begin
|
begin
|
||||||
|
if ( superRoleId is null ) then return; end if;
|
||||||
superRoleId := findRoleId(superRole);
|
superRoleId := findRoleId(superRole);
|
||||||
if ( subRoleId is null ) then return; end if;
|
if ( subRoleId is null ) then return; end if;
|
||||||
subRoleId := findRoleId(subRole);
|
subRoleId := findRoleId(subRole);
|
||||||
@ -680,7 +727,7 @@ begin
|
|||||||
perform assertReferenceType('subRoleId (descendant)', subRoleId, 'RbacRole');
|
perform assertReferenceType('subRoleId (descendant)', subRoleId, 'RbacRole');
|
||||||
|
|
||||||
if isGranted(subRoleId, superRoleId) then
|
if isGranted(subRoleId, superRoleId) then
|
||||||
raise exception '[400] Cyclic role grant detected between % and %', subRoleId, superRoleId;
|
call raiseDuplicateRoleGrantException(subRoleId, superRoleId);
|
||||||
end if;
|
end if;
|
||||||
|
|
||||||
insert
|
insert
|
||||||
@ -704,11 +751,39 @@ begin
|
|||||||
if (isGranted(superRoleId, subRoleId)) then
|
if (isGranted(superRoleId, subRoleId)) then
|
||||||
delete from RbacGrants where ascendantUuid = superRoleId and descendantUuid = subRoleId;
|
delete from RbacGrants where ascendantUuid = superRoleId and descendantUuid = subRoleId;
|
||||||
else
|
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;
|
subRole, subRoleId, superRole, superRoleId;
|
||||||
end if;
|
end if;
|
||||||
end; $$;
|
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:--//
|
--changeset rbac-base-QUERY-ACCESSIBLE-OBJECT-UUIDS:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
|
@ -56,14 +56,17 @@ begin
|
|||||||
roleTypeToAssume = split_part(roleNameParts, '#', 3);
|
roleTypeToAssume = split_part(roleNameParts, '#', 3);
|
||||||
|
|
||||||
objectUuidToAssume = findObjectUuidByIdName(objectTableToAssume, objectNameToAssume);
|
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
|
from RbacRole r
|
||||||
where r.objectUuid = objectUuidToAssume
|
where r.objectUuid = objectUuidToAssume
|
||||||
and r.roleType = roleTypeToAssume
|
and r.roleType = roleTypeToAssume
|
||||||
into roleUuidToAssume;
|
into roleUuidToAssume;
|
||||||
if roleUuidToAssume is null then
|
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;
|
end if;
|
||||||
if not isGranted(currentUserUuid, roleUuidToAssume) then
|
if not isGranted(currentUserUuid, roleUuidToAssume) then
|
||||||
raise exception '[403] user % has no permission to assume role %', currentUser(), roleName;
|
raise exception '[403] user % has no permission to assume role %', currentUser(), roleName;
|
||||||
|
@ -37,7 +37,7 @@ declare
|
|||||||
subRoleUuid uuid;
|
subRoleUuid uuid;
|
||||||
superRoleUuid uuid;
|
superRoleUuid uuid;
|
||||||
userUuid uuid;
|
userUuid uuid;
|
||||||
grantedByRoleUuid uuid;
|
userGrantsByRoleUuid uuid;
|
||||||
begin
|
begin
|
||||||
roleUuid := createRole(roleDescriptor);
|
roleUuid := createRole(roleDescriptor);
|
||||||
|
|
||||||
@ -58,14 +58,15 @@ begin
|
|||||||
end loop;
|
end loop;
|
||||||
|
|
||||||
if cardinality(userUuids) > 0 then
|
if cardinality(userUuids) > 0 then
|
||||||
|
-- direct grants to users need a grantedByRole which can revoke the grant
|
||||||
if grantedByRole is null then
|
if grantedByRole is null then
|
||||||
grantedByRoleUuid := roleUuid;
|
userGrantsByRoleUuid := roleUuid; -- TODO: or do we want to require an explicit userGrantsByRoleUuid?
|
||||||
hsh-michaelhoennig marked this conversation as resolved
Outdated
|
|||||||
else
|
else
|
||||||
grantedByRoleUuid := getRoleId(grantedByRole);
|
userGrantsByRoleUuid := getRoleId(grantedByRole);
|
||||||
end if;
|
end if;
|
||||||
foreach userUuid in array userUuids
|
foreach userUuid in array userUuids
|
||||||
loop
|
loop
|
||||||
call grantRoleToUserUnchecked(grantedByRoleUuid, roleUuid, userUuid);
|
call grantRoleToUserUnchecked(userGrantsByRoleUuid, roleUuid, userUuid);
|
||||||
end loop;
|
end loop;
|
||||||
end if;
|
end if;
|
||||||
|
|
||||||
|
@ -73,6 +73,7 @@ begin
|
|||||||
return roleDescriptor('%2$s', entity.uuid, 'tenant', assumed);
|
return roleDescriptor('%2$s', entity.uuid, 'tenant', assumed);
|
||||||
end; $f$;
|
end; $f$;
|
||||||
|
|
||||||
|
-- TODO: remove guest role
|
||||||
create or replace function %1$sGuest(entity %2$s, assumed boolean = true)
|
create or replace function %1$sGuest(entity %2$s, assumed boolean = true)
|
||||||
returns RbacRoleDescriptor
|
returns RbacRoleDescriptor
|
||||||
language plpgsql
|
language plpgsql
|
||||||
@ -81,6 +82,14 @@ begin
|
|||||||
return roleDescriptor('%2$s', entity.uuid, 'guest', assumed);
|
return roleDescriptor('%2$s', entity.uuid, 'guest', assumed);
|
||||||
end; $f$;
|
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);
|
$sql$, prefix, targetTable);
|
||||||
execute sql;
|
execute sql;
|
||||||
end; $$;
|
end; $$;
|
||||||
@ -148,12 +157,16 @@ end; $$;
|
|||||||
--changeset rbac-generators-RESTRICTED-VIEW:1 endDelimiter:--//
|
--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 $$
|
language plpgsql as $$
|
||||||
declare
|
declare
|
||||||
sql text;
|
sql text;
|
||||||
|
newColumns text;
|
||||||
begin
|
begin
|
||||||
targetTable := lower(targetTable);
|
targetTable := lower(targetTable);
|
||||||
|
if columnNames = '*' then
|
||||||
|
columnNames := columnsNames(targetTable);
|
||||||
|
end if;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Creates a restricted view based on the 'SELECT' permission of the current subject.
|
Creates a restricted view based on the 'SELECT' permission of the current subject.
|
||||||
@ -175,6 +188,7 @@ begin
|
|||||||
/**
|
/**
|
||||||
Instead of insert trigger function for the restricted view.
|
Instead of insert trigger function for the restricted view.
|
||||||
*/
|
*/
|
||||||
|
newColumns := 'new.' || replace(columnNames, ',', ', new.');
|
||||||
sql := format($sql$
|
sql := format($sql$
|
||||||
create or replace function %1$sInsert()
|
create or replace function %1$sInsert()
|
||||||
returns trigger
|
returns trigger
|
||||||
@ -183,12 +197,12 @@ begin
|
|||||||
newTargetRow %1$s;
|
newTargetRow %1$s;
|
||||||
begin
|
begin
|
||||||
insert
|
insert
|
||||||
into %1$s
|
into %1$s (%2$s)
|
||||||
values (new.*)
|
values (%3$s)
|
||||||
returning * into newTargetRow;
|
returning * into newTargetRow;
|
||||||
return newTargetRow;
|
return newTargetRow;
|
||||||
end; $f$;
|
end; $f$;
|
||||||
$sql$, targetTable);
|
$sql$, targetTable, columnNames, newColumns);
|
||||||
execute sql;
|
execute sql;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -121,6 +121,29 @@ begin transaction;
|
|||||||
call defineContext('creating global admin role', null, null, null);
|
call defineContext('creating global admin role', null, null, null);
|
||||||
select createRole(globalAdmin());
|
select createRole(globalAdmin());
|
||||||
commit;
|
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:--//
|
--changeset rbac-global-ADMIN-USERS:1 context:dev,tc endDelimiter:--//
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
### rbac customer
|
### 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
|
```mermaid
|
||||||
%%{init:{'flowchart':{'htmlLabels':false}}}%%
|
%%{init:{'flowchart':{'htmlLabels':false}}}%%
|
||||||
@ -21,6 +21,7 @@ subgraph customer["`**customer**`"]
|
|||||||
subgraph customer:permissions[ ]
|
subgraph customer:permissions[ ]
|
||||||
style customer:permissions fill:#dd4901,stroke:white
|
style customer:permissions fill:#dd4901,stroke:white
|
||||||
|
|
||||||
|
perm:customer:INSERT{{customer:INSERT}}
|
||||||
perm:customer:DELETE{{customer:DELETE}}
|
perm:customer:DELETE{{customer:DELETE}}
|
||||||
perm:customer:UPDATE{{customer:UPDATE}}
|
perm:customer:UPDATE{{customer:UPDATE}}
|
||||||
perm:customer:SELECT{{customer:SELECT}}
|
perm:customer:SELECT{{customer:SELECT}}
|
||||||
@ -36,6 +37,7 @@ role:customer:owner ==> role:customer:admin
|
|||||||
role:customer:admin ==> role:customer:tenant
|
role:customer:admin ==> role:customer:tenant
|
||||||
|
|
||||||
%% granting permissions to roles
|
%% granting permissions to roles
|
||||||
|
role:global:admin ==> perm:customer:INSERT
|
||||||
role:customer:owner ==> perm:customer:DELETE
|
role:customer:owner ==> perm:customer:DELETE
|
||||||
role:customer:admin ==> perm:customer:UPDATE
|
role:customer:admin ==> perm:customer:UPDATE
|
||||||
role:customer:tenant ==> perm:customer:SELECT
|
role:customer:tenant ==> perm:customer:SELECT
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
--liquibase formatted sql
|
--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:--//
|
--changeset test-customer-rbac-OBJECT:1 endDelimiter:--//
|
||||||
@ -36,8 +37,8 @@ begin
|
|||||||
perform createRoleWithGrants(
|
perform createRoleWithGrants(
|
||||||
testCustomerOwner(NEW),
|
testCustomerOwner(NEW),
|
||||||
permissions => array['DELETE'],
|
permissions => array['DELETE'],
|
||||||
userUuids => array[currentUserUuid()],
|
incomingSuperRoles => array[globalAdmin(unassumed())],
|
||||||
incomingSuperRoles => array[globalAdmin(unassumed())]
|
userUuids => array[currentUserUuid()]
|
||||||
);
|
);
|
||||||
|
|
||||||
perform createRoleWithGrants(
|
perform createRoleWithGrants(
|
||||||
@ -72,15 +73,56 @@ create trigger insertTriggerForTestCustomer_tg
|
|||||||
after insert on test_customer
|
after insert on test_customer
|
||||||
for each row
|
for each row
|
||||||
execute procedure insertTriggerForTestCustomer_tf();
|
execute procedure insertTriggerForTestCustomer_tf();
|
||||||
|
|
||||||
--//
|
--//
|
||||||
|
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset test-customer-rbac-INSERT:1 endDelimiter:--//
|
--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()
|
create or replace function test_customer_insert_permission_missing_tf()
|
||||||
returns trigger
|
returns trigger
|
||||||
@ -93,26 +135,27 @@ end; $$;
|
|||||||
create trigger test_customer_insert_permission_check_tg
|
create trigger test_customer_insert_permission_check_tg
|
||||||
before insert on test_customer
|
before insert on test_customer
|
||||||
for each row
|
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() )
|
when ( not isGlobalAdmin() )
|
||||||
execute procedure test_customer_insert_permission_missing_tf();
|
execute procedure test_customer_insert_permission_missing_tf();
|
||||||
|
|
||||||
--//
|
--//
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset test-customer-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
--changeset test-customer-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
|
|
||||||
call generateRbacIdentityViewFromProjection('test_customer', $idName$
|
call generateRbacIdentityViewFromProjection('test_customer',
|
||||||
|
$idName$
|
||||||
prefix
|
prefix
|
||||||
$idName$);
|
$idName$);
|
||||||
|
|
||||||
--//
|
--//
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset test-customer-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
--changeset test-customer-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
call generateRbacRestrictedView('test_customer',
|
call generateRbacRestrictedView('test_customer',
|
||||||
'reference',
|
$orderBy$
|
||||||
|
reference
|
||||||
|
$orderBy$,
|
||||||
$updates$
|
$updates$
|
||||||
reference = new.reference,
|
reference = new.reference,
|
||||||
prefix = new.prefix,
|
prefix = new.prefix,
|
||||||
@ -120,4 +163,3 @@ call generateRbacRestrictedView('test_customer',
|
|||||||
$updates$);
|
$updates$);
|
||||||
--//
|
--//
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
### rbac package
|
### 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
|
```mermaid
|
||||||
%%{init:{'flowchart':{'htmlLabels':false}}}%%
|
%%{init:{'flowchart':{'htmlLabels':false}}}%%
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
--liquibase formatted sql
|
--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:--//
|
--changeset test-package-rbac-OBJECT:1 endDelimiter:--//
|
||||||
@ -33,9 +34,10 @@ declare
|
|||||||
|
|
||||||
begin
|
begin
|
||||||
call enterTriggerForObjectUuid(NEW.uuid);
|
call enterTriggerForObjectUuid(NEW.uuid);
|
||||||
SELECT * FROM test_customer c
|
|
||||||
WHERE c.uuid= NEW.customerUuid
|
SELECT * FROM test_customer WHERE uuid = NEW.customerUuid INTO newCustomer;
|
||||||
into newCustomer;
|
assert newCustomer.uuid is not null, format('newCustomer must not be null for NEW.customerUuid = %s', NEW.customerUuid);
|
||||||
|
|
||||||
|
|
||||||
perform createRoleWithGrants(
|
perform createRoleWithGrants(
|
||||||
testPackageOwner(NEW),
|
testPackageOwner(NEW),
|
||||||
@ -75,9 +77,9 @@ create trigger insertTriggerForTestPackage_tg
|
|||||||
after insert on test_package
|
after insert on test_package
|
||||||
for each row
|
for each row
|
||||||
execute procedure insertTriggerForTestPackage_tf();
|
execute procedure insertTriggerForTestPackage_tf();
|
||||||
|
|
||||||
--//
|
--//
|
||||||
|
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset test-package-rbac-update-trigger:1 endDelimiter:--//
|
--changeset test-package-rbac-update-trigger:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
@ -99,17 +101,15 @@ declare
|
|||||||
begin
|
begin
|
||||||
call enterTriggerForObjectUuid(NEW.uuid);
|
call enterTriggerForObjectUuid(NEW.uuid);
|
||||||
|
|
||||||
SELECT * FROM test_customer c
|
SELECT * FROM test_customer WHERE uuid = OLD.customerUuid INTO oldCustomer;
|
||||||
WHERE c.uuid= OLD.customerUuid
|
assert oldCustomer.uuid is not null, format('oldCustomer must not be null for OLD.customerUuid = %s', OLD.customerUuid);
|
||||||
into oldCustomer;
|
|
||||||
SELECT * FROM test_customer c
|
SELECT * FROM test_customer WHERE uuid = NEW.customerUuid INTO newCustomer;
|
||||||
WHERE c.uuid= NEW.customerUuid
|
assert newCustomer.uuid is not null, format('newCustomer must not be null for NEW.customerUuid = %s', NEW.customerUuid);
|
||||||
into newCustomer;
|
|
||||||
|
|
||||||
if NEW.customerUuid <> OLD.customerUuid then
|
if NEW.customerUuid <> OLD.customerUuid then
|
||||||
|
|
||||||
call revokePermissionFromRole(findPermissionId(OLD.uuid, 'INSERT'), testCustomerAdmin(oldCustomer));
|
|
||||||
|
|
||||||
call revokeRoleFromRole(testPackageOwner(OLD), testCustomerAdmin(oldCustomer));
|
call revokeRoleFromRole(testPackageOwner(OLD), testCustomerAdmin(oldCustomer));
|
||||||
hsh-michaelhoennig marked this conversation as resolved
Outdated
|
|||||||
call grantRoleToRole(testPackageOwner(NEW), testCustomerAdmin(newCustomer));
|
call grantRoleToRole(testPackageOwner(NEW), testCustomerAdmin(newCustomer));
|
||||||
|
|
||||||
@ -138,9 +138,9 @@ create trigger updateTriggerForTestPackage_tg
|
|||||||
after update on test_package
|
after update on test_package
|
||||||
for each row
|
for each row
|
||||||
execute procedure updateTriggerForTestPackage_tf();
|
execute procedure updateTriggerForTestPackage_tf();
|
||||||
|
|
||||||
--//
|
--//
|
||||||
|
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset test-package-rbac-INSERT:1 endDelimiter:--//
|
--changeset test-package-rbac-INSERT:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
@ -160,7 +160,7 @@ do language plpgsql $$
|
|||||||
LOOP
|
LOOP
|
||||||
roleUuid := findRoleId(testCustomerAdmin(row));
|
roleUuid := findRoleId(testCustomerAdmin(row));
|
||||||
permissionUuid := createPermission(row.uuid, 'INSERT', 'test_package');
|
permissionUuid := createPermission(row.uuid, 'INSERT', 'test_package');
|
||||||
call grantPermissionToRole(roleUuid, permissionUuid);
|
call grantPermissionToRole(permissionUuid, roleUuid);
|
||||||
END LOOP;
|
END LOOP;
|
||||||
END;
|
END;
|
||||||
$$;
|
$$;
|
||||||
@ -174,18 +174,22 @@ create or replace function test_package_test_customer_insert_tf()
|
|||||||
strict as $$
|
strict as $$
|
||||||
begin
|
begin
|
||||||
call grantPermissionToRole(
|
call grantPermissionToRole(
|
||||||
testCustomerAdmin(NEW),
|
createPermission(NEW.uuid, 'INSERT', 'test_package'),
|
||||||
createPermission(NEW.uuid, 'INSERT', 'test_package'));
|
testCustomerAdmin(NEW));
|
||||||
return NEW;
|
return NEW;
|
||||||
end; $$;
|
end; $$;
|
||||||
|
|
||||||
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
|
after insert on test_customer
|
||||||
for each row
|
for each row
|
||||||
execute procedure test_package_test_customer_insert_tf();
|
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()
|
create or replace function test_package_insert_permission_missing_tf()
|
||||||
returns trigger
|
returns trigger
|
||||||
@ -200,22 +204,25 @@ create trigger test_package_insert_permission_check_tg
|
|||||||
for each row
|
for each row
|
||||||
when ( not hasInsertPermission(NEW.customerUuid, 'INSERT', 'test_package') )
|
when ( not hasInsertPermission(NEW.customerUuid, 'INSERT', 'test_package') )
|
||||||
execute procedure test_package_insert_permission_missing_tf();
|
execute procedure test_package_insert_permission_missing_tf();
|
||||||
|
|
||||||
--//
|
--//
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset test-package-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
--changeset test-package-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
|
|
||||||
call generateRbacIdentityViewFromProjection('test_package', $idName$
|
call generateRbacIdentityViewFromProjection('test_package',
|
||||||
|
$idName$
|
||||||
name
|
name
|
||||||
$idName$);
|
$idName$);
|
||||||
|
|
||||||
--//
|
--//
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset test-package-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
--changeset test-package-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
call generateRbacRestrictedView('test_package',
|
call generateRbacRestrictedView('test_package',
|
||||||
'name',
|
$orderBy$
|
||||||
hsh-michaelhoennig marked this conversation as resolved
Outdated
|
|||||||
|
name
|
||||||
|
$orderBy$,
|
||||||
$updates$
|
$updates$
|
||||||
version = new.version,
|
version = new.version,
|
||||||
customerUuid = new.customerUuid,
|
customerUuid = new.customerUuid,
|
||||||
@ -223,4 +230,3 @@ call generateRbacRestrictedView('test_package',
|
|||||||
$updates$);
|
$updates$);
|
||||||
--//
|
--//
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
### rbac domain
|
### 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
|
```mermaid
|
||||||
%%{init:{'flowchart':{'htmlLabels':false}}}%%
|
%%{init:{'flowchart':{'htmlLabels':false}}}%%
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
--liquibase formatted sql
|
--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:--//
|
--changeset test-domain-rbac-OBJECT:1 endDelimiter:--//
|
||||||
@ -33,9 +34,10 @@ declare
|
|||||||
|
|
||||||
begin
|
begin
|
||||||
call enterTriggerForObjectUuid(NEW.uuid);
|
call enterTriggerForObjectUuid(NEW.uuid);
|
||||||
SELECT * FROM test_package p
|
|
||||||
WHERE p.uuid= NEW.packageUuid
|
SELECT * FROM test_package WHERE uuid = NEW.packageUuid INTO newPackage;
|
||||||
into newPackage;
|
assert newPackage.uuid is not null, format('newPackage must not be null for NEW.packageUuid = %s', NEW.packageUuid);
|
||||||
|
|
||||||
|
|
||||||
perform createRoleWithGrants(
|
perform createRoleWithGrants(
|
||||||
testDomainOwner(NEW),
|
testDomainOwner(NEW),
|
||||||
@ -71,9 +73,9 @@ create trigger insertTriggerForTestDomain_tg
|
|||||||
after insert on test_domain
|
after insert on test_domain
|
||||||
for each row
|
for each row
|
||||||
execute procedure insertTriggerForTestDomain_tf();
|
execute procedure insertTriggerForTestDomain_tf();
|
||||||
|
|
||||||
--//
|
--//
|
||||||
|
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset test-domain-rbac-update-trigger:1 endDelimiter:--//
|
--changeset test-domain-rbac-update-trigger:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
@ -95,17 +97,15 @@ declare
|
|||||||
begin
|
begin
|
||||||
call enterTriggerForObjectUuid(NEW.uuid);
|
call enterTriggerForObjectUuid(NEW.uuid);
|
||||||
|
|
||||||
SELECT * FROM test_package p
|
SELECT * FROM test_package WHERE uuid = OLD.packageUuid INTO oldPackage;
|
||||||
WHERE p.uuid= OLD.packageUuid
|
assert oldPackage.uuid is not null, format('oldPackage must not be null for OLD.packageUuid = %s', OLD.packageUuid);
|
||||||
into oldPackage;
|
|
||||||
SELECT * FROM test_package p
|
SELECT * FROM test_package WHERE uuid = NEW.packageUuid INTO newPackage;
|
||||||
WHERE p.uuid= NEW.packageUuid
|
assert newPackage.uuid is not null, format('newPackage must not be null for NEW.packageUuid = %s', NEW.packageUuid);
|
||||||
into newPackage;
|
|
||||||
|
|
||||||
if NEW.packageUuid <> OLD.packageUuid then
|
if NEW.packageUuid <> OLD.packageUuid then
|
||||||
|
|
||||||
call revokePermissionFromRole(findPermissionId(OLD.uuid, 'INSERT'), testPackageAdmin(oldPackage));
|
|
||||||
|
|
||||||
call revokeRoleFromRole(testDomainOwner(OLD), testPackageAdmin(oldPackage));
|
call revokeRoleFromRole(testDomainOwner(OLD), testPackageAdmin(oldPackage));
|
||||||
call grantRoleToRole(testDomainOwner(NEW), testPackageAdmin(newPackage));
|
call grantRoleToRole(testDomainOwner(NEW), testPackageAdmin(newPackage));
|
||||||
|
|
||||||
@ -137,9 +137,9 @@ create trigger updateTriggerForTestDomain_tg
|
|||||||
after update on test_domain
|
after update on test_domain
|
||||||
for each row
|
for each row
|
||||||
execute procedure updateTriggerForTestDomain_tf();
|
execute procedure updateTriggerForTestDomain_tf();
|
||||||
|
|
||||||
--//
|
--//
|
||||||
|
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset test-domain-rbac-INSERT:1 endDelimiter:--//
|
--changeset test-domain-rbac-INSERT:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
@ -159,7 +159,7 @@ do language plpgsql $$
|
|||||||
LOOP
|
LOOP
|
||||||
roleUuid := findRoleId(testPackageAdmin(row));
|
roleUuid := findRoleId(testPackageAdmin(row));
|
||||||
permissionUuid := createPermission(row.uuid, 'INSERT', 'test_domain');
|
permissionUuid := createPermission(row.uuid, 'INSERT', 'test_domain');
|
||||||
call grantPermissionToRole(roleUuid, permissionUuid);
|
call grantPermissionToRole(permissionUuid, roleUuid);
|
||||||
END LOOP;
|
END LOOP;
|
||||||
END;
|
END;
|
||||||
$$;
|
$$;
|
||||||
@ -173,18 +173,22 @@ create or replace function test_domain_test_package_insert_tf()
|
|||||||
strict as $$
|
strict as $$
|
||||||
begin
|
begin
|
||||||
call grantPermissionToRole(
|
call grantPermissionToRole(
|
||||||
testPackageAdmin(NEW),
|
createPermission(NEW.uuid, 'INSERT', 'test_domain'),
|
||||||
createPermission(NEW.uuid, 'INSERT', 'test_domain'));
|
testPackageAdmin(NEW));
|
||||||
return NEW;
|
return NEW;
|
||||||
end; $$;
|
end; $$;
|
||||||
|
|
||||||
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
|
after insert on test_package
|
||||||
for each row
|
for each row
|
||||||
execute procedure test_domain_test_package_insert_tf();
|
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()
|
create or replace function test_domain_insert_permission_missing_tf()
|
||||||
returns trigger
|
returns trigger
|
||||||
@ -199,22 +203,25 @@ create trigger test_domain_insert_permission_check_tg
|
|||||||
for each row
|
for each row
|
||||||
when ( not hasInsertPermission(NEW.packageUuid, 'INSERT', 'test_domain') )
|
when ( not hasInsertPermission(NEW.packageUuid, 'INSERT', 'test_domain') )
|
||||||
execute procedure test_domain_insert_permission_missing_tf();
|
execute procedure test_domain_insert_permission_missing_tf();
|
||||||
|
|
||||||
--//
|
--//
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset test-domain-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
--changeset test-domain-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
|
|
||||||
call generateRbacIdentityViewFromProjection('test_domain', $idName$
|
call generateRbacIdentityViewFromProjection('test_domain',
|
||||||
|
$idName$
|
||||||
name
|
name
|
||||||
$idName$);
|
$idName$);
|
||||||
|
|
||||||
--//
|
--//
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset test-domain-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
--changeset test-domain-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
call generateRbacRestrictedView('test_domain',
|
call generateRbacRestrictedView('test_domain',
|
||||||
'name',
|
$orderBy$
|
||||||
|
name
|
||||||
|
$orderBy$,
|
||||||
$updates$
|
$updates$
|
||||||
version = new.version,
|
version = new.version,
|
||||||
packageUuid = new.packageUuid,
|
packageUuid = new.packageUuid,
|
||||||
@ -222,4 +229,3 @@ call generateRbacRestrictedView('test_domain',
|
|||||||
$updates$);
|
$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
|
file: db/changelog/005-uuid-ossp-extension.sql
|
||||||
- include:
|
- include:
|
||||||
file: db/changelog/006-numeric-hash-functions.sql
|
file: db/changelog/006-numeric-hash-functions.sql
|
||||||
|
- include:
|
||||||
|
file: db/changelog/007-table-columns.sql
|
||||||
- include:
|
- include:
|
||||||
file: db/changelog/009-check-environment.sql
|
file: db/changelog/009-check-environment.sql
|
||||||
- include:
|
- include:
|
||||||
|
@ -462,7 +462,7 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
|
|||||||
RestAssured // @formatter:off
|
RestAssured // @formatter:off
|
||||||
.given()
|
.given()
|
||||||
.header("current-user", "superuser-alex@hostsharing.net")
|
.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)
|
.port(port)
|
||||||
.when()
|
.when()
|
||||||
.delete("http://localhost/api/hs/office/memberships/" + givenMembership.getUuid())
|
.delete("http://localhost/api/hs/office/memberships/" + givenMembership.getUuid())
|
||||||
|
@ -204,7 +204,7 @@ class TestCustomerControllerAcceptanceTest {
|
|||||||
.statusCode(403)
|
.statusCode(403)
|
||||||
.contentType(ContentType.JSON)
|
.contentType(ContentType.JSON)
|
||||||
.statusCode(403)
|
.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
|
// @formatter:on
|
||||||
|
|
||||||
// finally, the new customer was not created
|
// finally, the new customer was not created
|
||||||
|
@ -29,6 +29,7 @@ class TestCustomerEntityUnitTest {
|
|||||||
subgraph customer:permissions[ ]
|
subgraph customer:permissions[ ]
|
||||||
style customer:permissions fill:#dd4901,stroke:white
|
style customer:permissions fill:#dd4901,stroke:white
|
||||||
|
|
||||||
|
perm:customer:INSERT{{customer:INSERT}}
|
||||||
perm:customer:DELETE{{customer:DELETE}}
|
perm:customer:DELETE{{customer:DELETE}}
|
||||||
perm:customer:UPDATE{{customer:UPDATE}}
|
perm:customer:UPDATE{{customer:UPDATE}}
|
||||||
perm:customer:SELECT{{customer:SELECT}}
|
perm:customer:SELECT{{customer:SELECT}}
|
||||||
@ -44,6 +45,7 @@ class TestCustomerEntityUnitTest {
|
|||||||
role:customer:admin ==> role:customer:tenant
|
role:customer:admin ==> role:customer:tenant
|
||||||
|
|
||||||
%% granting permissions to roles
|
%% granting permissions to roles
|
||||||
|
role:global:admin ==> perm:customer:INSERT
|
||||||
role:customer:owner ==> perm:customer:DELETE
|
role:customer:owner ==> perm:customer:DELETE
|
||||||
role:customer:admin ==> perm:customer:UPDATE
|
role:customer:admin ==> perm:customer:UPDATE
|
||||||
role:customer:tenant ==> perm:customer:SELECT
|
role:customer:tenant ==> perm:customer:SELECT
|
||||||
|
@ -4,8 +4,9 @@ spring:
|
|||||||
platform: postgres
|
platform: postgres
|
||||||
|
|
||||||
datasource:
|
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-local: jdbc:postgresql://localhost:5432/postgres
|
||||||
|
url: ${spring.datasource.url-tc}
|
||||||
username: postgres
|
username: postgres
|
||||||
password: password
|
password: password
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user
rename missing to check