avoid nested subselect for insert permission check
This commit is contained in:
parent
66af0def5b
commit
9fdeb047ee
@ -88,7 +88,7 @@ public class HsOfficePartnerDetailsEntity implements HasUuid, Stringifyable {
|
|||||||
|
|
||||||
.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
|
||||||
|
@ -114,8 +114,7 @@ public class InsertTriggerGenerator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
final var superRoleEntityAlias = g.getSuperRoleDef().getEntityAlias();
|
if (g.getSuperRoleDef().getEntityAlias().isFetchedByDirectForeignKey()) {
|
||||||
if (superRoleEntityAlias.fetchSql().part == RbacView.SQL.Part.AUTO_FETCH) {
|
|
||||||
generateInsertPermissionTriggerAllowByRoleOfDirectForeignKey(plPgSql, g);
|
generateInsertPermissionTriggerAllowByRoleOfDirectForeignKey(plPgSql, g);
|
||||||
} else {
|
} else {
|
||||||
generateInsertPermissionTriggerAllowByRoleOfIndirectForeignKey(plPgSql, g);
|
generateInsertPermissionTriggerAllowByRoleOfIndirectForeignKey(plPgSql, g);
|
||||||
@ -164,24 +163,23 @@ public class InsertTriggerGenerator {
|
|||||||
|
|
||||||
An indirect role is a role FIXME.
|
An indirect role is a role FIXME.
|
||||||
*/
|
*/
|
||||||
create or replace function ${rawSubTable}_insert_permission_missing_tf()
|
create or replace function ${rawSubTable}_insert_permission_check_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()));
|
||||||
with("varName", g.getSuperRoleDef().getEntityAlias().aliasName()));
|
plPgSql.indented(2, () -> {
|
||||||
plPgSql.indented(3, () -> {
|
|
||||||
plPgSql.writeLn(
|
plPgSql.writeLn(
|
||||||
"(" + g.getSuperRoleDef().getEntityAlias().fetchSql().sql + ") AS ${varName}",
|
"superRoleObjectUuid := (" + g.getSuperRoleDef().getEntityAlias().fetchSql().sql + ");",
|
||||||
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("""
|
||||||
|
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();
|
||||||
@ -192,7 +190,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_missing_tf();
|
execute procedure ${rawSubTable}_insert_permission_check_tf();
|
||||||
|
|
||||||
""",
|
""",
|
||||||
with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableName()));
|
with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableName()));
|
||||||
|
@ -34,6 +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.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;
|
||||||
|
|
||||||
@ -839,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) {
|
||||||
@ -909,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);
|
||||||
}
|
}
|
||||||
@ -929,7 +941,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, Part.AUTO_FETCH);
|
return new SQL(null, AUTO_FETCH);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -189,30 +189,22 @@ 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 an indirect role.
|
where the check is performed by a direct role.
|
||||||
|
|
||||||
An indirect role is a role FIXME.
|
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
|
||||||
language plpgsql as $$
|
language plpgsql as $$
|
||||||
begin
|
begin
|
||||||
if ( not hasInsertPermission(
|
raise exception '[403] insert into test_package not allowed for current subjects % (%)',
|
||||||
( SELECT customer.uuid FROM
|
currentSubjects(), currentSubjectsUuids();
|
||||||
|
|
||||||
(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();
|
|
||||||
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();
|
||||||
--//
|
--//
|
||||||
|
|
||||||
|
@ -188,30 +188,22 @@ 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 an indirect role.
|
where the check is performed by a direct role.
|
||||||
|
|
||||||
An indirect role is a role FIXME.
|
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
|
||||||
language plpgsql as $$
|
language plpgsql as $$
|
||||||
begin
|
begin
|
||||||
if ( not hasInsertPermission(
|
raise exception '[403] insert into test_domain not allowed for current subjects % (%)',
|
||||||
( SELECT package.uuid FROM
|
currentSubjects(), currentSubjectsUuids();
|
||||||
|
|
||||||
(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();
|
|
||||||
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();
|
||||||
--//
|
--//
|
||||||
|
|
||||||
|
@ -54,8 +54,8 @@ begin
|
|||||||
hsOfficeRelationAdmin(NEW),
|
hsOfficeRelationAdmin(NEW),
|
||||||
permissions => array['UPDATE'],
|
permissions => array['UPDATE'],
|
||||||
incomingSuperRoles => array[
|
incomingSuperRoles => array[
|
||||||
hsOfficePersonAdmin(newAnchorPerson),
|
hsOfficeRelationOwner(NEW),
|
||||||
hsOfficeRelationOwner(NEW)]
|
hsOfficePersonAdmin(newAnchorPerson)]
|
||||||
);
|
);
|
||||||
|
|
||||||
perform createRoleWithGrants(
|
perform createRoleWithGrants(
|
||||||
@ -69,13 +69,13 @@ begin
|
|||||||
hsOfficeRelationTenant(NEW),
|
hsOfficeRelationTenant(NEW),
|
||||||
permissions => array['SELECT'],
|
permissions => array['SELECT'],
|
||||||
incomingSuperRoles => array[
|
incomingSuperRoles => array[
|
||||||
hsOfficeRelationAgent(NEW),
|
|
||||||
hsOfficeContactAdmin(newContact),
|
hsOfficeContactAdmin(newContact),
|
||||||
hsOfficePersonAdmin(newHolderPerson)],
|
hsOfficePersonAdmin(newHolderPerson),
|
||||||
|
hsOfficeRelationAgent(NEW)],
|
||||||
outgoingSubRoles => array[
|
outgoingSubRoles => array[
|
||||||
hsOfficePersonReferrer(newAnchorPerson),
|
hsOfficePersonReferrer(newHolderPerson),
|
||||||
hsOfficeContactReferrer(newContact),
|
hsOfficeContactReferrer(newContact),
|
||||||
hsOfficePersonReferrer(newHolderPerson)]
|
hsOfficePersonReferrer(newAnchorPerson)]
|
||||||
);
|
);
|
||||||
|
|
||||||
call leaveTriggerForObjectUuid(NEW.uuid);
|
call leaveTriggerForObjectUuid(NEW.uuid);
|
||||||
|
@ -35,7 +35,7 @@ declare
|
|||||||
begin
|
begin
|
||||||
call enterTriggerForObjectUuid(NEW.uuid);
|
call enterTriggerForObjectUuid(NEW.uuid);
|
||||||
|
|
||||||
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 = NEW.uuid
|
ON partner.detailsUuid = NEW.uuid
|
||||||
|
@ -57,17 +57,17 @@ begin
|
|||||||
hsOfficeSepaMandateAgent(NEW),
|
hsOfficeSepaMandateAgent(NEW),
|
||||||
incomingSuperRoles => array[hsOfficeSepaMandateAdmin(NEW)],
|
incomingSuperRoles => array[hsOfficeSepaMandateAdmin(NEW)],
|
||||||
outgoingSubRoles => array[
|
outgoingSubRoles => array[
|
||||||
hsOfficeBankAccountReferrer(newBankAccount),
|
hsOfficeRelationAgent(newDebitorRel),
|
||||||
hsOfficeRelationAgent(newDebitorRel)]
|
hsOfficeBankAccountReferrer(newBankAccount)]
|
||||||
);
|
);
|
||||||
|
|
||||||
perform createRoleWithGrants(
|
perform createRoleWithGrants(
|
||||||
hsOfficeSepaMandateReferrer(NEW),
|
hsOfficeSepaMandateReferrer(NEW),
|
||||||
permissions => array['SELECT'],
|
permissions => array['SELECT'],
|
||||||
incomingSuperRoles => array[
|
incomingSuperRoles => array[
|
||||||
|
hsOfficeSepaMandateAgent(NEW),
|
||||||
hsOfficeRelationAgent(newDebitorRel),
|
hsOfficeRelationAgent(newDebitorRel),
|
||||||
hsOfficeBankAccountAdmin(newBankAccount),
|
hsOfficeBankAccountAdmin(newBankAccount)],
|
||||||
hsOfficeSepaMandateAgent(NEW)],
|
|
||||||
outgoingSubRoles => array[hsOfficeRelationTenant(newDebitorRel)]
|
outgoingSubRoles => array[hsOfficeRelationTenant(newDebitorRel)]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user