Compare commits

..

3 Commits

11 changed files with 89 additions and 83 deletions

View File

@ -1,9 +1,11 @@
package net.hostsharing.hsadminng.rbac.rbacdef; package net.hostsharing.hsadminng.rbac.rbacdef;
import java.util.Optional; import java.util.Optional;
import java.util.Set;
import java.util.function.BinaryOperator; import java.util.function.BinaryOperator;
import java.util.stream.Stream; import java.util.stream.Stream;
import static java.util.stream.Collectors.joining;
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.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;
@ -25,7 +27,6 @@ public class InsertTriggerGenerator {
void generateTo(final StringWriter plPgSql) { void generateTo(final StringWriter plPgSql) {
generateInsertGrants(plPgSql); generateInsertGrants(plPgSql);
generateInsertPermissionChecks(plPgSql); generateInsertPermissionChecks(plPgSql);
plPgSql.writeLn("--//");
} }
private void generateInsertGrants(final StringWriter plPgSql) { private void generateInsertGrants(final StringWriter plPgSql) {
@ -53,35 +54,45 @@ public class InsertTriggerGenerator {
plPgSql.writeLn(""" plPgSql.writeLn("""
-- granting INSERT permission to ${rawSubTable} ---------------------------- -- granting INSERT permission to ${rawSubTable} ----------------------------
""", """,
with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableName())); with("rawSubTable", g.getSuperRoleDef().getEntityAlias().getRawTableName()));
plPgSql.writeLn(""" if (isGrantToADifferentTable(g)) {
/* plPgSql.writeLn(
Grants INSERT INTO ${rawSubTable} permissions to specified role of pre-existing ${rawSuperTable} rows. """
*/ /*
do language plpgsql $$ Grants INSERT INTO ${rawSubTable} permissions to specified role of pre-existing ${rawSuperTable} rows.
declare */
preExistingRow ${rawSuperTable}; do language plpgsql $$
begin declare
call defineContext('create INSERT INTO ${rawSubTable} permissions for pre-exising ${rawSuperTable} rows'); preExistingRow ${rawSuperTable};
begin
FOR preExistingRow IN SELECT * FROM ${rawSuperTable} call defineContext('create INSERT INTO ${rawSubTable} permissions for pre-exising ${rawSuperTable} rows');
${whenCondition}
LOOP FOR preExistingRow IN SELECT * FROM ${rawSuperTable}
call grantPermissionToRole( ${whenCondition}
createPermission(preExistingRow.uuid, 'INSERT', '${rawSubTable}'), LOOP
hsBookingItemAGENT(preExistingRow)); call grantPermissionToRole(
END LOOP; createPermission(preExistingRow.uuid, 'INSERT', '${rawSubTable}'),
end; hsBookingItemAGENT(preExistingRow));
$$; END LOOP;
""", end;
with("whenCondition", g.getSuperRoleDef().getEntityAlias().isCaseDependent() $$;
// TODO.impl: .type needs to be dynamically generated """,
? "WHERE preExistingRow.type = '${value}'" with("whenCondition", g.getSuperRoleDef().getEntityAlias().isCaseDependent()
// TODO.impl: .type needs to be dynamically generated
? "WHERE preExistingRow.type = '${value}'"
.replace("${value}", g.getSuperRoleDef().getEntityAlias().usingCase().value) .replace("${value}", g.getSuperRoleDef().getEntityAlias().usingCase().value)
: "-- unconditional for all rows in that table"), : "-- unconditional for all rows in that table"),
with("rawSuperTable", g.getSuperRoleDef().getEntityAlias().getRawTableName()), with("rawSuperTable", g.getSuperRoleDef().getEntityAlias().getRawTableName()),
with("rawSubTable", g.getPermDef().getEntityAlias().getRawTableName())); with("rawSubTable", g.getPermDef().getEntityAlias().getRawTableName()));
} else {
plPgSql.writeLn("""
-- Granting INSERT INTO hs_hosting_asset permissions to specified role of pre-existing hs_hosting_asset rows slipped,
-- because there cannot yet be any pre-existing rows in the same table yet.
""",
with("rawSuperTable", g.getSuperRoleDef().getEntityAlias().getRawTableName()),
with("rawSubTable", g.getPermDef().getEntityAlias().getRawTableName()));
}
plPgSql.writeLn(""" plPgSql.writeLn("""
/** /**
@ -150,7 +161,6 @@ public class InsertTriggerGenerator {
plPgSql.writeLn("--//"); plPgSql.writeLn("--//");
} }
private void generateInsertPermissionChecks(final StringWriter plPgSql) { private void generateInsertPermissionChecks(final StringWriter plPgSql) {
plPgSql.writeLn(""" plPgSql.writeLn("""
-- ============================================================================ -- ============================================================================
@ -177,20 +187,30 @@ public class InsertTriggerGenerator {
when ( not ( when ( not (
""", """,
with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableName())); with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableName()));
plPgSql.chopEmptyLines();
plPgSql.indented(2, () -> { plPgSql.indented(3, () -> {
getInsertGrants().forEach(g -> { getInsertGrants().forEach(g -> {
final RbacView.EntityAlias superRoleEntityAlias = g.getSuperRoleDef().getEntityAlias(); final RbacView.EntityAlias superRoleEntityAlias = g.getSuperRoleDef().getEntityAlias();
final RbacView.EntityAlias permissionEntityAlias = g.getPermDef().entityAlias;
final var caseCondition = superRoleEntityAlias.isCaseDependent() final var caseCondition = superRoleEntityAlias.isCaseDependent()
? "NEW.type = '" + superRoleEntityAlias.usingCase().value + "' and " ? ("NEW.type in (" + toStringList(g.getForCases()) + ") and ")
: ""; : "";
plPgSql.writeLn("${caseCondition}hasInsertPermission(NEW.${refColumn}, 'INSERT', '${rawSubTable}') or", if ( g.getSuperRoleDef().isGlobalAdmin() ) {
with("caseCondition", caseCondition), plPgSql.writeLn(
with("refColumn", superRoleEntityAlias.dependsOnColumName()), "${caseCondition}isGlobalAdmin() or",
with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableName())); with("caseCondition", caseCondition));
} else {
plPgSql.writeLn(
"${caseCondition}hasInsertPermission(NEW.${refColumn}, '${rawSubTable}') or",
with("caseCondition", caseCondition),
with("refColumn", superRoleEntityAlias.dependsOnColumName()),
with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableName()));
}
}); });
plPgSql.chopTail(" or\n"); plPgSql.chopTail(" or\n");
}); });
plPgSql.writeLn();
plPgSql.writeLn(""" plPgSql.writeLn("""
) ) ) )
@ -200,6 +220,14 @@ public class InsertTriggerGenerator {
with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableName())); with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableName()));
} }
private String toStringList(final Set<RbacView.CaseDef> cases) {
return cases.stream().map(c -> "'" + c.value + "'").collect(joining(", "));
}
private boolean isGrantToADifferentTable(final RbacView.RbacGrantDefinition g) {
return !rbacDef.getRootEntityAlias().getRawTableName().equals(g.getSuperRoleDef().getEntityAlias().getRawTableName());
}
private Stream<RbacView.RbacGrantDefinition> getInsertGrants() { private Stream<RbacView.RbacGrantDefinition> getInsertGrants() {
return rbacDef.getGrantDefs().stream() return rbacDef.getGrantDefs().stream()
.filter(g -> g.grantType() == PERM_TO_ROLE) .filter(g -> g.grantType() == PERM_TO_ROLE)

View File

@ -560,11 +560,13 @@ public class RbacView {
register(this); register(this);
} }
public RbacGrantDefinition(final RbacPermissionDefinition permDef, final RbacRoleDefinition roleDef) { public RbacGrantDefinition(final RbacPermissionDefinition permDef, final RbacRoleDefinition roleDef,
final CaseDef forCase) {
this.userDef = null; this.userDef = null;
this.subRoleDef = null; this.subRoleDef = null;
this.superRoleDef = roleDef; this.superRoleDef = roleDef;
this.permDef = permDef; this.permDef = permDef;
this.forCases = forCase != null ? hashSet(forCase) : null;
register(this); register(this);
} }
@ -676,7 +678,8 @@ public class RbacView {
final String tableName; final String tableName;
final boolean toCreate; final boolean toCreate;
private RbacPermissionDefinition(final EntityAlias entityAlias, final Permission permission, final String tableName, final boolean toCreate) { private RbacPermissionDefinition(final EntityAlias entityAlias, final Permission permission, final String tableName,
final boolean toCreate) {
this.entityAlias = entityAlias; this.entityAlias = entityAlias;
this.permission = permission; this.permission = permission;
this.tableName = tableName; this.tableName = tableName;
@ -788,6 +791,10 @@ public class RbacView {
public String toString() { public String toString() {
return "role:" + entityAlias.aliasName + role; return "role:" + entityAlias.aliasName + role;
} }
public boolean isGlobalAdmin() {
return entityAlias.isGlobal() && role == Role.ADMIN;
}
} }
public RbacUserReference findUserRef(final RbacUserReference.UserRole userRole) { public RbacUserReference findUserRef(final RbacUserReference.UserRole userRole) {
@ -842,19 +849,6 @@ public class RbacView {
.orElseGet(() -> new RbacPermissionDefinition(entityAlias, perm, tableName, true)); // TODO: true => toCreate .orElseGet(() -> new RbacPermissionDefinition(entityAlias, perm, tableName, true)); // TODO: true => toCreate
} }
RbacPermissionDefinition findRbacPerm(final EntityAlias entityAlias, final Permission perm) {
return findRbacPerm(entityAlias, perm, null);
}
public RbacPermissionDefinition findRbacPerm(final String entityAliasName, final Permission perm, String tableName) {
return findRbacPerm(findEntityAlias(entityAliasName), perm, tableName);
}
public RbacPermissionDefinition findRbacPerm(final String entityAliasName, final Permission perm) {
return findRbacPerm(findEntityAlias(entityAliasName), perm);
}
private RbacGrantDefinition findOrCreateGrantDef(final RbacRoleDefinition roleDefinition, final RbacUserReference user) { private RbacGrantDefinition findOrCreateGrantDef(final RbacRoleDefinition roleDefinition, final RbacUserReference user) {
return grantDefs.stream() return grantDefs.stream()
.filter(g -> g.subRoleDef == roleDefinition && g.userDef == user) .filter(g -> g.subRoleDef == roleDefinition && g.userDef == user)
@ -866,7 +860,7 @@ public class RbacView {
return grantDefs.stream() return grantDefs.stream()
.filter(g -> g.permDef == permDef && g.superRoleDef == roleDef) .filter(g -> g.permDef == permDef && g.superRoleDef == roleDef)
.findFirst() .findFirst()
.orElseGet(() -> new RbacGrantDefinition(permDef, roleDef)); .orElseGet(() -> new RbacGrantDefinition(permDef, roleDef, processingCase));
} }
private RbacGrantDefinition findOrCreateGrantDef( private RbacGrantDefinition findOrCreateGrantDef(

View File

@ -569,14 +569,14 @@ select exists(
); );
$$; $$;
create or replace function hasInsertPermission(objectUuid uuid, forOp RbacOp, tableName text ) create or replace function hasInsertPermission(objectUuid uuid, tableName text )
returns BOOL returns BOOL
stable -- leakproof stable -- leakproof
language plpgsql as $$ language plpgsql as $$
declare declare
permissionUuid uuid; permissionUuid uuid;
begin begin
permissionUuid = findPermissionId(objectUuid, forOp, tableName); permissionUuid = findPermissionId(objectUuid, 'INSERT'::RbacOp, tableName);
return permissionUuid is not null; return permissionUuid is not null;
end; end;
$$; $$;

View File

@ -200,7 +200,7 @@ 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') ) when ( not hasInsertPermission(NEW.customerUuid, 'test_package') )
execute procedure test_package_insert_permission_missing_tf(); execute procedure test_package_insert_permission_missing_tf();
--// --//

View File

@ -199,7 +199,7 @@ 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') ) when ( not hasInsertPermission(NEW.packageUuid, 'test_domain') )
execute procedure test_domain_insert_permission_missing_tf(); execute procedure test_domain_insert_permission_missing_tf();
--// --//

View File

@ -209,7 +209,7 @@ 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') ) when ( not hasInsertPermission(NEW.anchorUuid, 'hs_office_relation') )
execute procedure hs_office_relation_insert_permission_missing_tf(); execute procedure hs_office_relation_insert_permission_missing_tf();
--// --//

View File

@ -168,7 +168,7 @@ begin
); );
assert superRoleObjectUuid is not null, 'superRoleObjectUuid must not be null'; assert superRoleObjectUuid is not null, 'superRoleObjectUuid must not be null';
if ( not hasInsertPermission(superRoleObjectUuid, 'INSERT', 'hs_office_sepamandate') ) then if ( not hasInsertPermission(superRoleObjectUuid, '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();

View File

@ -123,7 +123,7 @@ end; $$;
create trigger hs_office_coopsharestransaction_insert_permission_check_tg create trigger hs_office_coopsharestransaction_insert_permission_check_tg
before insert on hs_office_coopsharestransaction before insert on hs_office_coopsharestransaction
for each row for each row
when ( not hasInsertPermission(NEW.membershipUuid, 'INSERT', 'hs_office_coopsharestransaction') ) when ( not hasInsertPermission(NEW.membershipUuid, 'hs_office_coopsharestransaction') )
execute procedure hs_office_coopsharestransaction_insert_permission_missing_tf(); execute procedure hs_office_coopsharestransaction_insert_permission_missing_tf();
--// --//

View File

@ -123,7 +123,7 @@ end; $$;
create trigger hs_office_coopassetstransaction_insert_permission_check_tg create trigger hs_office_coopassetstransaction_insert_permission_check_tg
before insert on hs_office_coopassetstransaction before insert on hs_office_coopassetstransaction
for each row for each row
when ( not hasInsertPermission(NEW.membershipUuid, 'INSERT', 'hs_office_coopassetstransaction') ) when ( not hasInsertPermission(NEW.membershipUuid, 'hs_office_coopassetstransaction') )
execute procedure hs_office_coopassetstransaction_insert_permission_missing_tf(); execute procedure hs_office_coopassetstransaction_insert_permission_missing_tf();
--// --//

View File

@ -164,7 +164,7 @@ begin
); );
assert superRoleObjectUuid is not null, 'superRoleObjectUuid must not be null'; assert superRoleObjectUuid is not null, 'superRoleObjectUuid must not be null';
if ( not hasInsertPermission(superRoleObjectUuid, 'INSERT', 'hs_booking_item') ) then if ( not hasInsertPermission(superRoleObjectUuid, 'hs_booking_item') ) then
raise exception raise exception
'[403] insert into hs_booking_item not allowed for current subjects % (%)', '[403] insert into hs_booking_item not allowed for current subjects % (%)',
currentSubjects(), currentSubjectsUuids(); currentSubjects(), currentSubjectsUuids();

View File

@ -93,7 +93,7 @@ execute procedure insertTriggerForHsHostingAsset_tf();
--changeset hs-hosting-asset-rbac-GRANTING-INSERT-PERMISSION:1 endDelimiter:--// --changeset hs-hosting-asset-rbac-GRANTING-INSERT-PERMISSION:1 endDelimiter:--//
-- ---------------------------------------------------------------------------- -- ----------------------------------------------------------------------------
-- granting INSERT permission to hs_hosting_asset ---------------------------- -- granting INSERT permission to hs_booking_item ----------------------------
/* /*
Grants INSERT INTO hs_hosting_asset permissions to specified role of pre-existing hs_booking_item rows. Grants INSERT INTO hs_hosting_asset permissions to specified role of pre-existing hs_booking_item rows.
@ -138,24 +138,8 @@ execute procedure new_hs_hosting_asset_grants_insert_to_hs_booking_item_tf();
-- granting INSERT permission to hs_hosting_asset ---------------------------- -- granting INSERT permission to hs_hosting_asset ----------------------------
/* -- Granting INSERT INTO hs_hosting_asset permissions to specified role of pre-existing hs_hosting_asset rows slipped,
Grants INSERT INTO hs_hosting_asset permissions to specified role of pre-existing hs_hosting_asset rows. -- because there cannot yet be any pre-existing rows in the same table yet.
*/
do language plpgsql $$
declare
preExistingRow hs_hosting_asset;
begin
call defineContext('create INSERT INTO hs_hosting_asset permissions for pre-exising hs_hosting_asset rows');
FOR preExistingRow IN SELECT * FROM hs_hosting_asset
WHERE preExistingRow.type = 'MANAGED_SERVER'
LOOP
call grantPermissionToRole(
createPermission(preExistingRow.uuid, 'INSERT', 'hs_hosting_asset'),
hsBookingItemAGENT(preExistingRow));
END LOOP;
end;
$$;
/** /**
Grants hs_hosting_asset INSERT permission to specified role of new hs_hosting_asset rows. Grants hs_hosting_asset INSERT permission to specified role of new hs_hosting_asset rows.
@ -202,12 +186,12 @@ create trigger hs_hosting_asset_insert_permission_check_tg
before insert on hs_hosting_asset before insert on hs_hosting_asset
for each row for each row
when ( not ( when ( not (
hasInsertPermission(NEW.bookingItemUuid, 'hs_hosting_asset') or
hasInsertPermission(NEW.bookingItemUuid, 'INSERT', 'hs_hosting_asset') or NEW.type in ('MANAGED_WEBSPACE') and hasInsertPermission(NEW.parentAssetUuid, 'hs_hosting_asset')
NEW.type = 'MANAGED_SERVER' and hasInsertPermission(NEW.parentAssetUuid, 'INSERT', 'hs_hosting_asset') ) ) ) )
execute procedure hs_hosting_asset_insert_permission_missing_tf(); execute procedure hs_hosting_asset_insert_permission_missing_tf();
--// --//
--//
-- ============================================================================ -- ============================================================================
--changeset hs-hosting-asset-rbac-IDENTITY-VIEW:1 endDelimiter:--// --changeset hs-hosting-asset-rbac-IDENTITY-VIEW:1 endDelimiter:--//