diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacViewPostgresGenerator.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacViewPostgresGenerator.java index a12e06d5..21257a24 100644 --- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacViewPostgresGenerator.java +++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacViewPostgresGenerator.java @@ -14,15 +14,14 @@ public class RbacViewPostgresGenerator { private final RbacView rbacDef; private final String liqibaseTagPrefix; - private final StringBuilder plPgSql = new StringBuilder(); + private final StringWriter plPgSql = new StringWriter(); public RbacViewPostgresGenerator(final RbacView forRbacDef) { rbacDef = forRbacDef; liqibaseTagPrefix = rbacDef.getRootEntityAlias().entityClass().getSimpleName(); - plPgSql.append(""" + plPgSql.writeLn(""" --liquibase formatted sql -- generated at: %{timestamp} - """ .replace("%{timestamp}", LocalDateTime.now().toString())); diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RolesGrantsAndPermissionsGenerator.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RolesGrantsAndPermissionsGenerator.java index d12ca747..27e84a3b 100644 --- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RolesGrantsAndPermissionsGenerator.java +++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RolesGrantsAndPermissionsGenerator.java @@ -26,34 +26,33 @@ class RolesGrantsAndPermissionsGenerator { RolesGrantsAndPermissionsGenerator(final RbacView rbacDef, final String liquibaseTagPrefix) { this.rbacDef = rbacDef; - this.rbacGrants.addAll(rbacGrants); + this.rbacGrants.addAll(rbacDef.getGrantDefs()); this.liquibaseTagPrefix = liquibaseTagPrefix; entityClass = rbacDef.getRootEntityAlias().entityClass(); - simpleEntityName = entityClass.getSimpleName(); - simpleEntityVarName = uncapitalize(simpleEntityName); + simpleEntityVarName = rbacDef.getRootEntityAlias().simpleName(); + simpleEntityName = capitalize(simpleEntityVarName); rawTableName = withoutRvSuffix(entityClass.getAnnotation(Table.class).name()); } - void generateTo(final StringBuilder plPgSql) { + void generateTo(final StringWriter plPgSql) { generateHeader(plPgSql); generateTriggerFunction(plPgSql); generageInsertTrigger(plPgSql); generateFooter(plPgSql); } - private void generateHeader(final StringBuilder plPgSql) { - plPgSql.append(""" + private void generateHeader(final StringWriter plPgSql) { + plPgSql.writeLn(""" -- ============================================================================ --changeset %{liquibaseTagPrefix}-rbac-CREATE-ROLES-GRANTS-PERMISSIONS:1 endDelimiter:--// -- ---------------------------------------------------------------------------- - """ .replace("%{liquibaseTagPrefix}", liquibaseTagPrefix)); } - private void generateTriggerFunction(final StringBuilder plPgSql) { - plPgSql.append(""" + private void generateTriggerFunction(final StringWriter plPgSql) { + plPgSql.writeLn(""" /* Creates the roles, grants and permission for the AFTER INSERT TRIGGER. */ @@ -66,27 +65,30 @@ class RolesGrantsAndPermissionsGenerator { if TG_OP <> 'INSERT' then raise exception 'invalid usage of TRIGGER AFTER INSERT function'; end if; - %{createRolesWithGrantsSql} - return NEW; - end; $$; """ - .replace("%{simpleEntityName}", simpleEntityName) - .replace("%{createRolesWithGrantsSql}", createRolesWithGrantsSql()) - ); + .replace("%{simpleEntityName}", simpleEntityName)); + plPgSql.indented(() -> { + createRolesWithGrantsSql(plPgSql, OWNER); + createRolesWithGrantsSql(plPgSql, ADMIN); + createRolesWithGrantsSql(plPgSql, AGENT); + createRolesWithGrantsSql(plPgSql, TENANT); + createRolesWithGrantsSql(plPgSql, REFERRER); + + if (!rbacGrants.isEmpty()) { + throw new IllegalStateException("unprocessed grants: " + rbacGrants); + // rbacGrants.forEach(g -> plPgSql.writeLn("-- unprocessed grant: " + g)); + } + + plPgSql.writeLn("return NEW;"); + }); + + plPgSql.writeLn("end; $$;"); + plPgSql.writeLn(); } - private String createRolesWithGrantsSql() { - final var plPgSql = new StringBuilder(); - createRolesWithGrantsSql(plPgSql, OWNER); - createRolesWithGrantsSql(plPgSql, ADMIN); - createRolesWithGrantsSql(plPgSql, AGENT); - createRolesWithGrantsSql(plPgSql, TENANT); - createRolesWithGrantsSql(plPgSql, REFERRER); - return plPgSql.toString(); - } - private void createRolesWithGrantsSql(final StringBuilder plPgSql, final RbacView.Role role) { + private void createRolesWithGrantsSql(final StringWriter plPgSql, final RbacView.Role role) { final var isToCreate = rbacDef.getRoleDefs().stream() .filter(roleDef -> rbacDef.isRootEntityAlias(roleDef.getEntityAlias()) && roleDef.getRole() == role ) @@ -95,62 +97,64 @@ class RolesGrantsAndPermissionsGenerator { return; } - plPgSql.append(""" - - perform createRoleWithGrants( - %{simpleEntityVarName)%{roleSuffix}(NEW), - """ - .replace("%{simpleEntityVarName)", simpleEntityVarName) - .replace("%{roleSuffix}", capitalize(role.roleName()))); + plPgSql.writeLn(); + plPgSql.writeLn("perform createRoleWithGrants("); + plPgSql.indented( () -> { + plPgSql.writeLn("%{simpleVarName)%{roleSuffix}(NEW)," + .replace("%{simpleVarName)", simpleEntityVarName) + .replace("%{roleSuffix}", capitalize(role.roleName()))); - final var permissionGrantsForRole = findPermissionsGrantsForRole(rbacDef.getRootEntityAlias(), role); - if (!permissionGrantsForRole.isEmpty()) { - final var permissionsForRoleInPlPgSql = permissionGrantsForRole.stream() - .map(RbacView.RbacGrantDefinition::getPermDef) - .map(RbacPermissionDefinition::getPermission) - .map(RbacView.Permission::permission) - .map(p -> "'" + p + "'") - .collect(joining(", ")); - plPgSql.append(indent(3) + "permissions => array[" + permissionsForRoleInPlPgSql + "],\n"); - rbacGrants.removeAll(permissionGrantsForRole); - } + final var permissionGrantsForRole = findPermissionsGrantsForRole(rbacDef.getRootEntityAlias(), role); + if (!permissionGrantsForRole.isEmpty()) { + final var permissionsForRoleInPlPgSql = permissionGrantsForRole.stream() + .map(RbacView.RbacGrantDefinition::getPermDef) + .map(RbacPermissionDefinition::getPermission) + .map(RbacView.Permission::permission) + .map(p -> "'" + p + "'") + .collect(joining(", ")); + plPgSql.indented( () -> + plPgSql.writeLn("permissions => array[" + permissionsForRoleInPlPgSql + "],\n")); + rbacGrants.removeAll(permissionGrantsForRole); + } - final var grantsToUsers = findGrantsToUserForRole(rbacDef.getRootEntityAlias(), role); - if (!grantsToUsers.isEmpty()) { - final var grantsToUsersPlPgSql = grantsToUsers.stream() - .map(RbacView.RbacGrantDefinition::getUserDef) - .map(this::toPlPgSqlReference) - .collect(joining(", ")); - plPgSql.append(indent(3) + "userUuids => array[" + grantsToUsersPlPgSql + "],\n"); - rbacGrants.removeAll(grantsToUsers); - } + final var grantsToUsers = findGrantsToUserForRole(rbacDef.getRootEntityAlias(), role); + if (!grantsToUsers.isEmpty()) { + final var grantsToUsersPlPgSql = grantsToUsers.stream() + .map(RbacView.RbacGrantDefinition::getUserDef) + .map(this::toPlPgSqlReference) + .collect(joining(", ")); + plPgSql.indented(() -> + plPgSql.writeLn("userUuids => array[" + grantsToUsersPlPgSql + "],\n")); + rbacGrants.removeAll(grantsToUsers); + } - final var incomingGrants = findIncomingSuperRolesForRole(rbacDef.getRootEntityAlias(), role); - if (!incomingGrants.isEmpty()) { - final var incomingGrantsInPlPgSql = incomingGrants.stream() - .map(RbacView.RbacGrantDefinition::getSuperRoleDef) - .map(r -> toPlPgSqlReference(NEW, r)) - .collect(joining(", ")); - plPgSql.append(indent(3) + "incomingSuperRoles => array[" + incomingGrantsInPlPgSql + "],\n"); - rbacGrants.removeAll(incomingGrants); - } + final var incomingGrants = findIncomingSuperRolesForRole(rbacDef.getRootEntityAlias(), role); + if (!incomingGrants.isEmpty()) { + final var incomingGrantsInPlPgSql = incomingGrants.stream() + .map(RbacView.RbacGrantDefinition::getSuperRoleDef) + .map(r -> toPlPgSqlReference(NEW, r)) + .collect(joining(", ")); + plPgSql.indented(() -> + plPgSql.writeLn("incomingSuperRoles => array[" + incomingGrantsInPlPgSql + "],\n")); + rbacGrants.removeAll(incomingGrants); + } - final var outgoingGrants = findOutgoingSuperRolesForRole(rbacDef.getRootEntityAlias(), role); - if (!outgoingGrants.isEmpty()) { - final var outgoingGrantsInPlPgSql = outgoingGrants.stream() - .map(RbacView.RbacGrantDefinition::getSuperRoleDef) - .map(r -> toPlPgSqlReference(NEW, r)) - .collect(joining(", ")); - plPgSql.append(indent(3) + "outgoingSubRoles => array[" + outgoingGrantsInPlPgSql + "],\n"); - rbacGrants.removeAll(outgoingGrants); - } + final var outgoingGrants = findOutgoingSuperRolesForRole(rbacDef.getRootEntityAlias(), role); + if (!outgoingGrants.isEmpty()) { + final var outgoingGrantsInPlPgSql = outgoingGrants.stream() + .map(RbacView.RbacGrantDefinition::getSuperRoleDef) + .map(r -> toPlPgSqlReference(NEW, r)) + .collect(joining(", ")); + plPgSql.indented(() -> + plPgSql.writeLn("outgoingSubRoles => array[" + outgoingGrantsInPlPgSql + "],\n")); + rbacGrants.removeAll(outgoingGrants); + } - if (!rbacGrants.isEmpty()) { - throw new IllegalStateException("unprocessed grants: " + rbacGrants); - } + plPgSql.chopTail(",\n"); + plPgSql.writeLn(); + }); - chopTail(plPgSql, ",\n"); - plPgSql.append("\n" + indent(2) + ");\n"); + plPgSql.writeLn(");"); } private Set findPermissionsGrantsForRole(final RbacView.EntityAlias entityAlias, final RbacView.Role role) { @@ -182,8 +186,8 @@ class RolesGrantsAndPermissionsGenerator { .collect(Collectors.toSet()); } - private void generageInsertTrigger(final StringBuilder plPgSql) { - plPgSql.append(""" + private void generageInsertTrigger(final StringWriter plPgSql) { + plPgSql.writeLn(""" /* An AFTER INSERT TRIGGER which creates the role structure for a new %{simpleEntityName} */ @@ -200,8 +204,9 @@ class RolesGrantsAndPermissionsGenerator { ); } - private static void generateFooter(final StringBuilder plPgSql) { - plPgSql.append("\n\n"); + private static void generateFooter(final StringWriter plPgSql) { + plPgSql.writeLn(); + plPgSql.writeLn(); } private String withoutRvSuffix(final String tableName) { @@ -229,14 +234,4 @@ class RolesGrantsAndPermissionsGenerator { private static String toTriggerReference(final PostgresTriggerReference triggerRef, final RbacView.EntityAlias entityAlias) { return triggerRef.name().toLowerCase() + capitalize(entityAlias.aliasName()); } - - private String indent(final int tabs) { - return " ".repeat(4*tabs); - } - - private void chopTail(final StringBuilder plPgSql, final String tail) { - if (plPgSql.toString().endsWith(tail)) { - plPgSql.setLength(plPgSql.length() - tail.length()); - } - } }