RBAC Diagram+PostgreSQL Generator #21
@ -503,7 +503,7 @@ public class RbacView {
|
||||
}
|
||||
return switch (fetchSql.part) {
|
||||
case SQL_QUERY -> fetchSql;
|
||||
case AUTO_FETCH -> SQL.query("SELECT * FROM " + getRawTableName(entityClass) + " WHERE uuid = ${ref}." + dependsOnColum.column);
|
||||
case AUTO_FETCH -> SQL.query("SELECT * FROM " + getRawTableName() + " WHERE uuid = ${ref}." + dependsOnColum.column);
|
||||
default -> throw new IllegalStateException("unexpected SQL definition: " + fetchSql);
|
||||
};
|
||||
}
|
||||
@ -521,11 +521,11 @@ public class RbacView {
|
||||
? aliasName
|
||||
: uncapitalize(withoutEntitySuffix(entityClass.getSimpleName()));
|
||||
}
|
||||
}
|
||||
|
||||
public static String getRawTableName(final Class<?> entityClass) {
|
||||
String getRawTableName() {
|
||||
return withoutRvSuffix(entityClass.getAnnotation(Table.class).name());
|
||||
}
|
||||
}
|
||||
public static String withoutRvSuffix(final String tableName) {
|
||||
return tableName.substring(0, tableName.length()-"_rv".length());
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ public class RbacViewPostgresGenerator {
|
||||
|
||||
public RbacViewPostgresGenerator(final RbacView forRbacDef) {
|
||||
rbacDef = forRbacDef;
|
||||
liqibaseTagPrefix = rbacDef.getRootEntityAlias().entityClass().getSimpleName();
|
||||
liqibaseTagPrefix = rbacDef.getRootEntityAlias().getRawTableName().replace("_", "-");
|
||||
plPgSql.writeLn("""
|
||||
--liquibase formatted sql
|
||||
-- This code generated was by ${generator} at ${timestamp}.
|
||||
|
@ -13,7 +13,6 @@ import static net.hostsharing.hsadminng.rbac.rbacdef.PostgresTriggerReference.NE
|
||||
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;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.StringWriter.with;
|
||||
import static org.apache.commons.lang3.StringUtils.capitalize;
|
||||
import static org.apache.commons.lang3.StringUtils.uncapitalize;
|
||||
@ -38,36 +37,65 @@ class RolesGrantsAndPermissionsGenerator {
|
||||
entityClass = rbacDef.getRootEntityAlias().entityClass();
|
||||
simpleEntityVarName = rbacDef.getRootEntityAlias().simpleName();
|
||||
simpleEntityName = capitalize(simpleEntityVarName);
|
||||
rawTableName = getRawTableName(entityClass);
|
||||
rawTableName = rbacDef.getRootEntityAlias().getRawTableName();
|
||||
}
|
||||
|
||||
void generateTo(final StringWriter plPgSql) {
|
||||
generateHeader(plPgSql);
|
||||
generateTriggerFunction(plPgSql);
|
||||
generateInsertTrigger(plPgSql);
|
||||
if (hasAnyUpdatableEntityAliases()) {
|
||||
generateUpdateTrigger(plPgSql);
|
||||
}
|
||||
generateFooter(plPgSql);
|
||||
}
|
||||
|
||||
private void generateHeader(final StringWriter plPgSql) {
|
||||
private void generateHeader(final StringWriter plPgSql, final String triggerType) {
|
||||
plPgSql.writeLn("""
|
||||
-- ============================================================================
|
||||
--changeset ${liquibaseTagPrefix}-rbac-CREATE-ROLES-GRANTS-PERMISSIONS:1 endDelimiter:--//
|
||||
--changeset ${liquibaseTagPrefix}-rbac-${triggerType}-trigger:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
"""
|
||||
.replace("${liquibaseTagPrefix}", liquibaseTagPrefix));
|
||||
""",
|
||||
with("liquibaseTagPrefix", liquibaseTagPrefix),
|
||||
with("triggerType", triggerType));
|
||||
}
|
||||
|
||||
private void generateTriggerFunction(final StringWriter plPgSql) {
|
||||
private void generateInsertTriggerFunction(final StringWriter plPgSql) {
|
||||
plPgSql.writeLn("""
|
||||
/*
|
||||
A Creates the roles, grants and permission for the AFTER INSERT TRIGGER.
|
||||
*/
|
||||
|
||||
create or replace procedure buildRbacSystemFor${simpleEntityName}(
|
||||
TG_OP text,
|
||||
NEW ${rawTableName}
|
||||
)
|
||||
language plpgsql as $$
|
||||
|
||||
declare
|
||||
"""
|
||||
.replace("${simpleEntityName}", simpleEntityName)
|
||||
.replace("${rawTableName}", rawTableName));
|
||||
|
||||
plPgSql.chopEmptyLines();
|
||||
plPgSql.indented(() -> {
|
||||
referencedEntityAliases()
|
||||
.forEach((ea) -> plPgSql.writeLn(entityRefVar(NEW, ea) + " " + ea.getRawTableName() + ";"));
|
||||
});
|
||||
|
||||
plPgSql.writeLn();
|
||||
plPgSql.writeLn("begin");
|
||||
plPgSql.indented(() -> {
|
||||
generateCreateRolesAndGrantsAfterInsert(plPgSql);
|
||||
plPgSql.ensureSingleEmptyLine();
|
||||
});
|
||||
plPgSql.writeLn("end; $$;");
|
||||
plPgSql.writeLn();
|
||||
}
|
||||
|
||||
private void generateUpdateTriggerFunction(final StringWriter plPgSql) {
|
||||
plPgSql.writeLn("""
|
||||
/*
|
||||
Called from the AFTER UPDATE TRIGGER to re-wire the grants.
|
||||
*/
|
||||
|
||||
create or replace procedure updateRbacGrantsFor${simpleEntityName}(
|
||||
OLD ${rawTableName},
|
||||
NEW ${rawTableName}
|
||||
)
|
||||
@ -80,27 +108,17 @@ class RolesGrantsAndPermissionsGenerator {
|
||||
|
||||
plPgSql.chopEmptyLines();
|
||||
plPgSql.indented(() -> {
|
||||
|
||||
referencedEntityAliases()
|
||||
.forEach((ea) -> plPgSql.writeLn(entityRefVar(NEW, ea) + " " + getRawTableName(ea.entityClass()) + ";"));
|
||||
|
||||
updatableEntityAliases()
|
||||
.forEach((ea) -> plPgSql.writeLn(entityRefVar(OLD, ea) + " " + getRawTableName(ea.entityClass()) + ";"));
|
||||
.forEach((ea) -> {
|
||||
plPgSql.writeLn(entityRefVar(OLD, ea) + " " + ea.getRawTableName() + ";");
|
||||
plPgSql.writeLn(entityRefVar(NEW, ea) + " " + ea.getRawTableName() + ";");
|
||||
});
|
||||
});
|
||||
|
||||
plPgSql.writeLn();
|
||||
plPgSql.writeLn("begin");
|
||||
plPgSql.indented(() -> {
|
||||
|
||||
generateCreateRolesAndGrantsAfterInsert(plPgSql);
|
||||
if (hasAnyUpdatableEntityAliases()) {
|
||||
generateUpdateRolesAndGrantsAfterUpdate(plPgSql);
|
||||
}
|
||||
plPgSql.writeLn("""
|
||||
else
|
||||
raise exception 'invalid usage of TRIGGER';
|
||||
end if;
|
||||
""");
|
||||
plPgSql.ensureSingleEmptyLine();
|
||||
});
|
||||
plPgSql.writeLn("end; $$;");
|
||||
@ -117,12 +135,6 @@ class RolesGrantsAndPermissionsGenerator {
|
||||
ea.fetchSql().sql + " into " + entityRefVar(NEW, ea) + ";",
|
||||
with("ref", NEW.name())));
|
||||
|
||||
plPgSql.ensureSingleEmptyLine();
|
||||
plPgSql.writeLn("if TG_OP = 'INSERT' then");
|
||||
|
||||
plPgSql.indented(() -> {
|
||||
|
||||
plPgSql.chopEmptyLines();
|
||||
createRolesWithGrantsSql(plPgSql, OWNER);
|
||||
createRolesWithGrantsSql(plPgSql, ADMIN);
|
||||
createRolesWithGrantsSql(plPgSql, AGENT);
|
||||
@ -132,8 +144,6 @@ class RolesGrantsAndPermissionsGenerator {
|
||||
generateGrants(plPgSql, ROLE_TO_USER);
|
||||
generateGrants(plPgSql, ROLE_TO_ROLE);
|
||||
generateGrants(plPgSql, PERM_TO_ROLE);
|
||||
plPgSql.ensureSingleEmptyLine();
|
||||
});
|
||||
}
|
||||
|
||||
private Stream<RbacView.EntityAlias> referencedEntityAliases() {
|
||||
@ -151,14 +161,16 @@ class RolesGrantsAndPermissionsGenerator {
|
||||
|
||||
private void generateUpdateRolesAndGrantsAfterUpdate(final StringWriter plPgSql) {
|
||||
plPgSql.ensureSingleEmptyLine();
|
||||
plPgSql.writeLn("elsif TG_OP = 'UPDATE' then");
|
||||
|
||||
plPgSql.indented(() -> {
|
||||
|
||||
updatableEntityAliases()
|
||||
.forEach((ea) -> plPgSql.writeLn(
|
||||
.forEach((ea) -> {
|
||||
plPgSql.writeLn(
|
||||
ea.fetchSql().sql + " into " + entityRefVar(OLD, ea) + ";",
|
||||
with("ref", OLD.name())));
|
||||
with("ref", OLD.name()));
|
||||
plPgSql.writeLn(
|
||||
ea.fetchSql().sql + " into " + entityRefVar(NEW, ea) + ";",
|
||||
with("ref", NEW.name()));
|
||||
});
|
||||
|
||||
updatableEntityAliases()
|
||||
.map(RbacView.EntityAlias::dependsOnColum)
|
||||
@ -173,7 +185,6 @@ class RolesGrantsAndPermissionsGenerator {
|
||||
});
|
||||
plPgSql.writeLn("end if;");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private boolean isUpdatable(final RbacView.Column c) {
|
||||
@ -386,6 +397,10 @@ class RolesGrantsAndPermissionsGenerator {
|
||||
}
|
||||
|
||||
private void generateInsertTrigger(final StringWriter plPgSql) {
|
||||
|
||||
generateHeader(plPgSql, "insert");
|
||||
generateInsertTriggerFunction(plPgSql);
|
||||
|
||||
plPgSql.writeLn("""
|
||||
/*
|
||||
AFTER INSERT TRIGGER to create the role+grant structure for a new ${rawTableName} row.
|
||||
@ -405,14 +420,19 @@ class RolesGrantsAndPermissionsGenerator {
|
||||
on ${rawTableName}
|
||||
for each row
|
||||
execute procedure insertTriggerFor${simpleEntityName}_tf();
|
||||
--//
|
||||
"""
|
||||
.replace("${simpleEntityName}", simpleEntityName)
|
||||
.replace("${rawTableName}", rawTableName)
|
||||
);
|
||||
|
||||
generateFooter(plPgSql);
|
||||
}
|
||||
|
||||
private void generateUpdateTrigger(final StringWriter plPgSql) {
|
||||
|
||||
generateHeader(plPgSql, "update");
|
||||
generateUpdateTriggerFunction(plPgSql);
|
||||
|
||||
plPgSql.writeLn("""
|
||||
/*
|
||||
AFTER INSERT TRIGGER to re-wire the grant structure for a new ${rawTableName} row.
|
||||
@ -423,7 +443,7 @@ class RolesGrantsAndPermissionsGenerator {
|
||||
language plpgsql
|
||||
strict as $$
|
||||
begin
|
||||
call buildRbacSystemFor${simpleEntityName}(TG_OP, OLD, NEW);
|
||||
call buildRbacSystemFor${simpleEntityName}(NEW);
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
@ -437,10 +457,12 @@ class RolesGrantsAndPermissionsGenerator {
|
||||
.replace("${simpleEntityName}", simpleEntityName)
|
||||
.replace("${rawTableName}", rawTableName)
|
||||
);
|
||||
|
||||
generateFooter(plPgSql);
|
||||
}
|
||||
|
||||
private static void generateFooter(final StringWriter plPgSql) {
|
||||
plPgSql.writeLn();
|
||||
plPgSql.writeLn("--//");
|
||||
plPgSql.writeLn();
|
||||
}
|
||||
|
||||
|
@ -90,14 +90,18 @@ public class StringWriter {
|
||||
this.varDefs = varDefs;
|
||||
}
|
||||
|
||||
String apply(final String text) {
|
||||
this.text = text;
|
||||
String apply(final String textToAppend) {
|
||||
try {
|
||||
text = textToAppend;
|
||||
stream(varDefs).forEach(varDef -> {
|
||||
final var pattern = Pattern.compile("\\$\\{" + varDef.name() + "}", Pattern.CASE_INSENSITIVE);
|
||||
final var matcher = pattern.matcher(text);
|
||||
this.text = matcher.replaceAll(varDef.value());
|
||||
text = matcher.replaceAll(varDef.value());
|
||||
});
|
||||
return this.text;
|
||||
return text;
|
||||
} catch (Exception exc) {
|
||||
throw exc;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user