RBAC Diagram+PostgreSQL Generator #21

Merged
hsh-michaelhoennig merged 54 commits from experimental-rbacview-generator into master 2024-03-11 12:30:44 +01:00
3 changed files with 78 additions and 9 deletions
Showing only changes of commit 5276471adb - Show all commits

View File

@ -105,17 +105,19 @@ public class HsOfficeSepaMandateEntity implements Stringifyable, HasUuid {
.createRole(OWNER, (with) -> {
with.owningUser(CREATOR);
with.incomingSuperRole(GLOBAL, ADMIN);
with.outgoingSubRole("bankAccount", REFERRER);
with.permission(ALL);
})
.createSubRole(ADMIN, (with) -> {
with.permission(EDIT);
})
.createSubRole(AGENT, (with) -> {
with.outgoingSubRole("bankAccount", REFERRER);
with.outgoingSubRole("debitorRel", AGENT);
})
.createSubRole(REFERRER, (with) -> {
with.incomingSuperRole("bankAccount", ADMIN);
with.incomingSuperRole("debitorRel", AGENT);
with.outgoingSubRole("debitorRel", TENANT);
with.permission(VIEW);
});
}

View File

@ -13,6 +13,7 @@ import jakarta.validation.constraints.NotNull;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import static java.util.Optional.ofNullable;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.autoFetched;
import static org.apache.commons.lang3.StringUtils.uncapitalize;
@ -300,6 +301,18 @@ public class RbacView {
return this;
}
boolean dependsOnColumn(final String columnName) {
return dependsRoleDefOnColumnName(this.superRoleDef, columnName)
|| dependsRoleDefOnColumnName(this.subRoleDef, columnName);
}
private Boolean dependsRoleDefOnColumnName(final RbacRoleDefinition superRoleDef, final String columnName) {
return ofNullable(superRoleDef)
.map(r -> r.getEntityAlias().dependsOnColum())
.map(d -> columnName.equals(d.column))
.orElse(false);
}
public enum GrantType {
ROLE_TO_USER,
ROLE_TO_ROLE,

View File

@ -4,10 +4,12 @@ import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacPermissionDefinition;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import static java.util.stream.Collectors.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.PostgresTriggerReference.NEW;
import static net.hostsharing.hsadminng.rbac.rbacdef.PostgresTriggerReference.OLD;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacGrantDefinition.GrantType.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.getRawTableName;
@ -79,12 +81,19 @@ class RolesGrantsAndPermissionsGenerator {
});
});
plPgSql.writeLn("""
begin
if TG_OP <> 'INSERT' then
raise exception 'invalid usage of TRIGGER AFTER INSERT function';
end if;
""");
plPgSql.indented(() -> {
plPgSql.writeLn("begin");
generateCreateRolesAndGrantsAfterInsert(plPgSql);
generateUpdateRolesAndGrantsAfterUpdate(plPgSql);
plPgSql.ensureEmptyLine();
plPgSql.writeLn("end; $$;");
});
plPgSql.writeLn();
}
private void generateCreateRolesAndGrantsAfterInsert(final StringWriter plPgSql) {
plPgSql.ensureEmptyLine();
plPgSql.writeLn("if TG_OP = 'INSERT' then");
plPgSql.indented(() -> {
@ -106,8 +115,53 @@ class RolesGrantsAndPermissionsGenerator {
generateGrants(plPgSql, PERM_TO_ROLE);
});
plPgSql.writeLn("end; $$;");
plPgSql.writeLn();
plPgSql.writeLn("end if;");
}
private void generateUpdateRolesAndGrantsAfterUpdate(final StringWriter plPgSql) {
plPgSql.ensureEmptyLine();
plPgSql.writeLn("if TG_OP = 'UPDATE' then");
plPgSql.indented(() -> {
rbacDef.getEntityAliases().values().stream()
.filter(ea -> !rbacDef.isRootEntityAlias(ea))
.filter(ea -> ea.fetchSql() != null)
.forEach(ea -> {
plPgSql.writeLn( ea.fetchSql().sql.replace("${ref}", OLD.name()) + " into " + entityRefVar(OLD, ea) + ";");
});
rbacDef.getEntityAliases().values().stream()
.map(RbacView.EntityAlias::dependsOnColum)
.filter(Objects::nonNull)
.filter(this::isUpdatable)
.map(c -> c.column)
.sorted()
.distinct()
.forEach(columnName -> {
plPgSql.writeLn();
plPgSql.writeLn("if NEW." + columnName + " <> OLD." + columnName + " then");
plPgSql.indented(() -> {
updateGrantsDependingOn(plPgSql, columnName);
});
plPgSql.writeLn("end if;");
});
});
plPgSql.writeLn("end if;");
}
private boolean isUpdatable(final RbacView.Column c) {
return rbacDef.getUpdatableColumns().contains(c);
}
private void updateGrantsDependingOn(final StringWriter plPgSql, final String columnName) {
rbacDef.getGrantDefs().stream()
.filter(RbacView.RbacGrantDefinition::isToCreate)
.filter(g -> g.dependsOnColumn(columnName))
.forEach(g -> {
plPgSql.writeLn("-- TODO: " + g);
});
}
private void generateGrants(final StringWriter plPgSql, final RbacView.RbacGrantDefinition.GrantType grantType) {