generate indirect permission by indirect foreign key depending on directlyFetchedByDependsOnColumn vs. fetchedBySql

This commit is contained in:
Michael Hoennig 2024-03-25 06:08:36 +01:00
parent e6ef5b59c7
commit 29c7708188
11 changed files with 66 additions and 69 deletions

View File

@ -25,6 +25,7 @@ import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NULLABLE; 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.directlyFetchedByDependsOnColumn;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.fetchedBySql; 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;
@ -161,11 +162,7 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
.importEntityAlias("refundBankAccount", HsOfficeBankAccountEntity.class, .importEntityAlias("refundBankAccount", HsOfficeBankAccountEntity.class,
dependsOnColumn("refundBankAccountUuid"), dependsOnColumn("refundBankAccountUuid"),
fetchedBySql(""" directlyFetchedByDependsOnColumn(),
SELECT *
FROM hs_office_bankaccount AS b
WHERE b.uuid = ${REF}.refundBankAccountUuid
"""),
NULLABLE) NULLABLE)
.toRole("refundBankAccount", ADMIN).grantRole("debitorRel", AGENT) .toRole("refundBankAccount", ADMIN).grantRole("debitorRel", AGENT)
.toRole("debitorRel", AGENT).grantRole("refundBankAccount", REFERRER) .toRole("debitorRel", AGENT).grantRole("refundBankAccount", REFERRER)

View File

@ -115,7 +115,7 @@ public class HsOfficeSepaMandateEntity implements Stringifyable, HasUuid {
NOT_NULL) NOT_NULL)
.importEntityAlias("bankAccount", HsOfficeBankAccountEntity.class, .importEntityAlias("bankAccount", HsOfficeBankAccountEntity.class,
dependsOnColumn("bankAccountUuid"), dependsOnColumn("bankAccountUuid"),
autoFetched(), directlyFetchedByDependsOnColumn(),
NOT_NULL) NOT_NULL)
.createRole(OWNER, (with) -> { .createRole(OWNER, (with) -> {

View File

@ -115,14 +115,11 @@ public class InsertTriggerGenerator {
} }
} else { } else {
final var superRoleEntityAlias = g.getSuperRoleDef().getEntityAlias(); final var superRoleEntityAlias = g.getSuperRoleDef().getEntityAlias();
if (superRoleEntityAlias.fetchSql().part == RbacView.SQL.Part.AUTO_FETCH) {
// TODO: Maybe this should depend on the indirection degree of the fetchSql?
// Maybe we need a separate fetchedBy method for all the simple, direct cases?
if (superRoleEntityAlias.fetchSql().sql.contains("JOIN ")) {
generateInsertPermissionTriggerAllowByRoleOfIndirectForeignKey(plPgSql, g);
} else {
generateInsertPermissionTriggerAllowByRoleOfDirectForeignKey(plPgSql, g); generateInsertPermissionTriggerAllowByRoleOfDirectForeignKey(plPgSql, g);
} else {
generateInsertPermissionTriggerAllowByRoleOfIndirectForeignKey(plPgSql, g);
} }
} }
}, },

View File

@ -34,7 +34,7 @@ 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.autoFetched; 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
@ -343,7 +343,7 @@ public class RbacView {
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, null); importEntityAliasImpl(aliasName, entityClass, directlyFetchedByDependsOnColumn(), dependsOnColum, false, null);
return this; return this;
} }
@ -928,7 +928,7 @@ 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, Part.AUTO_FETCH);
} }

View File

@ -17,7 +17,7 @@ import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnCo
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.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
@ -50,10 +50,7 @@ public class TestDomainEntity implements HasUuid {
.importEntityAlias("package", TestPackageEntity.class, .importEntityAlias("package", TestPackageEntity.class,
dependsOnColumn("packageUuid"), dependsOnColumn("packageUuid"),
fetchedBySql(""" directlyFetchedByDependsOnColumn(),
SELECT * FROM test_package p
WHERE p.uuid= ${ref}.packageUuid
"""),
NOT_NULL) NOT_NULL)
.toRole("package", ADMIN).grantPermission(INSERT) .toRole("package", ADMIN).grantPermission(INSERT)

View File

@ -51,10 +51,7 @@ public class TestPackageEntity implements HasUuid {
.importEntityAlias("customer", TestCustomerEntity.class, .importEntityAlias("customer", TestCustomerEntity.class,
dependsOnColumn("customerUuid"), dependsOnColumn("customerUuid"),
fetchedBySql(""" directlyFetchedByDependsOnColumn(),
SELECT * FROM test_customer c
WHERE c.uuid= ${ref}.customerUuid
"""),
NOT_NULL) NOT_NULL)
.toRole("customer", ADMIN).grantPermission(INSERT) .toRole("customer", ADMIN).grantPermission(INSERT)

View File

@ -35,9 +35,7 @@ declare
begin begin
call enterTriggerForObjectUuid(NEW.uuid); call enterTriggerForObjectUuid(NEW.uuid);
SELECT * FROM test_customer c SELECT * FROM test_customer WHERE uuid = NEW.customerUuid INTO newCustomer;
WHERE c.uuid= NEW.customerUuid
INTO newCustomer;
assert newCustomer.uuid is not null, format('newCustomer must not be null for NEW.customerUuid = %s', NEW.customerUuid); assert newCustomer.uuid is not null, format('newCustomer must not be null for NEW.customerUuid = %s', NEW.customerUuid);
@ -103,14 +101,10 @@ 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
INTO oldCustomer;
assert oldCustomer.uuid is not null, format('oldCustomer must not be null for OLD.customerUuid = %s', OLD.customerUuid); assert oldCustomer.uuid is not null, format('oldCustomer must not be null for OLD.customerUuid = %s', OLD.customerUuid);
SELECT * FROM test_customer c SELECT * FROM test_customer WHERE uuid = NEW.customerUuid INTO newCustomer;
WHERE c.uuid= NEW.customerUuid
INTO newCustomer;
assert newCustomer.uuid is not null, format('newCustomer must not be null for NEW.customerUuid = %s', NEW.customerUuid); assert newCustomer.uuid is not null, format('newCustomer must not be null for NEW.customerUuid = %s', NEW.customerUuid);
@ -195,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();
--// --//

View File

@ -35,9 +35,7 @@ declare
begin begin
call enterTriggerForObjectUuid(NEW.uuid); call enterTriggerForObjectUuid(NEW.uuid);
SELECT * FROM test_package p SELECT * FROM test_package WHERE uuid = NEW.packageUuid INTO newPackage;
WHERE p.uuid= NEW.packageUuid
INTO newPackage;
assert newPackage.uuid is not null, format('newPackage must not be null for NEW.packageUuid = %s', NEW.packageUuid); assert newPackage.uuid is not null, format('newPackage must not be null for NEW.packageUuid = %s', NEW.packageUuid);
@ -99,14 +97,10 @@ 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
INTO oldPackage;
assert oldPackage.uuid is not null, format('oldPackage must not be null for OLD.packageUuid = %s', OLD.packageUuid); assert oldPackage.uuid is not null, format('oldPackage must not be null for OLD.packageUuid = %s', OLD.packageUuid);
SELECT * FROM test_package p SELECT * FROM test_package WHERE uuid = NEW.packageUuid INTO newPackage;
WHERE p.uuid= NEW.packageUuid
INTO newPackage;
assert newPackage.uuid is not null, format('newPackage must not be null for NEW.packageUuid = %s', NEW.packageUuid); assert newPackage.uuid is not null, format('newPackage must not be null for NEW.packageUuid = %s', NEW.packageUuid);
@ -194,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();
--// --//

View File

@ -58,8 +58,8 @@ begin
hsOfficeRelationAdmin(NEW), hsOfficeRelationAdmin(NEW),
permissions => array['UPDATE'], permissions => array['UPDATE'],
incomingSuperRoles => array[ incomingSuperRoles => array[
hsOfficeRelationOwner(NEW), hsOfficePersonAdmin(newAnchorPerson),
hsOfficePersonAdmin(newAnchorPerson)] hsOfficeRelationOwner(NEW)]
); );
perform createRoleWithGrants( perform createRoleWithGrants(
@ -73,13 +73,13 @@ begin
hsOfficeRelationTenant(NEW), hsOfficeRelationTenant(NEW),
permissions => array['SELECT'], permissions => array['SELECT'],
incomingSuperRoles => array[ incomingSuperRoles => array[
hsOfficeRelationAgent(NEW),
hsOfficePersonAdmin(newHolderPerson), hsOfficePersonAdmin(newHolderPerson),
hsOfficeContactAdmin(newContact), hsOfficeContactAdmin(newContact)],
hsOfficeRelationAgent(NEW)],
outgoingSubRoles => array[ outgoingSubRoles => array[
hsOfficePersonReferrer(newHolderPerson),
hsOfficePersonReferrer(newAnchorPerson), hsOfficePersonReferrer(newAnchorPerson),
hsOfficeContactReferrer(newContact)] hsOfficeContactReferrer(newContact),
hsOfficePersonReferrer(newHolderPerson)]
); );
call leaveTriggerForObjectUuid(NEW.uuid); call leaveTriggerForObjectUuid(NEW.uuid);
@ -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();
--// --//

View File

@ -72,9 +72,9 @@ begin
hsOfficeSepaMandateReferrer(NEW), hsOfficeSepaMandateReferrer(NEW),
permissions => array['SELECT'], permissions => array['SELECT'],
incomingSuperRoles => array[ incomingSuperRoles => array[
hsOfficeRelationAgent(newDebitorRel),
hsOfficeSepaMandateAgent(NEW), hsOfficeSepaMandateAgent(NEW),
hsOfficeBankAccountAdmin(newBankAccount), hsOfficeBankAccountAdmin(newBankAccount)],
hsOfficeRelationAgent(newDebitorRel)],
outgoingSubRoles => array[hsOfficeRelationTenant(newDebitorRel)] outgoingSubRoles => array[hsOfficeRelationTenant(newDebitorRel)]
); );

View File

@ -52,10 +52,7 @@ begin
INTO newDebitorRel; 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 * SELECT * FROM hs_office_bankaccount WHERE uuid = NEW.refundBankAccountUuid INTO newRefundBankAccount;
FROM hs_office_bankaccount AS b
WHERE b.uuid = NEW.refundBankAccountUuid
INTO newRefundBankAccount;
call grantRoleToRole(hsOfficeBankAccountReferrer(newRefundBankAccount), hsOfficeRelationAgent(newDebitorRel)); call grantRoleToRole(hsOfficeBankAccountReferrer(newRefundBankAccount), hsOfficeRelationAgent(newDebitorRel));
call grantRoleToRole(hsOfficeRelationAdmin(newDebitorRel), hsOfficeRelationAdmin(newPartnerRel)); call grantRoleToRole(hsOfficeRelationAdmin(newDebitorRel), hsOfficeRelationAdmin(newPartnerRel));