RolesGrantsAndPermissionsGenerator.java
package net.hostsharing.hsadminng.rbac.generator;
import net.hostsharing.hsadminng.rbac.generator.RbacSpec.CaseDef;
import net.hostsharing.hsadminng.rbac.generator.RbacSpec.RbacGrantDefinition;
import net.hostsharing.hsadminng.rbac.generator.RbacSpec.RbacPermissionDefinition;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Stream;
import static java.util.Optional.ofNullable;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toSet;
import static net.hostsharing.hsadminng.rbac.generator.PostgresTriggerReference.NEW;
import static net.hostsharing.hsadminng.rbac.generator.PostgresTriggerReference.OLD;
import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Permission.INSERT;
import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.RbacGrantDefinition.GrantType.*;
import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Role.*;
import static net.hostsharing.hsadminng.rbac.generator.StringWriter.with;
import static org.apache.commons.lang3.StringUtils.capitalize;
class RolesGrantsAndPermissionsGenerator {
private final RbacSpec rbacDef;
private final Set<RbacGrantDefinition> rbacGrants = new HashSet<>();
private final Set<RbacSpec.Role> rolesWithCreatorGrant = new LinkedHashSet<>();
private final String liquibaseTagPrefix;
private final String simpleEntityName;
private final String simpleEntityVarName;
private final String qualifiedRawTableName;
RolesGrantsAndPermissionsGenerator(final RbacSpec rbacDef, final String liquibaseTagPrefix) {
this.rbacDef = rbacDef;
this.rbacGrants.addAll(rbacDef.getGrantDefs().stream()
.filter(RbacGrantDefinition::isToCreate)
.collect(toSet()));
this.liquibaseTagPrefix = liquibaseTagPrefix;
simpleEntityVarName = rbacDef.getRootEntityAlias().simpleName();
simpleEntityName = capitalize(simpleEntityVarName);
qualifiedRawTableName = rbacDef.getRootEntityAlias().getRawTableNameWithSchema();
}
void generateTo(final StringWriter plPgSql) {
generateInsertTrigger(plPgSql);
if (hasAnyUpdatableEntityAliases()) {
generateUpdateTrigger(plPgSql);
}
}
private void generateHeader(final StringWriter plPgSql, final String triggerType) {
plPgSql.writeLn("""
-- ============================================================================
--changeset RolesGrantsAndPermissionsGenerator:${liquibaseTagPrefix}-rbac-${triggerType}-trigger runOnChange:true endDelimiter:--//
--validCheckSum: ANY
-- ----------------------------------------------------------------------------
""",
with("liquibaseTagPrefix", liquibaseTagPrefix),
with("triggerType", triggerType));
}
private void generateInsertTriggerFunction(final StringWriter plPgSql) {
plPgSql.writeLn("""
/*
Creates the roles, grants and permission for the AFTER INSERT TRIGGER.
*/
create or replace procedure ${rawTableQualifiedName}_build_rbac_system(
NEW ${rawTableQualifiedName}
)
language plpgsql as $$
"""
.replace("${rawTableQualifiedName}", qualifiedRawTableName));
plPgSql.writeLn("declare");
plPgSql.indented(() -> {
referencedEntityAliases()
.forEach((ea) -> plPgSql.writeLn(entityRefVar(NEW, ea) + " " + ea.getRawTableNameWithSchema() + ";"));
});
plPgSql.writeLn();
plPgSql.writeLn("begin");
plPgSql.indented(() -> {
plPgSql.writeLn("call rbac.enterTriggerForObjectUuid(NEW.uuid);");
plPgSql.writeLn();
generateCreateRolesAndGrantsAfterInsert(plPgSql);
plPgSql.ensureSingleEmptyLine();
plPgSql.writeLn("call rbac.leaveTriggerForObjectUuid(NEW.uuid);");
});
plPgSql.writeLn("end; $$;");
plPgSql.writeLn();
}
private void generateSimplifiedUpdateTriggerFunction(final StringWriter plPgSql) {
final var updateConditions = updatableEntityAliases()
.map(RbacSpec.EntityAlias::dependsOnColumName)
.distinct()
.map(columnName -> "NEW." + columnName + " is distinct from OLD." + columnName)
.collect(joining( "\n or "));
if (updateConditions.isBlank()) {
// TODO_impl: RBAC rules for _rv do not yet work properly - check if this comment appears in generated output!
plPgSql.writeLn("-- no updatable columns found, skipping update trigger");
return;
}
plPgSql.writeLn("""
/*
Called from the AFTER UPDATE TRIGGER to re-wire the grants.
*/
create or replace procedure ${rawTableQualifiedName}_update_rbac_system(
OLD ${rawTableQualifiedName},
NEW ${rawTableQualifiedName}
)
language plpgsql as $$
begin
if ${updateConditions} then
delete from rbac.grant g where g.grantedbytriggerof = OLD.uuid;
call ${rawTableQualifiedName}_build_rbac_system(NEW);
end if;
end; $$;
""",
with("simpleEntityName", simpleEntityName),
with("rawTableQualifiedName", qualifiedRawTableName),
with("updateConditions", updateConditions));
}
private void generateUpdateTriggerFunction(final StringWriter plPgSql) {
plPgSql.writeLn("""
/*
Called from the AFTER UPDATE TRIGGER to re-wire the grants.
*/
create or replace procedure ${rawTableQualifiedName}_update_rbac_system(
OLD ${rawTableQualifiedName},
NEW ${rawTableQualifiedName}
)
language plpgsql as $$
declare
""",
with("rawTableQualifiedName", qualifiedRawTableName));
plPgSql.chopEmptyLines();
plPgSql.indented(() -> {
referencedEntityAliases()
.forEach((ea) -> {
plPgSql.writeLn(entityRefVar(OLD, ea) + " " + ea.getRawTableNameWithSchema() + ";");
plPgSql.writeLn(entityRefVar(NEW, ea) + " " + ea.getRawTableNameWithSchema() + ";");
});
});
plPgSql.writeLn();
plPgSql.writeLn("begin");
plPgSql.indented(() -> {
plPgSql.writeLn("call rbac.enterTriggerForObjectUuid(NEW.uuid);");
plPgSql.writeLn();
generateUpdateRolesAndGrantsAfterUpdate(plPgSql);
plPgSql.ensureSingleEmptyLine();
plPgSql.writeLn("call rbac.leaveTriggerForObjectUuid(NEW.uuid);");
});
plPgSql.writeLn("end; $$;");
plPgSql.writeLn();
}
private boolean hasAnyUpdatableEntityAliases() {
return updatableEntityAliases().anyMatch(e -> true);
}
private boolean hasAnyUpdatableAndNullableEntityAliases() {
return updatableEntityAliases()
.filter(ea -> ea.nullable() == RbacSpec.Nullable.NULLABLE)
.anyMatch(e -> true);
}
private boolean hasAnyConditionalGrants() {
return rbacDef.getGrantDefs().stream().anyMatch(RbacGrantDefinition::isConditional);
}
private void generateCreateRolesAndGrantsAfterInsert(final StringWriter plPgSql) {
referencedEntityAliases()
.forEach((ea) -> {
generateFetchedVars(plPgSql, ea, NEW);
plPgSql.writeLn();
});
createRolesWithGrantsSql(plPgSql, OWNER);
createRolesWithGrantsSql(plPgSql, ADMIN);
createRolesWithGrantsSql(plPgSql, AGENT);
createRolesWithGrantsSql(plPgSql, TENANT);
createRolesWithGrantsSql(plPgSql, REFERRER);
generateGrants(plPgSql, ROLE_TO_USER);
generateGrants(plPgSql, ROLE_TO_ROLE);
if (!rbacDef.getAllCases().isEmpty()) {
plPgSql.writeLn();
final var ifOrElsIf = new AtomicReference<>("IF ");
rbacDef.getAllCases().forEach(caseDef -> {
if (caseDef.value != null) {
plPgSql.writeLn(ifOrElsIf + "NEW." + rbacDef.getDiscriminatorColumName() + " = '" + caseDef.value + "' THEN");
} else {
plPgSql.writeLn("ELSE");
}
plPgSql.indented(() -> {
generateGrants(plPgSql, ROLE_TO_ROLE, caseDef);
});
ifOrElsIf.set("ELSIF ");
});
plPgSql.writeLn("END IF;");
}
generateGrants(plPgSql, PERM_TO_ROLE);
if (!rolesWithCreatorGrant.isEmpty()) {
plPgSql.ensureSingleEmptyLine();
rolesWithCreatorGrant.forEach(role ->
plPgSql.writeLn("call rbac.grantRoleToCurrentContext(" +
qualifiedRawTableName + "_" + capitalize(role.name()) + "(NEW));"));
}
}
private Stream<RbacSpec.EntityAlias> referencedEntityAliases() {
return rbacDef.getEntityAliases().values().stream()
.filter(ea -> !rbacDef.isRootEntityAlias(ea))
.filter(ea -> ea.dependsOnColum() != null)
.filter(ea -> ea.entityClass() != null)
.filter(ea -> ea.fetchSql() != null);
}
private Stream<RbacSpec.EntityAlias> updatableEntityAliases() {
return referencedEntityAliases()
.filter(ea -> rbacDef.getUpdatableColumns().contains(ea.dependsOnColum().column));
}
private void generateUpdateRolesAndGrantsAfterUpdate(final StringWriter plPgSql) {
plPgSql.ensureSingleEmptyLine();
referencedEntityAliases()
.forEach((ea) -> {
generateFetchedVars(plPgSql, ea, OLD);
generateFetchedVars(plPgSql, ea, NEW);
plPgSql.writeLn();
});
updatableEntityAliases()
.map(RbacSpec.EntityAlias::dependsOnColum)
.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;");
});
}
private void generateFetchedVars(
final StringWriter plPgSql,
final RbacSpec.EntityAlias ea,
final PostgresTriggerReference old) {
plPgSql.writeLn(
ea.fetchSql().sql + " INTO " + entityRefVar(old, ea) + ";",
with("columns", ea.aliasName() + ".*"),
with("ref", old.name()));
if (ea.nullable() == RbacSpec.Nullable.NOT_NULL) {
plPgSql.writeLn(
"assert ${entityRefVar}.uuid is not null, format('${entityRefVar} must not be null for ${REF}.${dependsOnColumn} = %s of ${rawTable}', ${REF}.${dependsOnColumn});",
with("entityRefVar", entityRefVar(old, ea)),
with("dependsOnColumn", ea.dependsOnColumName()),
with("ref", old.name()),
with("rawTable", qualifiedRawTableName));
plPgSql.writeLn();
}
}
private void updateGrantsDependingOn(final StringWriter plPgSql, final String columnName) {
rbacDef.getGrantDefs().stream()
.filter(RbacGrantDefinition::isToCreate)
.filter(g -> g.dependsOnColumn(columnName))
.filter(g -> !isInsertPermissionGrant(g))
.forEach(g -> {
plPgSql.ensureSingleEmptyLine();
plPgSql.writeLn(generateRevoke(g));
plPgSql.writeLn(generateGrant(g));
plPgSql.writeLn();
});
}
private static Boolean isInsertPermissionGrant(final RbacGrantDefinition g) {
final var isInsertPermissionGrant = ofNullable(g.getPermDef()).map(RbacPermissionDefinition::getPermission).map(p -> p == INSERT).orElse(false);
return isInsertPermissionGrant;
}
private void generateGrants(final StringWriter plPgSql, final RbacGrantDefinition.GrantType grantType, final CaseDef caseDef) {
rbacGrants.stream()
.filter(g -> g.matchesCase(caseDef))
.filter(g -> g.grantType() == grantType)
.map(this::generateGrant)
.sorted()
.forEach(text -> plPgSql.writeLn(text, with("ref", NEW.name())));
}
private void generateGrants(final StringWriter plPgSql, final RbacGrantDefinition.GrantType grantType) {
plPgSql.ensureSingleEmptyLine();
rbacGrants.stream()
.filter(g -> !g.isConditional())
.filter(g -> g.grantType() == grantType)
.map(this::generateGrant)
.sorted()
.forEach(text -> plPgSql.writeLn(text, with("ref", NEW.name())));
}
private String generateRevoke(RbacGrantDefinition grantDef) {
return switch (grantDef.grantType()) {
case ROLE_TO_USER -> throw new IllegalArgumentException("unexpected grant");
case ROLE_TO_ROLE -> "call rbac.revokeRoleFromRole(${subRoleRef}, ${superRoleRef});"
.replace("${subRoleRef}", roleRef(OLD, grantDef.getSubRoleDef()))
.replace("${superRoleRef}", roleRef(OLD, grantDef.getSuperRoleDef()));
case PERM_TO_ROLE -> "call rbac.revokePermissionFromRole(${permRef}, ${superRoleRef});"
.replace("${permRef}", getPerm(OLD, grantDef.getPermDef()))
.replace("${superRoleRef}", roleRef(OLD, grantDef.getSuperRoleDef()));
};
}
private String generateGrant(RbacGrantDefinition grantDef) {
final var grantSql = switch (grantDef.grantType()) {
case ROLE_TO_USER -> throw new IllegalArgumentException("unexpected grant");
case ROLE_TO_ROLE -> "call rbac.grantRoleToRole(${subRoleRef}, ${superRoleRef}${assumed});"
.replace("${assumed}", grantDef.isAssumed() ? "" : ", rbac.unassumed()")
.replace("${subRoleRef}", roleRef(NEW, grantDef.getSubRoleDef()))
.replace("${superRoleRef}", roleRef(NEW, grantDef.getSuperRoleDef()));
case PERM_TO_ROLE ->
grantDef.getPermDef().getPermission() == INSERT ? ""
: "call rbac.grantPermissionToRole(${permRef}, ${superRoleRef});"
.replace("${permRef}", createPerm(NEW, grantDef.getPermDef()))
.replace("${superRoleRef}", roleRef(NEW, grantDef.getSuperRoleDef()));
};
return grantSql;
}
private String findPerm(final PostgresTriggerReference ref, final RbacPermissionDefinition permDef) {
return permRef("rbac.findPermissionId", ref, permDef);
}
private String getPerm(final PostgresTriggerReference ref, final RbacPermissionDefinition permDef) {
return permRef("rbac.getPermissionId", ref, permDef);
}
private String createPerm(final PostgresTriggerReference ref, final RbacPermissionDefinition permDef) {
return permRef("rbac.createPermission", ref, permDef);
}
private String permRef(final String functionName, final PostgresTriggerReference ref, final RbacPermissionDefinition permDef) {
return "${prefix}(${entityRef}.uuid, '${perm}')"
.replace("${prefix}", functionName)
.replace("${entityRef}", rbacDef.isRootEntityAlias(permDef.entityAlias)
? ref.name()
: refVarName(ref, permDef.entityAlias))
.replace("${perm}", permDef.permission.name());
}
private String refVarName(final PostgresTriggerReference ref, final RbacSpec.EntityAlias entityAlias) {
return ref.name().toLowerCase() + capitalize(entityAlias.aliasName());
}
private String roleRef(final PostgresTriggerReference rootRefVar, final RbacSpec.RbacRoleDefinition roleDef) {
if (roleDef.getEntityAlias().isGlobal()) {
return "rbac.global_ADMIN()";
}
final String entityRefVar = entityRefVar(rootRefVar, roleDef.getEntityAlias());
return roleDef.descriptorFunctionName() + "(" + entityRefVar + ")";
}
private String entityRefVar(
final PostgresTriggerReference rootRefVar,
final RbacSpec.EntityAlias entityAlias) {
return rbacDef.isRootEntityAlias(entityAlias)
? rootRefVar.name()
: rootRefVar.name().toLowerCase() + capitalize(entityAlias.aliasName());
}
private void createRolesWithGrantsSql(final StringWriter plPgSql, final RbacSpec.Role role) {
final var isToCreate = rbacDef.getRoleDefs().stream()
.filter(roleDef -> rbacDef.isRootEntityAlias(roleDef.getEntityAlias()) && roleDef.getRole() == role)
.findFirst().map(RbacSpec.RbacRoleDefinition::isToCreate).orElse(false);
if (!isToCreate) {
return;
}
plPgSql.writeLn();
plPgSql.writeLn("perform rbac.defineRoleWithGrants(");
plPgSql.indented(() -> {
plPgSql.writeLn("${qualifiedRawTableName)_${roleSuffix}(NEW),"
.replace("${qualifiedRawTableName)", qualifiedRawTableName)
.replace("${roleSuffix}", capitalize(role.name())));
generatePermissionsForRole(plPgSql, role);
generateIncomingSuperRolesForRole(plPgSql, role);
generateOutgoingSubRolesForRole(plPgSql, role);
generateUserGrantsForRole(plPgSql, role);
plPgSql.chopTail(",\n");
plPgSql.writeLn();
});
plPgSql.writeLn(");");
}
private void generateUserGrantsForRole(final StringWriter plPgSql, final RbacSpec.Role role) {
final var grantsToUsers = findGrantsToUserForRole(rbacDef.getRootEntityAlias(), role);
if (!grantsToUsers.isEmpty()) {
grantsToUsers.forEach(g -> {
if (g.getUserDef().role != RbacSpec.RbacSubjectReference.UserRole.CREATOR) {
throw new IllegalArgumentException("unknown user role: " + g.getUserDef());
}
});
// deferred to the end of the trigger (rbac.grantRoleToCurrentContext), so an assumed
// role which already reaches this role via the spec-grants can be detected and skipped
rolesWithCreatorGrant.add(role);
rbacGrants.removeAll(grantsToUsers);
}
}
private void generatePermissionsForRole(final StringWriter plPgSql, final RbacSpec.Role role) {
final var permissionGrantsForRole = findPermissionsGrantsForRole(rbacDef.getRootEntityAlias(), role);
if (!permissionGrantsForRole.isEmpty()) {
final var arrayElements = permissionGrantsForRole.stream()
.map(RbacGrantDefinition::getPermDef)
.map(RbacPermissionDefinition::getPermission)
.map(RbacSpec.Permission::name)
.map(p -> "'" + p + "'")
.sorted()
.toList();
plPgSql.indented(() ->
plPgSql.writeLn("permissions => array[" + joinArrayElements(arrayElements, 3) + "],\n"));
rbacGrants.removeAll(permissionGrantsForRole);
}
}
private void generateIncomingSuperRolesForRole(final StringWriter plPgSql, final RbacSpec.Role role) {
final var unconditionalIncomingGrants = findIncomingSuperRolesForRole(rbacDef.getRootEntityAlias(), role).stream()
.filter(g -> !g.isConditional())
.toList();
if (!unconditionalIncomingGrants.isEmpty()) {
final var arrayElements = unconditionalIncomingGrants.stream()
.map(g -> toPlPgSqlReference(NEW, g.getSuperRoleDef(), g.isAssumed()))
.sorted().toList();
plPgSql.indented(() ->
plPgSql.writeLn("incomingSuperRoles => array[" + joinArrayElements(arrayElements, 1) + "],\n"));
rbacGrants.removeAll(unconditionalIncomingGrants);
}
}
private void generateOutgoingSubRolesForRole(final StringWriter plPgSql, final RbacSpec.Role role) {
final var unconditionalOutgoingGrants = findOutgoingSuperRolesForRole(rbacDef.getRootEntityAlias(), role).stream()
.filter(g -> !g.isConditional())
.toList();
if (!unconditionalOutgoingGrants.isEmpty()) {
final var arrayElements = unconditionalOutgoingGrants.stream()
.map(g -> toPlPgSqlReference(NEW, g.getSubRoleDef(), g.isAssumed()))
.sorted().toList();
plPgSql.indented(() ->
plPgSql.writeLn("outgoingSubRoles => array[" + joinArrayElements(arrayElements, 1) + "],\n"));
rbacGrants.removeAll(unconditionalOutgoingGrants);
}
}
private String joinArrayElements(final List<String> arrayElements, final int singleLineLimit) {
return arrayElements.size() <= singleLineLimit
? String.join(", ", arrayElements)
: arrayElements.stream().collect(joining(",\n\t", "\n\t", ""));
}
private Set<RbacGrantDefinition> findPermissionsGrantsForRole(
final RbacSpec.EntityAlias entityAlias,
final RbacSpec.Role role) {
final var roleDef = rbacDef.findRbacRole(entityAlias, role);
return rbacGrants.stream()
.filter(g -> g.grantType() == PERM_TO_ROLE && g.getSuperRoleDef() == roleDef)
.collect(toSet());
}
private Set<RbacGrantDefinition> findGrantsToUserForRole(
final RbacSpec.EntityAlias entityAlias,
final RbacSpec.Role role) {
final var roleDef = rbacDef.findRbacRole(entityAlias, role);
return rbacGrants.stream()
.filter(g -> g.grantType() == ROLE_TO_USER && g.getSubRoleDef() == roleDef)
.collect(toSet());
}
private Set<RbacGrantDefinition> findIncomingSuperRolesForRole(
final RbacSpec.EntityAlias entityAlias,
final RbacSpec.Role role) {
final var roleDef = rbacDef.findRbacRole(entityAlias, role);
return rbacGrants.stream()
.filter(g -> g.grantType() == ROLE_TO_ROLE && g.getSubRoleDef() == roleDef)
.collect(toSet());
}
private Set<RbacGrantDefinition> findOutgoingSuperRolesForRole(
final RbacSpec.EntityAlias entityAlias,
final RbacSpec.Role role) {
final var roleDef = rbacDef.findRbacRole(entityAlias, role);
return rbacGrants.stream()
.filter(g -> g.grantType() == ROLE_TO_ROLE && g.getSuperRoleDef() == roleDef)
.filter(g -> g.getSubRoleDef().getEntityAlias() != entityAlias)
.collect(toSet());
}
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 ${rawTableQualifiedName} row.
*/
create or replace function ${rawTableQualifiedName}_build_rbac_system_after_insert_tf()
returns trigger
language plpgsql
strict as $$
begin
call ${rawTableQualifiedName}_build_rbac_system(NEW);
return NEW;
end; $$;
create or replace trigger build_rbac_system_after_insert_tg
after insert on ${rawTableQualifiedName}
for each row
execute procedure ${rawTableQualifiedName}_build_rbac_system_after_insert_tf();
"""
.replace("${rawTableQualifiedName}", qualifiedRawTableName)
);
generateFooter(plPgSql);
}
private void generateUpdateTrigger(final StringWriter plPgSql) {
generateHeader(plPgSql, "update");
if ( hasAnyUpdatableAndNullableEntityAliases() || hasAnyConditionalGrants() ) {
generateSimplifiedUpdateTriggerFunction(plPgSql);
} else {
generateUpdateTriggerFunction(plPgSql);
}
plPgSql.writeLn("""
/*
AFTER UPDATE TRIGGER to re-wire the grant structure for a new ${rawTableQualifiedName} row.
*/
create or replace function ${rawTableQualifiedName}_update_rbac_system_after_update_tf()
returns trigger
language plpgsql
strict as $$
begin
call ${rawTableQualifiedName}_update_rbac_system(OLD, NEW);
return NEW;
end; $$;
create or replace trigger update_rbac_system_after_update_tg
after update on ${rawTableQualifiedName}
for each row
execute procedure ${rawTableQualifiedName}_update_rbac_system_after_update_tf();
"""
.replace("${rawTableQualifiedName}", qualifiedRawTableName)
);
generateFooter(plPgSql);
}
private String schemaPrefix(final String qualifiedIdentifier) {
return qualifiedIdentifier.contains(".")
? qualifiedIdentifier.split("\\.")[0] + "."
: "";
}
private static void generateFooter(final StringWriter plPgSql) {
plPgSql.writeLn("--//");
plPgSql.writeLn();
}
private String toPlPgSqlReference(
final PostgresTriggerReference triggerRef,
final RbacSpec.RbacRoleDefinition roleDef,
final boolean assumed) {
final var assumedArg = assumed ? "" : ", rbac.unassumed()";
return roleDef.descriptorFunctionName() +
(roleDef.getEntityAlias().isGlobal() ? ( assumed ? "()" : "(rbac.unassumed())")
: rbacDef.isRootEntityAlias(roleDef.getEntityAlias()) ? ("(" + triggerRef.name() + ")")
: "(" + toTriggerReference(triggerRef, roleDef.getEntityAlias()) + assumedArg + ")");
}
private static String toTriggerReference(
final PostgresTriggerReference triggerRef,
final RbacSpec.EntityAlias entityAlias) {
return triggerRef.name().toLowerCase() + capitalize(entityAlias.aliasName());
}
}