improved RBAC generators #26

Merged
hsh-michaelhoennig merged 17 commits from improved-rbac-generator into master 2024-03-26 11:25:18 +01:00
3 changed files with 74 additions and 18 deletions
Showing only changes of commit fd7630aaa8 - Show all commits

View File

@ -113,8 +113,17 @@ public class InsertTriggerGenerator {
"invalid global role for INSERT permission: " + g.getSuperRoleDef().getRole()); "invalid global role for INSERT permission: " + g.getSuperRoleDef().getRole());
} }
} }
} else {
final var superRoleEntityAlias = g.getSuperRoleDef().getEntityAlias();
// 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 { } else {
generateInsertPermissionTriggerAllowByRoleOfDirectForeignKey(plPgSql, g); generateInsertPermissionTriggerAllowByRoleOfDirectForeignKey(plPgSql, g);
}
} }
}, },
() -> { () -> {
@ -149,6 +158,50 @@ public class InsertTriggerGenerator {
with("referenceColumn", g.getSuperRoleDef().getEntityAlias().dependsOnColumName())); with("referenceColumn", g.getSuperRoleDef().getEntityAlias().dependsOnColumName()));
} }
private void generateInsertPermissionTriggerAllowByRoleOfIndirectForeignKey(
final StringWriter plPgSql,
final RbacView.RbacGrantDefinition g) {
plPgSql.writeLn("""
/**
Checks if the user or assumed roles are allowed to insert a row to ${rawSubTable},
where the check is performed by an indirect role.
An indirect role is a role FIXME.
*/
create or replace function ${rawSubTable}_insert_permission_missing_tf()
returns trigger
language plpgsql as $$
begin
if ( not hasInsertPermission(
( SELECT ${varName}.uuid FROM
""",
with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableName()),
with("varName", g.getSuperRoleDef().getEntityAlias().aliasName()));
plPgSql.indented(3, () -> {
plPgSql.writeLn(
"(" + g.getSuperRoleDef().getEntityAlias().fetchSql().sql + ") AS ${varName}",
with("varName", g.getSuperRoleDef().getEntityAlias().aliasName()),
with("ref", NEW.name()));
});
plPgSql.writeLn("""
), 'INSERT', '${rawSubTable}') ) then
raise exception
'[403] insert into ${rawSubTable} not allowed for current subjects % (%)',
currentSubjects(), currentSubjectsUuids();
end if;
return NEW;
end; $$;
create trigger ${rawSubTable}_insert_permission_check_tg
before insert on ${rawSubTable}
for each row
execute procedure ${rawSubTable}_insert_permission_missing_tf();
""",
with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableName()));
}
private void generateInsertPermissionTriggerAllowOnlyGlobalAdmin(final StringWriter plPgSql) { private void generateInsertPermissionTriggerAllowOnlyGlobalAdmin(final StringWriter plPgSql) {
plPgSql.writeLn(""" plPgSql.writeLn("""
/** /**

View File

@ -1033,6 +1033,23 @@ public class RbacView {
} }
} }
private static void generateRbacView(final Class<? extends HasUuid> c) {
final Method mainMethod = stream(c.getMethods()).filter(
m -> isStatic(m.getModifiers()) && m.getName().equals("main")
)
.findFirst()
.orElse(null);
if (mainMethod != null) {
try {
mainMethod.invoke(null, new Object[] { null });
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
} else {
System.err.println("WARNING: no main method in: " + c.getName() + " => no RBAC rules generated");
}
}
/** /**
* This main method generates the RbacViews (PostgreSQL+diagram) for all given entity classes. * This main method generates the RbacViews (PostgreSQL+diagram) for all given entity classes.
*/ */
@ -1052,21 +1069,6 @@ public class RbacView {
HsOfficeSepaMandateEntity.class, HsOfficeSepaMandateEntity.class,
HsOfficeCoopSharesTransactionEntity.class, HsOfficeCoopSharesTransactionEntity.class,
HsOfficeMembershipEntity.class HsOfficeMembershipEntity.class
).forEach(c -> { ).forEach(RbacView::generateRbacView);
final Method mainMethod = stream(c.getMethods()).filter(
m -> isStatic(m.getModifiers()) && m.getName().equals("main")
)
.findFirst()
.orElse(null);
if (mainMethod != null) {
try {
mainMethod.invoke(null, new Object[] { null });
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
} else {
System.err.println("WARNING: no main method in: " + c.getName() + " => no RBAC rules generated");
}
});
} }
} }

View File

@ -4,8 +4,9 @@ spring:
platform: postgres platform: postgres
datasource: datasource:
url: jdbc:tc:postgresql:15.5-bookworm:///spring_boot_testcontainers url-tc: jdbc:tc:postgresql:15.5-bookworm:///spring_boot_testcontainers
url-local: jdbc:postgresql://localhost:5432/postgres url-local: jdbc:postgresql://localhost:5432/postgres
url: ${spring.datasource.url-tc}
username: postgres username: postgres
password: password password: password