Compare commits

..

No commits in common. "09fc332dcc3fe81f17674232aa122fe615454fcf" and "86ee6dfe16e8e9a7d9780331e81f82604165ee81" have entirely different histories.

27 changed files with 210 additions and 203 deletions

View File

@ -150,7 +150,11 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
.toRole("global", ADMIN).grantPermission(INSERT) .toRole("global", ADMIN).grantPermission(INSERT)
.importRootEntityAliasProxy("debitorRel", HsOfficeRelationEntity.class, .importRootEntityAliasProxy("debitorRel", HsOfficeRelationEntity.class,
directlyFetchedByDependsOnColumn(), fetchedBySql("""
SELECT *
FROM hs_office_relation AS r
WHERE r.type = 'DEBITOR' AND r.uuid = ${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)
@ -166,7 +170,7 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
.importEntityAlias("partnerRel", HsOfficeRelationEntity.class, .importEntityAlias("partnerRel", HsOfficeRelationEntity.class,
dependsOnColumn("debitorRelUuid"), dependsOnColumn("debitorRelUuid"),
fetchedBySql(""" fetchedBySql("""
SELECT ${columns} SELECT partnerRel.*
FROM hs_office_relation AS partnerRel FROM hs_office_relation AS partnerRel
JOIN hs_office_relation AS debitorRel JOIN hs_office_relation AS debitorRel
ON debitorRel.type = 'DEBITOR' AND debitorRel.anchorUuid = partnerRel.holderUuid ON debitorRel.type = 'DEBITOR' AND debitorRel.anchorUuid = partnerRel.holderUuid

View File

@ -132,10 +132,10 @@ public class HsOfficeMembershipEntity implements HasUuid, Stringifyable {
.importEntityAlias("partnerRel", HsOfficeRelationEntity.class, .importEntityAlias("partnerRel", HsOfficeRelationEntity.class,
dependsOnColumn("partnerUuid"), dependsOnColumn("partnerUuid"),
fetchedBySql(""" fetchedBySql("""
SELECT ${columns} SELECT r.*
FROM hs_office_partner AS partner FROM hs_office_partner AS p
JOIN hs_office_relation AS partnerRel ON partnerRel.uuid = partner.partnerRelUuid JOIN hs_office_relation AS r ON r.uuid = p.partnerRelUuid
WHERE partner.uuid = ${REF}.partnerUuid WHERE p.uuid = ${REF}.partnerUuid
"""), """),
NOT_NULL) NOT_NULL)
.toRole("partnerRel", ADMIN).grantPermission(INSERT) .toRole("partnerRel", ADMIN).grantPermission(INSERT)

View File

@ -32,7 +32,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.directlyFetchedByDependsOnColumn; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.fetchedBySql;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
import static java.util.Optional.ofNullable; import static java.util.Optional.ofNullable;
import static net.hostsharing.hsadminng.stringify.Stringify.stringify; import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@ -95,17 +95,17 @@ public class HsOfficePartnerEntity implements Stringifyable, HasUuid {
return rbacViewFor("partner", HsOfficePartnerEntity.class) return rbacViewFor("partner", HsOfficePartnerEntity.class)
.withIdentityView(SQL.projection("'P-' || partnerNumber")) .withIdentityView(SQL.projection("'P-' || partnerNumber"))
.withUpdatableColumns("partnerRelUuid") .withUpdatableColumns("partnerRelUuid")
.toRole("global", ADMIN).grantPermission(INSERT) .toRole("global", ADMIN).grantPermission(INSERT) // FIXME: global -> partnerRel.anchor?
.importRootEntityAliasProxy("partnerRel", HsOfficeRelationEntity.class, .importRootEntityAliasProxy("partnerRel", HsOfficeRelationEntity.class,
directlyFetchedByDependsOnColumn(), fetchedBySql("SELECT * FROM hs_office_relation AS r WHERE r.uuid = ${ref}.partnerRelUuid"),
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,
directlyFetchedByDependsOnColumn(), fetchedBySql("SELECT * FROM hs_office_partner_details AS d WHERE d.uuid = ${ref}.detailsUuid"),
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)

View File

@ -20,7 +20,7 @@ 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.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.directlyFetchedByDependsOnColumn; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.fetchedBySql;
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;
@ -91,15 +91,15 @@ public class HsOfficeRelationEntity implements HasUuid, Stringifyable {
.withUpdatableColumns("contactUuid") .withUpdatableColumns("contactUuid")
.importEntityAlias("anchorPerson", HsOfficePersonEntity.class, .importEntityAlias("anchorPerson", HsOfficePersonEntity.class,
dependsOnColumn("anchorUuid"), dependsOnColumn("anchorUuid"),
directlyFetchedByDependsOnColumn(), fetchedBySql("select * from hs_office_person as p where p.uuid = ${REF}.anchorUuid"),
NOT_NULL) NOT_NULL)
.importEntityAlias("holderPerson", HsOfficePersonEntity.class, .importEntityAlias("holderPerson", HsOfficePersonEntity.class,
dependsOnColumn("holderUuid"), dependsOnColumn("holderUuid"),
directlyFetchedByDependsOnColumn(), fetchedBySql("select * from hs_office_person as p where p.uuid = ${REF}.holderUuid"),
NOT_NULL) NOT_NULL)
.importEntityAlias("contact", HsOfficeContactEntity.class, .importEntityAlias("contact", HsOfficeContactEntity.class,
dependsOnColumn("contactUuid"), dependsOnColumn("contactUuid"),
directlyFetchedByDependsOnColumn(), fetchedBySql("select * from hs_office_contact as c where c.uuid = ${REF}.contactUuid"),
NOT_NULL) NOT_NULL)
.createRole(OWNER, (with) -> { .createRole(OWNER, (with) -> {
with.owningUser(CREATOR); with.owningUser(CREATOR);

View File

@ -107,7 +107,7 @@ public class HsOfficeSepaMandateEntity implements Stringifyable, HasUuid {
.importEntityAlias("debitorRel", HsOfficeRelationEntity.class, .importEntityAlias("debitorRel", HsOfficeRelationEntity.class,
dependsOnColumn("debitorUuid"), dependsOnColumn("debitorUuid"),
fetchedBySql(""" fetchedBySql("""
SELECT ${columns} SELECT debitorRel.*
FROM hs_office_relation debitorRel FROM hs_office_relation debitorRel
JOIN hs_office_debitor debitor ON debitor.debitorRelUuid = debitorRel.uuid JOIN hs_office_debitor debitor ON debitor.debitorRelUuid = debitorRel.uuid
WHERE debitor.uuid = ${REF}.debitorUuid WHERE debitor.uuid = ${REF}.debitorUuid

View File

@ -114,7 +114,8 @@ public class InsertTriggerGenerator {
} }
} }
} else { } else {
if (g.getSuperRoleDef().getEntityAlias().isFetchedByDirectForeignKey()) { final var superRoleEntityAlias = g.getSuperRoleDef().getEntityAlias();
if (superRoleEntityAlias.fetchSql().part == RbacView.SQL.Part.AUTO_FETCH) {
generateInsertPermissionTriggerAllowByRoleOfDirectForeignKey(plPgSql, g); generateInsertPermissionTriggerAllowByRoleOfDirectForeignKey(plPgSql, g);
} else { } else {
generateInsertPermissionTriggerAllowByRoleOfIndirectForeignKey(plPgSql, g); generateInsertPermissionTriggerAllowByRoleOfIndirectForeignKey(plPgSql, g);
@ -161,30 +162,26 @@ public class InsertTriggerGenerator {
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 an indirect role. 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 An indirect role is a role FIXME.
of the source entity, but needs to be fetched via joined tables.
*/ */
create or replace function ${rawSubTable}_insert_permission_check_tf() create or replace function ${rawSubTable}_insert_permission_missing_tf()
returns trigger returns trigger
language plpgsql as $$ language plpgsql as $$
declare
superRoleObjectUuid uuid;
begin begin
if ( not hasInsertPermission(
( SELECT ${varName}.uuid FROM
""", """,
with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableName())); with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableName()),
plPgSql.chopEmptyLines(); with("varName", g.getSuperRoleDef().getEntityAlias().aliasName()));
plPgSql.indented(2, () -> { plPgSql.indented(3, () -> {
plPgSql.writeLn( plPgSql.writeLn(
"superRoleObjectUuid := (" + g.getSuperRoleDef().getEntityAlias().fetchSql().sql + ");\n" + "(" + g.getSuperRoleDef().getEntityAlias().fetchSql().sql + ") AS ${varName}",
"assert superRoleObjectUuid is not null, 'superRoleObjectUuid must not be null';", with("varName", g.getSuperRoleDef().getEntityAlias().aliasName()),
with("columns", g.getSuperRoleDef().getEntityAlias().aliasName() + ".uuid"),
with("ref", NEW.name())); with("ref", NEW.name()));
}); });
plPgSql.writeLn();
plPgSql.writeLn(""" plPgSql.writeLn("""
if ( not hasInsertPermission(superRoleObjectUuid, 'INSERT', '${rawSubTable}') ) then
), 'INSERT', '${rawSubTable}') ) then
raise exception raise exception
'[403] insert into ${rawSubTable} not allowed for current subjects % (%)', '[403] insert into ${rawSubTable} not allowed for current subjects % (%)',
currentSubjects(), currentSubjectsUuids(); currentSubjects(), currentSubjectsUuids();
@ -195,7 +192,7 @@ public class InsertTriggerGenerator {
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
execute procedure ${rawSubTable}_insert_permission_check_tf(); execute procedure ${rawSubTable}_insert_permission_missing_tf();
""", """,
with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableName())); with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableName()));

View File

@ -26,20 +26,18 @@ public class RbacIdentityViewGenerator {
plPgSql.writeLn( plPgSql.writeLn(
switch (rbacDef.getIdentityViewSqlQuery().part) { switch (rbacDef.getIdentityViewSqlQuery().part) {
case SQL_PROJECTION -> """ case SQL_PROJECTION -> """
call generateRbacIdentityViewFromProjection('${rawTableName}', call generateRbacIdentityViewFromProjection('${rawTableName}', $idName$
$idName$
${identityViewSqlPart} ${identityViewSqlPart}
$idName$); $idName$);
"""; """;
case SQL_QUERY -> """ case SQL_QUERY -> """
call generateRbacIdentityViewFromQuery('${rawTableName}', call generateRbacIdentityViewFromQuery('${rawTableName}', $idName$
$idName$
${identityViewSqlPart} ${identityViewSqlPart}
$idName$); $idName$);
"""; """;
default -> throw new IllegalStateException("illegal SQL part given"); default -> throw new IllegalStateException("illegal SQL part given");
}, },
with("identityViewSqlPart", StringWriter.indented(2, rbacDef.getIdentityViewSqlQuery().sql)), with("identityViewSqlPart", rbacDef.getIdentityViewSqlQuery().sql),
with("rawTableName", rawTableName)); with("rawTableName", rawTableName));
plPgSql.writeLn("--//"); plPgSql.writeLn("--//");

View File

@ -32,10 +32,10 @@ public class RbacRestrictedViewGenerator {
""", """,
with("liquibaseTagPrefix", liquibaseTagPrefix), with("liquibaseTagPrefix", liquibaseTagPrefix),
with("orderBy", indented(2, rbacDef.getOrderBySqlExpression().sql)), with("orderBy", indented(rbacDef.getOrderBySqlExpression().sql, 2)),
with("updates", indented(2, rbacDef.getUpdatableColumns().stream() with("updates", indented(rbacDef.getUpdatableColumns().stream()
.map(c -> c + " = new." + c) .map(c -> c + " = new." + c)
.collect(joining(",\n")))), .collect(joining(",\n")), 2)),
with("rawTableName", rawTableName)); with("rawTableName", rawTableName));
} }
} }

View File

@ -34,7 +34,6 @@ 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.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.Part.AUTO_FETCH;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.directlyFetchedByDependsOnColumn; 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;
@ -840,8 +839,8 @@ public class RbacView {
}; };
} }
boolean isFetchedByDirectForeignKey() { public boolean hasFetchSql() {
return fetchSql != null && fetchSql.part == AUTO_FETCH; return fetchSql != null;
} }
private String withoutEntitySuffix(final String simpleEntityName) { private String withoutEntitySuffix(final String simpleEntityName) {
@ -910,25 +909,14 @@ 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 and `${columns}` for the projection. * using the reference `${ref}` of the root entity.
* * `${ref}` is going to be replaced by either `NEW` or `OLD` of the trigger function.
* <p>The query <strong>must define</strong> the entity alias name of the fetched table * `into ...` will be added with a variable name prefixed with either `new` or `old`.
* 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);
} }
@ -941,7 +929,7 @@ public class RbacView {
* @return the wrapped SQL definition object * @return the wrapped SQL definition object
*/ */
public static SQL directlyFetchedByDependsOnColumn() { public static SQL directlyFetchedByDependsOnColumn() {
return new SQL(null, AUTO_FETCH); return new SQL(null, Part.AUTO_FETCH);
} }
/** /**

View File

@ -7,7 +7,6 @@ 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;
@ -234,7 +233,6 @@ class RolesGrantsAndPermissionsGenerator {
final PostgresTriggerReference old) { final PostgresTriggerReference old) {
plPgSql.writeLn( plPgSql.writeLn(
ea.fetchSql().sql + " INTO " + entityRefVar(old, ea) + ";", ea.fetchSql().sql + " INTO " + entityRefVar(old, ea) + ";",
with("columns", ea.aliasName() + ".*"),
with("ref", old.name())); with("ref", old.name()));
if (ea.nullable() == RbacView.Nullable.NOT_NULL) { if (ea.nullable() == RbacView.Nullable.NOT_NULL) {
plPgSql.writeLn( plPgSql.writeLn(
@ -246,11 +244,14 @@ class RolesGrantsAndPermissionsGenerator {
} }
} }
private boolean isUpdatable(final RbacView.Column c) {
return rbacDef.getUpdatableColumns().contains(c);
}
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));
@ -259,11 +260,6 @@ 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()
@ -410,7 +406,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()))
.sorted().toList(); .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);
@ -422,7 +418,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()))
.sorted().toList(); .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);

View File

@ -82,7 +82,7 @@ public class StringWriter {
return string.toString(); return string.toString();
} }
public static String indented(final int indentLevel, final String text) { public static String indented(final String text, final int indentLevel) {
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)
@ -94,7 +94,7 @@ public class StringWriter {
if ( indentLevel == 0) { if ( indentLevel == 0) {
return text; return text;
} }
return indented(indentLevel, text); return indented(text, indentLevel);
} }
record VarDef(String name, String value){} record VarDef(String name, String value){}

View File

@ -71,6 +71,10 @@ public class RbacGrantsDiagramService {
private void traverseGrantsTo(final Set<RawRbacGrantEntity> graph, final UUID refUuid, final EnumSet<Include> includes) { private void traverseGrantsTo(final Set<RawRbacGrantEntity> graph, final UUID refUuid, final EnumSet<Include> includes) {
final var grants = rawGrantRepo.findByAscendingUuid(refUuid); final var grants = rawGrantRepo.findByAscendingUuid(refUuid);
grants.forEach(g -> { grants.forEach(g -> {
if ( g.getDescendantIdName() == null ) {
// FIXME: what's that?
return;
}
if (!includes.contains(PERMISSIONS) && g.getDescendantIdName().startsWith("perm ")) { if (!includes.contains(PERMISSIONS) && g.getDescendantIdName().startsWith("perm ")) {
return; return;
} }

View File

@ -691,7 +691,7 @@ declare
superRoleId uuid; superRoleId uuid;
subRoleId uuid; subRoleId uuid;
begin begin
-- TODO: maybe separate method grantRoleToRoleIfNotNull(...) for NULLABLE references -- FIXME: maybe separate method grantRoleToRoleIfNotNull(...)?
if superRole.objectUuid is null or subRole.objectuuid is null then if superRole.objectUuid is null or subRole.objectuuid is null then
return; return;
end if; end if;

View File

@ -60,7 +60,7 @@ begin
if cardinality(userUuids) > 0 then if cardinality(userUuids) > 0 then
-- direct grants to users need a grantedByRole which can revoke the grant -- direct grants to users need a grantedByRole which can revoke the grant
if grantedByRole is null then if grantedByRole is null then
userGrantsByRoleUuid := roleUuid; -- TODO: or do we want to require an explicit userGrantsByRoleUuid? userGrantsByRoleUuid := roleUuid; -- FIXME: or do we want to require an explicit userGrantsByRoleUuid?
else else
userGrantsByRoleUuid := getRoleId(grantedByRole); userGrantsByRoleUuid := getRoleId(grantedByRole);
end if; end if;

View File

@ -143,8 +143,7 @@ create trigger test_customer_insert_permission_check_tg
--changeset test-customer-rbac-IDENTITY-VIEW:1 endDelimiter:--// --changeset test-customer-rbac-IDENTITY-VIEW:1 endDelimiter:--//
-- ---------------------------------------------------------------------------- -- ----------------------------------------------------------------------------
call generateRbacIdentityViewFromProjection('test_customer', call generateRbacIdentityViewFromProjection('test_customer', $idName$
$idName$
prefix prefix
$idName$); $idName$);
--// --//

View File

@ -110,6 +110,8 @@ begin
if NEW.customerUuid <> OLD.customerUuid then if NEW.customerUuid <> OLD.customerUuid then
call revokePermissionFromRole(getPermissionId(OLD.uuid, 'INSERT'), testCustomerAdmin(oldCustomer));
call revokeRoleFromRole(testPackageOwner(OLD), testCustomerAdmin(oldCustomer)); call revokeRoleFromRole(testPackageOwner(OLD), testCustomerAdmin(oldCustomer));
call grantRoleToRole(testPackageOwner(NEW), testCustomerAdmin(newCustomer)); call grantRoleToRole(testPackageOwner(NEW), testCustomerAdmin(newCustomer));
@ -187,22 +189,30 @@ 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. where the check is performed by an indirect role.
A direct role is a role depending on a foreign key directly available in the NEW row. An indirect role is a role FIXME.
*/ */
create or replace function test_package_insert_permission_missing_tf() create or replace function test_package_insert_permission_missing_tf()
returns trigger returns trigger
language plpgsql as $$ language plpgsql as $$
begin begin
raise exception '[403] insert into test_package not allowed for current subjects % (%)', if ( not hasInsertPermission(
( SELECT customer.uuid FROM
(SELECT * FROM test_customer WHERE uuid = NEW.customerUuid) AS customer
), 'INSERT', 'test_package') ) then
raise exception
'[403] insert into test_package not allowed for current subjects % (%)',
currentSubjects(), currentSubjectsUuids(); currentSubjects(), currentSubjectsUuids();
end if;
return NEW;
end; $$; end; $$;
create trigger test_package_insert_permission_check_tg create trigger test_package_insert_permission_check_tg
before insert on test_package before insert on test_package
for each row for each row
when ( not hasInsertPermission(NEW.customerUuid, 'INSERT', 'test_package') )
execute procedure test_package_insert_permission_missing_tf(); execute procedure test_package_insert_permission_missing_tf();
--// --//
@ -210,8 +220,7 @@ create trigger test_package_insert_permission_check_tg
--changeset test-package-rbac-IDENTITY-VIEW:1 endDelimiter:--// --changeset test-package-rbac-IDENTITY-VIEW:1 endDelimiter:--//
-- ---------------------------------------------------------------------------- -- ----------------------------------------------------------------------------
call generateRbacIdentityViewFromProjection('test_package', call generateRbacIdentityViewFromProjection('test_package', $idName$
$idName$
name name
$idName$); $idName$);
--// --//

View File

@ -106,6 +106,8 @@ begin
if NEW.packageUuid <> OLD.packageUuid then if NEW.packageUuid <> OLD.packageUuid then
call revokePermissionFromRole(getPermissionId(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));
@ -186,22 +188,30 @@ 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. where the check is performed by an indirect role.
A direct role is a role depending on a foreign key directly available in the NEW row. An indirect role is a role FIXME.
*/ */
create or replace function test_domain_insert_permission_missing_tf() create or replace function test_domain_insert_permission_missing_tf()
returns trigger returns trigger
language plpgsql as $$ language plpgsql as $$
begin begin
raise exception '[403] insert into test_domain not allowed for current subjects % (%)', if ( not hasInsertPermission(
( SELECT package.uuid FROM
(SELECT * FROM test_package WHERE uuid = NEW.packageUuid) AS package
), 'INSERT', 'test_domain') ) then
raise exception
'[403] insert into test_domain not allowed for current subjects % (%)',
currentSubjects(), currentSubjectsUuids(); currentSubjects(), currentSubjectsUuids();
end if;
return NEW;
end; $$; end; $$;
create trigger test_domain_insert_permission_check_tg create trigger test_domain_insert_permission_check_tg
before insert on test_domain before insert on test_domain
for each row for each row
when ( not hasInsertPermission(NEW.packageUuid, 'INSERT', 'test_domain') )
execute procedure test_domain_insert_permission_missing_tf(); execute procedure test_domain_insert_permission_missing_tf();
--// --//
@ -209,8 +219,7 @@ create trigger test_domain_insert_permission_check_tg
--changeset test-domain-rbac-IDENTITY-VIEW:1 endDelimiter:--// --changeset test-domain-rbac-IDENTITY-VIEW:1 endDelimiter:--//
-- ---------------------------------------------------------------------------- -- ----------------------------------------------------------------------------
call generateRbacIdentityViewFromProjection('test_domain', call generateRbacIdentityViewFromProjection('test_domain', $idName$
$idName$
name name
$idName$); $idName$);
--// --//

View File

@ -125,8 +125,7 @@ execute procedure hs_office_contact_global_insert_tf();
--changeset hs-office-contact-rbac-IDENTITY-VIEW:1 endDelimiter:--// --changeset hs-office-contact-rbac-IDENTITY-VIEW:1 endDelimiter:--//
-- ---------------------------------------------------------------------------- -- ----------------------------------------------------------------------------
call generateRbacIdentityViewFromProjection('hs_office_contact', call generateRbacIdentityViewFromProjection('hs_office_contact', $idName$
$idName$
label label
$idName$); $idName$);
--// --//

View File

@ -17,11 +17,13 @@ CREATE CAST (character varying as HsOfficePersonType) WITH INOUT AS IMPLICIT;
create table if not exists hs_office_person create table if not exists hs_office_person
( (
uuid uuid unique references RbacObject (uuid) initially deferred, uuid uuid unique references RbacObject (uuid) initially deferred,
personUuid uuid GENERATED ALWAYS AS (uuid) stored, -- see usage in HsOfficePersonEntity
personType HsOfficePersonType not null, personType HsOfficePersonType not null,
tradeName varchar(96), tradeName varchar(96),
givenName varchar(48), givenName varchar(48),
familyName varchar(48) familyName varchar(48)
); );
--//
-- ============================================================================ -- ============================================================================

View File

@ -125,8 +125,7 @@ execute procedure hs_office_person_global_insert_tf();
--changeset hs-office-person-rbac-IDENTITY-VIEW:1 endDelimiter:--// --changeset hs-office-person-rbac-IDENTITY-VIEW:1 endDelimiter:--//
-- ---------------------------------------------------------------------------- -- ----------------------------------------------------------------------------
call generateRbacIdentityViewFromProjection('hs_office_person', call generateRbacIdentityViewFromProjection('hs_office_person', $idName$
$idName$
concat(tradeName, familyName, givenName) concat(tradeName, familyName, givenName)
$idName$); $idName$);
--// --//

View File

@ -37,13 +37,13 @@ declare
begin begin
call enterTriggerForObjectUuid(NEW.uuid); call enterTriggerForObjectUuid(NEW.uuid);
SELECT * FROM hs_office_person WHERE uuid = NEW.holderUuid INTO newHolderPerson; select * from hs_office_person as p where p.uuid = NEW.holderUuid INTO newHolderPerson;
assert newHolderPerson.uuid is not null, format('newHolderPerson must not be null for NEW.holderUuid = %s', NEW.holderUuid); assert newHolderPerson.uuid is not null, format('newHolderPerson must not be null for NEW.holderUuid = %s', NEW.holderUuid);
SELECT * FROM hs_office_person WHERE uuid = NEW.anchorUuid INTO newAnchorPerson; select * from hs_office_person as p where p.uuid = NEW.anchorUuid INTO newAnchorPerson;
assert newAnchorPerson.uuid is not null, format('newAnchorPerson must not be null for NEW.anchorUuid = %s', NEW.anchorUuid); assert newAnchorPerson.uuid is not null, format('newAnchorPerson must not be null for NEW.anchorUuid = %s', NEW.anchorUuid);
SELECT * FROM hs_office_contact WHERE uuid = NEW.contactUuid INTO newContact; select * from hs_office_contact as c where c.uuid = NEW.contactUuid INTO newContact;
assert newContact.uuid is not null, format('newContact must not be null for NEW.contactUuid = %s', NEW.contactUuid); assert newContact.uuid is not null, format('newContact must not be null for NEW.contactUuid = %s', NEW.contactUuid);
@ -65,20 +65,20 @@ begin
perform createRoleWithGrants( perform createRoleWithGrants(
hsOfficeRelationAgent(NEW), hsOfficeRelationAgent(NEW),
incomingSuperRoles => array[ incomingSuperRoles => array[
hsOfficePersonAdmin(newHolderPerson), hsOfficeRelationAdmin(NEW),
hsOfficeRelationAdmin(NEW)] hsOfficePersonAdmin(newHolderPerson)]
); );
perform createRoleWithGrants( perform createRoleWithGrants(
hsOfficeRelationTenant(NEW), hsOfficeRelationTenant(NEW),
permissions => array['SELECT'], permissions => array['SELECT'],
incomingSuperRoles => array[ incomingSuperRoles => array[
hsOfficeContactAdmin(newContact), hsOfficeRelationAgent(NEW),
hsOfficePersonAdmin(newHolderPerson), hsOfficePersonAdmin(newHolderPerson),
hsOfficeRelationAgent(NEW)], hsOfficeContactAdmin(newContact)],
outgoingSubRoles => array[ outgoingSubRoles => array[
hsOfficeContactReferrer(newContact),
hsOfficePersonReferrer(newAnchorPerson), hsOfficePersonReferrer(newAnchorPerson),
hsOfficeContactReferrer(newContact),
hsOfficePersonReferrer(newHolderPerson)] hsOfficePersonReferrer(newHolderPerson)]
); );
@ -130,22 +130,22 @@ declare
begin begin
call enterTriggerForObjectUuid(NEW.uuid); call enterTriggerForObjectUuid(NEW.uuid);
SELECT * FROM hs_office_person WHERE uuid = OLD.holderUuid INTO oldHolderPerson; select * from hs_office_person as p where p.uuid = OLD.holderUuid INTO oldHolderPerson;
assert oldHolderPerson.uuid is not null, format('oldHolderPerson must not be null for OLD.holderUuid = %s', OLD.holderUuid); assert oldHolderPerson.uuid is not null, format('oldHolderPerson must not be null for OLD.holderUuid = %s', OLD.holderUuid);
SELECT * FROM hs_office_person WHERE uuid = NEW.holderUuid INTO newHolderPerson; select * from hs_office_person as p where p.uuid = NEW.holderUuid INTO newHolderPerson;
assert newHolderPerson.uuid is not null, format('newHolderPerson must not be null for NEW.holderUuid = %s', NEW.holderUuid); assert newHolderPerson.uuid is not null, format('newHolderPerson must not be null for NEW.holderUuid = %s', NEW.holderUuid);
SELECT * FROM hs_office_person WHERE uuid = OLD.anchorUuid INTO oldAnchorPerson; select * from hs_office_person as p where p.uuid = OLD.anchorUuid INTO oldAnchorPerson;
assert oldAnchorPerson.uuid is not null, format('oldAnchorPerson must not be null for OLD.anchorUuid = %s', OLD.anchorUuid); assert oldAnchorPerson.uuid is not null, format('oldAnchorPerson must not be null for OLD.anchorUuid = %s', OLD.anchorUuid);
SELECT * FROM hs_office_person WHERE uuid = NEW.anchorUuid INTO newAnchorPerson; select * from hs_office_person as p where p.uuid = NEW.anchorUuid INTO newAnchorPerson;
assert newAnchorPerson.uuid is not null, format('newAnchorPerson must not be null for NEW.anchorUuid = %s', NEW.anchorUuid); assert newAnchorPerson.uuid is not null, format('newAnchorPerson must not be null for NEW.anchorUuid = %s', NEW.anchorUuid);
SELECT * FROM hs_office_contact WHERE uuid = OLD.contactUuid INTO oldContact; select * from hs_office_contact as c where c.uuid = OLD.contactUuid INTO oldContact;
assert oldContact.uuid is not null, format('oldContact must not be null for OLD.contactUuid = %s', OLD.contactUuid); assert oldContact.uuid is not null, format('oldContact must not be null for OLD.contactUuid = %s', OLD.contactUuid);
SELECT * FROM hs_office_contact WHERE uuid = NEW.contactUuid INTO newContact; select * from hs_office_contact as c where c.uuid = NEW.contactUuid INTO newContact;
assert newContact.uuid is not null, format('newContact must not be null for NEW.contactUuid = %s', NEW.contactUuid); assert newContact.uuid is not null, format('newContact must not be null for NEW.contactUuid = %s', NEW.contactUuid);
@ -228,22 +228,30 @@ execute procedure hs_office_relation_hs_office_person_insert_tf();
/** /**
Checks if the user or assumed roles are allowed to insert a row to hs_office_relation, Checks if the user or assumed roles are allowed to insert a row to hs_office_relation,
where the check is performed by a direct role. where the check is performed by an indirect role.
A direct role is a role depending on a foreign key directly available in the NEW row. An indirect role is a role FIXME.
*/ */
create or replace function hs_office_relation_insert_permission_missing_tf() create or replace function hs_office_relation_insert_permission_missing_tf()
returns trigger returns trigger
language plpgsql as $$ language plpgsql as $$
begin begin
raise exception '[403] insert into hs_office_relation not allowed for current subjects % (%)', if ( not hasInsertPermission(
( SELECT anchorPerson.uuid FROM
(select * from hs_office_person as p where p.uuid = NEW.anchorUuid) AS anchorPerson
), 'INSERT', 'hs_office_relation') ) then
raise exception
'[403] insert into hs_office_relation not allowed for current subjects % (%)',
currentSubjects(), currentSubjectsUuids(); currentSubjects(), currentSubjectsUuids();
end if;
return NEW;
end; $$; end; $$;
create trigger hs_office_relation_insert_permission_check_tg create trigger hs_office_relation_insert_permission_check_tg
before insert on hs_office_relation before insert on hs_office_relation
for each row for each row
when ( not hasInsertPermission(NEW.anchorUuid, 'INSERT', 'hs_office_relation') )
execute procedure hs_office_relation_insert_permission_missing_tf(); execute procedure hs_office_relation_insert_permission_missing_tf();
--// --//
@ -251,11 +259,11 @@ create trigger hs_office_relation_insert_permission_check_tg
--changeset hs-office-relation-rbac-IDENTITY-VIEW:1 endDelimiter:--// --changeset hs-office-relation-rbac-IDENTITY-VIEW:1 endDelimiter:--//
-- ---------------------------------------------------------------------------- -- ----------------------------------------------------------------------------
call generateRbacIdentityViewFromProjection('hs_office_relation', call generateRbacIdentityViewFromProjection('hs_office_relation', $idName$
$idName$
(select idName from hs_office_person_iv p where p.uuid = anchorUuid) (select idName from hs_office_person_iv p where p.uuid = anchorUuid)
|| '-with-' || target.type || '-' || '-with-' || target.type || '-'
|| (select idName from hs_office_person_iv p where p.uuid = holderUuid) || (select idName from hs_office_person_iv p where p.uuid = holderUuid)
$idName$); $idName$);
--// --//

View File

@ -36,10 +36,10 @@ declare
begin begin
call enterTriggerForObjectUuid(NEW.uuid); call enterTriggerForObjectUuid(NEW.uuid);
SELECT * FROM hs_office_relation WHERE uuid = NEW.partnerRelUuid INTO newPartnerRel; SELECT * FROM hs_office_relation AS r WHERE r.uuid = NEW.partnerRelUuid INTO newPartnerRel;
assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.partnerRelUuid = %s', NEW.partnerRelUuid); 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; SELECT * FROM hs_office_partner_details AS d WHERE d.uuid = NEW.detailsUuid INTO newPartnerDetails;
assert newPartnerDetails.uuid is not null, format('newPartnerDetails must not be null for NEW.detailsUuid = %s', NEW.detailsUuid); 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, 'DELETE'), hsOfficeRelationAdmin(newPartnerRel));
@ -95,16 +95,16 @@ declare
begin begin
call enterTriggerForObjectUuid(NEW.uuid); call enterTriggerForObjectUuid(NEW.uuid);
SELECT * FROM hs_office_relation WHERE uuid = OLD.partnerRelUuid INTO oldPartnerRel; SELECT * FROM hs_office_relation AS r WHERE r.uuid = OLD.partnerRelUuid INTO oldPartnerRel;
assert oldPartnerRel.uuid is not null, format('oldPartnerRel must not be null for OLD.partnerRelUuid = %s', OLD.partnerRelUuid); 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; SELECT * FROM hs_office_relation AS r WHERE r.uuid = NEW.partnerRelUuid INTO newPartnerRel;
assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.partnerRelUuid = %s', NEW.partnerRelUuid); 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; SELECT * FROM hs_office_partner_details AS d WHERE d.uuid = OLD.detailsUuid INTO oldPartnerDetails;
assert oldPartnerDetails.uuid is not null, format('oldPartnerDetails must not be null for OLD.detailsUuid = %s', OLD.detailsUuid); 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; SELECT * FROM hs_office_partner_details AS d WHERE d.uuid = NEW.detailsUuid INTO newPartnerDetails;
assert newPartnerDetails.uuid is not null, format('newPartnerDetails must not be null for NEW.detailsUuid = %s', NEW.detailsUuid); assert newPartnerDetails.uuid is not null, format('newPartnerDetails must not be null for NEW.detailsUuid = %s', NEW.detailsUuid);
@ -220,8 +220,7 @@ create trigger hs_office_partner_insert_permission_check_tg
--changeset hs-office-partner-rbac-IDENTITY-VIEW:1 endDelimiter:--// --changeset hs-office-partner-rbac-IDENTITY-VIEW:1 endDelimiter:--//
-- ---------------------------------------------------------------------------- -- ----------------------------------------------------------------------------
call generateRbacIdentityViewFromProjection('hs_office_partner', call generateRbacIdentityViewFromProjection('hs_office_partner', $idName$
$idName$
'P-' || partnerNumber 'P-' || partnerNumber
$idName$); $idName$);
--// --//

View File

@ -124,12 +124,12 @@ create trigger hs_office_partner_details_insert_permission_check_tg
--changeset hs-office-partner-details-rbac-IDENTITY-VIEW:1 endDelimiter:--// --changeset hs-office-partner-details-rbac-IDENTITY-VIEW:1 endDelimiter:--//
-- ---------------------------------------------------------------------------- -- ----------------------------------------------------------------------------
call generateRbacIdentityViewFromQuery('hs_office_partner_details', call generateRbacIdentityViewFromQuery('hs_office_partner_details', $idName$
$idName$
SELECT partnerDetails.uuid as uuid, partner_iv.idName || '-details' as idName SELECT partnerDetails.uuid as uuid, partner_iv.idName || '-details' as idName
FROM hs_office_partner_details AS partnerDetails FROM hs_office_partner_details AS partnerDetails
JOIN hs_office_partner partner ON partner.detailsUuid = partnerDetails.uuid JOIN hs_office_partner partner ON partner.detailsUuid = partnerDetails.uuid
JOIN hs_office_partner_iv partner_iv ON partner_iv.uuid = partner.uuid JOIN hs_office_partner_iv partner_iv ON partner_iv.uuid = partner.uuid
$idName$); $idName$);
--// --//

View File

@ -125,8 +125,7 @@ execute procedure hs_office_bankaccount_global_insert_tf();
--changeset hs-office-bankaccount-rbac-IDENTITY-VIEW:1 endDelimiter:--// --changeset hs-office-bankaccount-rbac-IDENTITY-VIEW:1 endDelimiter:--//
-- ---------------------------------------------------------------------------- -- ----------------------------------------------------------------------------
call generateRbacIdentityViewFromProjection('hs_office_bankaccount', call generateRbacIdentityViewFromProjection('hs_office_bankaccount', $idName$
$idName$
iban iban
$idName$); $idName$);
--// --//

View File

@ -72,9 +72,9 @@ begin
hsOfficeSepaMandateReferrer(NEW), hsOfficeSepaMandateReferrer(NEW),
permissions => array['SELECT'], permissions => array['SELECT'],
incomingSuperRoles => array[ incomingSuperRoles => array[
hsOfficeBankAccountAdmin(newBankAccount),
hsOfficeRelationAgent(newDebitorRel), hsOfficeRelationAgent(newDebitorRel),
hsOfficeSepaMandateAgent(NEW)], hsOfficeSepaMandateAgent(NEW),
hsOfficeBankAccountAdmin(newBankAccount)],
outgoingSubRoles => array[hsOfficeRelationTenant(newDebitorRel)] outgoingSubRoles => array[hsOfficeRelationTenant(newDebitorRel)]
); );
@ -149,25 +149,22 @@ execute procedure hs_office_sepamandate_hs_office_relation_insert_tf();
Checks if the user or assumed roles are allowed to insert a row to hs_office_sepamandate, Checks if the user or assumed roles are allowed to insert a row to hs_office_sepamandate,
where the check is performed by an indirect role. 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 An indirect role is a role FIXME.
of the source entity, but needs to be fetched via joined tables.
*/ */
create or replace function hs_office_sepamandate_insert_permission_check_tf() create or replace function hs_office_sepamandate_insert_permission_missing_tf()
returns trigger returns trigger
language plpgsql as $$ language plpgsql as $$
declare
superRoleObjectUuid uuid;
begin begin
superRoleObjectUuid := (SELECT debitorRel.uuid if ( not hasInsertPermission(
( SELECT debitorRel.uuid FROM
(SELECT debitorRel.*
FROM hs_office_relation debitorRel FROM hs_office_relation debitorRel
JOIN hs_office_debitor debitor ON debitor.debitorRelUuid = debitorRel.uuid JOIN hs_office_debitor debitor ON debitor.debitorRelUuid = debitorRel.uuid
WHERE debitor.uuid = NEW.debitorUuid WHERE debitor.uuid = NEW.debitorUuid
); ) AS debitorRel
assert superRoleObjectUuid is not null, 'superRoleObjectUuid must not be null';
if ( not hasInsertPermission(superRoleObjectUuid, 'INSERT', 'hs_office_sepamandate') ) then ), 'INSERT', 'hs_office_sepamandate') ) then
raise exception raise exception
'[403] insert into hs_office_sepamandate not allowed for current subjects % (%)', '[403] insert into hs_office_sepamandate not allowed for current subjects % (%)',
currentSubjects(), currentSubjectsUuids(); currentSubjects(), currentSubjectsUuids();
@ -178,18 +175,18 @@ end; $$;
create trigger hs_office_sepamandate_insert_permission_check_tg create trigger hs_office_sepamandate_insert_permission_check_tg
before insert on hs_office_sepamandate before insert on hs_office_sepamandate
for each row for each row
execute procedure hs_office_sepamandate_insert_permission_check_tf(); execute procedure hs_office_sepamandate_insert_permission_missing_tf();
--// --//
-- ============================================================================ -- ============================================================================
--changeset hs-office-sepamandate-rbac-IDENTITY-VIEW:1 endDelimiter:--// --changeset hs-office-sepamandate-rbac-IDENTITY-VIEW:1 endDelimiter:--//
-- ---------------------------------------------------------------------------- -- ----------------------------------------------------------------------------
call generateRbacIdentityViewFromQuery('hs_office_sepamandate', call generateRbacIdentityViewFromQuery('hs_office_sepamandate', $idName$
$idName$
select sm.uuid as uuid, ba.iban || '-' || sm.validity as idName select sm.uuid as uuid, ba.iban || '-' || sm.validity as idName
from hs_office_sepamandate sm from hs_office_sepamandate sm
join hs_office_bankaccount ba on ba.uuid = sm.bankAccountUuid join hs_office_bankaccount ba on ba.uuid = sm.bankAccountUuid
$idName$); $idName$);
--// --//

View File

@ -46,7 +46,10 @@ begin
INTO newPartnerRel; INTO newPartnerRel;
assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.debitorRelUuid = %s', NEW.debitorRelUuid); assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.debitorRelUuid = %s', NEW.debitorRelUuid);
SELECT * FROM hs_office_relation WHERE uuid = NEW.debitorRelUuid INTO newDebitorRel; SELECT *
FROM hs_office_relation AS r
WHERE r.type = 'DEBITOR' AND r.uuid = NEW.debitorRelUuid
INTO newDebitorRel;
assert newDebitorRel.uuid is not null, format('newDebitorRel must not be null for NEW.debitorRelUuid = %s', NEW.debitorRelUuid); 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; SELECT * FROM hs_office_bankaccount WHERE uuid = NEW.refundBankAccountUuid INTO newRefundBankAccount;
@ -193,8 +196,7 @@ create trigger hs_office_debitor_insert_permission_check_tg
--changeset hs-office-debitor-rbac-IDENTITY-VIEW:1 endDelimiter:--// --changeset hs-office-debitor-rbac-IDENTITY-VIEW:1 endDelimiter:--//
-- ---------------------------------------------------------------------------- -- ----------------------------------------------------------------------------
call generateRbacIdentityViewFromQuery('hs_office_debitor', call generateRbacIdentityViewFromQuery('hs_office_debitor', $idName$
$idName$
SELECT debitor.uuid AS uuid, SELECT debitor.uuid AS uuid,
'D-' || (SELECT partner.partnerNumber 'D-' || (SELECT partner.partnerNumber
FROM hs_office_partner partner FROM hs_office_partner partner
@ -205,6 +207,7 @@ create trigger hs_office_debitor_insert_permission_check_tg
WHERE debitorRel.uuid = debitor.debitorRelUuid) WHERE debitorRel.uuid = debitor.debitorRelUuid)
|| to_char(debitorNumberSuffix, 'fm00') as idName || to_char(debitorNumberSuffix, 'fm00') as idName
FROM hs_office_debitor AS debitor FROM hs_office_debitor AS debitor
$idName$); $idName$);
--// --//

View File

@ -35,10 +35,10 @@ declare
begin begin
call enterTriggerForObjectUuid(NEW.uuid); call enterTriggerForObjectUuid(NEW.uuid);
SELECT partnerRel.* SELECT r.*
FROM hs_office_partner AS partner FROM hs_office_partner AS p
JOIN hs_office_relation AS partnerRel ON partnerRel.uuid = partner.partnerRelUuid JOIN hs_office_relation AS r ON r.uuid = p.partnerRelUuid
WHERE partner.uuid = NEW.partnerUuid WHERE p.uuid = NEW.partnerUuid
INTO newPartnerRel; INTO newPartnerRel;
assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.partnerUuid = %s', NEW.partnerUuid); assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.partnerUuid = %s', NEW.partnerUuid);
@ -54,8 +54,8 @@ begin
hsOfficeMembershipAdmin(NEW), hsOfficeMembershipAdmin(NEW),
permissions => array['UPDATE'], permissions => array['UPDATE'],
incomingSuperRoles => array[ incomingSuperRoles => array[
hsOfficeMembershipOwner(NEW), hsOfficeRelationAgent(newPartnerRel),
hsOfficeRelationAgent(newPartnerRel)] hsOfficeMembershipOwner(NEW)]
); );
perform createRoleWithGrants( perform createRoleWithGrants(
@ -136,25 +136,22 @@ execute procedure hs_office_membership_hs_office_relation_insert_tf();
Checks if the user or assumed roles are allowed to insert a row to hs_office_membership, Checks if the user or assumed roles are allowed to insert a row to hs_office_membership,
where the check is performed by an indirect role. 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 An indirect role is a role FIXME.
of the source entity, but needs to be fetched via joined tables.
*/ */
create or replace function hs_office_membership_insert_permission_check_tf() create or replace function hs_office_membership_insert_permission_missing_tf()
returns trigger returns trigger
language plpgsql as $$ language plpgsql as $$
declare
superRoleObjectUuid uuid;
begin begin
superRoleObjectUuid := (SELECT partnerRel.uuid if ( not hasInsertPermission(
FROM hs_office_partner AS partner ( SELECT partnerRel.uuid FROM
JOIN hs_office_relation AS partnerRel ON partnerRel.uuid = partner.partnerRelUuid
WHERE partner.uuid = NEW.partnerUuid
);
assert superRoleObjectUuid is not null, 'superRoleObjectUuid must not be null';
if ( not hasInsertPermission(superRoleObjectUuid, 'INSERT', 'hs_office_membership') ) then (SELECT r.*
FROM hs_office_partner AS p
JOIN hs_office_relation AS r ON r.uuid = p.partnerRelUuid
WHERE p.uuid = NEW.partnerUuid
) AS partnerRel
), 'INSERT', 'hs_office_membership') ) then
raise exception raise exception
'[403] insert into hs_office_membership not allowed for current subjects % (%)', '[403] insert into hs_office_membership not allowed for current subjects % (%)',
currentSubjects(), currentSubjectsUuids(); currentSubjects(), currentSubjectsUuids();
@ -165,19 +162,19 @@ end; $$;
create trigger hs_office_membership_insert_permission_check_tg create trigger hs_office_membership_insert_permission_check_tg
before insert on hs_office_membership before insert on hs_office_membership
for each row for each row
execute procedure hs_office_membership_insert_permission_check_tf(); execute procedure hs_office_membership_insert_permission_missing_tf();
--// --//
-- ============================================================================ -- ============================================================================
--changeset hs-office-membership-rbac-IDENTITY-VIEW:1 endDelimiter:--// --changeset hs-office-membership-rbac-IDENTITY-VIEW:1 endDelimiter:--//
-- ---------------------------------------------------------------------------- -- ----------------------------------------------------------------------------
call generateRbacIdentityViewFromQuery('hs_office_membership', call generateRbacIdentityViewFromQuery('hs_office_membership', $idName$
$idName$
SELECT m.uuid AS uuid, SELECT m.uuid AS uuid,
'M-' || p.partnerNumber || m.memberNumberSuffix as idName 'M-' || p.partnerNumber || m.memberNumberSuffix as idName
FROM hs_office_membership AS m FROM hs_office_membership AS m
JOIN hs_office_partner AS p ON p.uuid = m.partnerUuid JOIN hs_office_partner AS p ON p.uuid = m.partnerUuid
$idName$); $idName$);
--// --//