improved RBAC generators #26
@ -131,7 +131,7 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
|
|||||||
"vatBusiness",
|
"vatBusiness",
|
||||||
"vatReverseCharge",
|
"vatReverseCharge",
|
||||||
"defaultPrefix" /* TODO: do we want that updatable? */)
|
"defaultPrefix" /* TODO: do we want that updatable? */)
|
||||||
.createPermission(custom("new-debitor")).grantedTo("global", ADMIN)
|
.createPermission(INSERT).grantedTo("global", ADMIN)
|
||||||
|
|
||||||
.importRootEntityAliasProxy("debitorRel", HsOfficeRelationEntity.class,
|
.importRootEntityAliasProxy("debitorRel", HsOfficeRelationEntity.class,
|
||||||
fetchedBySql("""
|
fetchedBySql("""
|
||||||
|
@ -84,7 +84,7 @@ public class HsOfficePartnerDetailsEntity implements HasUuid, Stringifyable {
|
|||||||
"birthName",
|
"birthName",
|
||||||
"birthday",
|
"birthday",
|
||||||
"dateOfDeath")
|
"dateOfDeath")
|
||||||
.createPermission(custom("new-partner-details")).grantedTo("global", ADMIN)
|
.createPermission(INSERT).grantedTo("global", ADMIN)
|
||||||
|
|
||||||
.importRootEntityAliasProxy("partnerRel", HsOfficeRelationEntity.class,
|
.importRootEntityAliasProxy("partnerRel", HsOfficeRelationEntity.class,
|
||||||
fetchedBySql("""
|
fetchedBySql("""
|
||||||
|
@ -90,7 +90,7 @@ public class HsOfficePartnerEntity implements Stringifyable, HasUuid {
|
|||||||
"partnerRelUuid",
|
"partnerRelUuid",
|
||||||
"personUuid",
|
"personUuid",
|
||||||
"contactUuid")
|
"contactUuid")
|
||||||
.createPermission(custom("new-partner")).grantedTo("global", ADMIN)
|
.createPermission(INSERT).grantedTo("global", ADMIN)
|
||||||
|
|
||||||
.importRootEntityAliasProxy("partnerRel", HsOfficeRelationEntity.class,
|
.importRootEntityAliasProxy("partnerRel", HsOfficeRelationEntity.class,
|
||||||
fetchedBySql("SELECT * FROM hs_office_relation AS r WHERE r.uuid = ${ref}.partnerRelUuid"),
|
fetchedBySql("SELECT * FROM hs_office_relation AS r WHERE r.uuid = ${ref}.partnerRelUuid"),
|
||||||
|
@ -4,6 +4,7 @@ import java.util.Optional;
|
|||||||
import java.util.function.BinaryOperator;
|
import java.util.function.BinaryOperator;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static net.hostsharing.hsadminng.rbac.rbacdef.PostgresTriggerReference.NEW;
|
||||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.INSERT;
|
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.INSERT;
|
||||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacGrantDefinition.GrantType.PERM_TO_ROLE;
|
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacGrantDefinition.GrantType.PERM_TO_ROLE;
|
||||||
import static net.hostsharing.hsadminng.rbac.rbacdef.StringWriter.with;
|
import static net.hostsharing.hsadminng.rbac.rbacdef.StringWriter.with;
|
||||||
@ -53,16 +54,16 @@ public class InsertTriggerGenerator {
|
|||||||
|
|
||||||
FOR row IN SELECT * FROM ${rawSuperTableName}
|
FOR row IN SELECT * FROM ${rawSuperTableName}
|
||||||
LOOP
|
LOOP
|
||||||
roleUuid := findRoleId(${rawSuperRoleDescriptor}(row));
|
roleUuid := findRoleId(${rawSuperRoleDescriptor});
|
||||||
permissionUuid := createPermission(row.uuid, 'INSERT', '${rawSubTableName}');
|
permissionUuid := createPermission(row.uuid, 'INSERT', '${rawSubTableName}');
|
||||||
call grantPermissionToRole(roleUuid, permissionUuid);
|
call grantPermissionToRole(permissionUuid, roleUuid);
|
||||||
END LOOP;
|
END LOOP;
|
||||||
END;
|
END;
|
||||||
$$;
|
$$;
|
||||||
""",
|
""",
|
||||||
with("rawSubTableName", rbacDef.getRootEntityAlias().getRawTableName()),
|
with("rawSubTableName", rbacDef.getRootEntityAlias().getRawTableName()),
|
||||||
with("rawSuperTableName", superRoleDef.getEntityAlias().getRawTableName()),
|
with("rawSuperTableName", superRoleDef.getEntityAlias().getRawTableName()),
|
||||||
with("rawSuperRoleDescriptor", toVar(superRoleDef))
|
with("rawSuperRoleDescriptor", toRoleDescriptor(superRoleDef, "row"))
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -79,24 +80,63 @@ public class InsertTriggerGenerator {
|
|||||||
strict as $$
|
strict as $$
|
||||||
begin
|
begin
|
||||||
call grantPermissionToRole(
|
call grantPermissionToRole(
|
||||||
${rawSuperRoleDescriptor}(NEW),
|
createPermission(NEW.uuid, 'INSERT', '${rawSubTableName}'),
|
||||||
createPermission(NEW.uuid, 'INSERT', '${rawSubTableName}'));
|
${rawSuperRoleDescriptor});
|
||||||
return NEW;
|
return NEW;
|
||||||
end; $$;
|
end; $$;
|
||||||
|
|
||||||
create trigger ${rawSubTableName}_${rawSuperTableName}_insert_tg
|
-- z_... is to put it at the end of after insert triggers, to make sure the roles exist
|
||||||
|
create trigger z_${rawSubTableName}_${rawSuperTableName}_insert_tg
|
||||||
after insert on ${rawSuperTableName}
|
after insert on ${rawSuperTableName}
|
||||||
for each row
|
for each row
|
||||||
execute procedure ${rawSubTableName}_${rawSuperTableName}_insert_tf();
|
execute procedure ${rawSubTableName}_${rawSuperTableName}_insert_tf();
|
||||||
""",
|
""",
|
||||||
with("rawSubTableName", rbacDef.getRootEntityAlias().getRawTableName()),
|
with("rawSubTableName", rbacDef.getRootEntityAlias().getRawTableName()),
|
||||||
with("rawSuperTableName", superRoleDef.getEntityAlias().getRawTableName()),
|
with("rawSuperTableName", superRoleDef.getEntityAlias().getRawTableName()),
|
||||||
with("rawSuperRoleDescriptor", toVar(superRoleDef))
|
with("rawSuperRoleDescriptor", toRoleDescriptor(superRoleDef, NEW.name()))
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void generateInsertCheckTrigger(final StringWriter plPgSql) {
|
private void generateInsertCheckTrigger(final StringWriter plPgSql) {
|
||||||
|
getOptionalInsertGrant().ifPresentOrElse(g -> {
|
||||||
|
if (!g.getSuperRoleDef().getEntityAlias().isGlobal()) {
|
||||||
|
if (rbacDef.isRootEntityAlias(g.getSuperRoleDef().getEntityAlias())) {
|
||||||
|
generateInsertPermissionTriggerAllowByDirectRole(plPgSql, g);
|
||||||
|
} else {
|
||||||
|
generateInsertPermissionTriggerAllowByIndirectRole(plPgSql, g);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch (g.getSuperRoleDef().getRole()) {
|
||||||
|
case ADMIN -> {
|
||||||
|
generateInsertPermissionTriggerAllowOnlyGlobalAdmin(plPgSql);
|
||||||
|
}
|
||||||
|
case GUEST -> {
|
||||||
|
// no permission check trigger generated, as anybody can insert rows into this table
|
||||||
|
}
|
||||||
|
default -> {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"invalid global role for INSERT permission: " + g.getSuperRoleDef().getRole());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
() -> {
|
||||||
|
plPgSql.writeLn("""
|
||||||
|
-- FIXME: Where is this case necessary?
|
||||||
|
create trigger ${rawSubTable}_insert_permission_check_tg
|
||||||
|
before insert on ${rawSubTable}
|
||||||
|
for each row
|
||||||
|
-- As there is no explicit INSERT grant specified for this table,
|
||||||
|
-- only global admins are allowed to insert any rows.
|
||||||
|
when ( not isGlobalAdmin() )
|
||||||
|
execute procedure ${rawSubTable}_insert_permission_missing_tf();
|
||||||
|
""",
|
||||||
|
with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableName()));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void generateInsertPermissionTriggerAllowByDirectRole(final StringWriter plPgSql, final RbacView.RbacGrantDefinition g) {
|
||||||
plPgSql.writeLn("""
|
plPgSql.writeLn("""
|
||||||
/**
|
/**
|
||||||
Checks if the user or assumed roles are allowed to insert a row to ${rawSubTable}.
|
Checks if the user or assumed roles are allowed to insert a row to ${rawSubTable}.
|
||||||
@ -108,10 +148,7 @@ public class InsertTriggerGenerator {
|
|||||||
raise exception '[403] insert into ${rawSubTable} not allowed for current subjects % (%)',
|
raise exception '[403] insert into ${rawSubTable} not allowed for current subjects % (%)',
|
||||||
currentSubjects(), currentSubjectsUuids();
|
currentSubjects(), currentSubjectsUuids();
|
||||||
end; $$;
|
end; $$;
|
||||||
""",
|
|
||||||
with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableName()));
|
|
||||||
getOptionalInsertGrant().ifPresentOrElse(g -> {
|
|
||||||
plPgSql.writeLn("""
|
|
||||||
create trigger ${rawSubTable}_insert_permission_check_tg
|
create trigger ${rawSubTable}_insert_permission_check_tg
|
||||||
before insert on ${rawSubTable}
|
before insert on ${rawSubTable}
|
||||||
for each row
|
for each row
|
||||||
@ -120,19 +157,69 @@ public class InsertTriggerGenerator {
|
|||||||
""",
|
""",
|
||||||
with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableName()),
|
with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableName()),
|
||||||
with("referenceColumn", g.getSuperRoleDef().getEntityAlias().dependsOnColumName()));
|
with("referenceColumn", g.getSuperRoleDef().getEntityAlias().dependsOnColumName()));
|
||||||
},
|
}
|
||||||
() -> {
|
|
||||||
|
private void generateInsertPermissionTriggerAllowByIndirectRole(
|
||||||
|
final StringWriter plPgSql,
|
||||||
|
final RbacView.RbacGrantDefinition g) {
|
||||||
plPgSql.writeLn("""
|
plPgSql.writeLn("""
|
||||||
|
/**
|
||||||
|
Checks if the user or assumed roles are allowed to insert a row to ${rawSubTable}.
|
||||||
hsh-michaelhoennig marked this conversation as resolved
Outdated
|
|||||||
|
*/
|
||||||
|
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) {
|
||||||
|
plPgSql.writeLn("""
|
||||||
|
/**
|
||||||
|
Checks if the user or assumed roles are allowed to insert a row to ${rawSubTable}.
|
||||||
|
*/
|
||||||
|
create or replace function ${rawSubTable}_insert_permission_missing_tf()
|
||||||
|
returns trigger
|
||||||
|
language plpgsql as $$
|
||||||
|
begin
|
||||||
|
raise exception '[403] insert into ${rawSubTable} not allowed for current subjects % (%)',
|
||||||
|
currentSubjects(), currentSubjectsUuids();
|
||||||
|
end; $$;
|
||||||
|
|
||||||
create trigger ${rawSubTable}_insert_permission_check_tg
|
create trigger ${rawSubTable}_insert_permission_check_tg
|
||||||
before insert on ${rawSubTable}
|
before insert on ${rawSubTable}
|
||||||
for each row
|
for each row
|
||||||
-- As there is no explicit INSERT grant specified for this table,
|
|
||||||
-- only global admins are allowed to insert any rows.
|
|
||||||
when ( not isGlobalAdmin() )
|
when ( not isGlobalAdmin() )
|
||||||
execute procedure ${rawSubTable}_insert_permission_missing_tf();
|
execute procedure ${rawSubTable}_insert_permission_missing_tf();
|
||||||
""",
|
""",
|
||||||
with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableName()));
|
with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableName()));
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Stream<RbacView.RbacGrantDefinition> getInsertGrants() {
|
private Stream<RbacView.RbacGrantDefinition> getInsertGrants() {
|
||||||
@ -162,4 +249,12 @@ public class InsertTriggerGenerator {
|
|||||||
return uncapitalize(roleDef.getEntityAlias().simpleName()) + capitalize(roleDef.getRole().roleName());
|
return uncapitalize(roleDef.getEntityAlias().simpleName()) + capitalize(roleDef.getRole().roleName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private String toRoleDescriptor(final RbacView.RbacRoleDefinition roleDef, final String ref) {
|
||||||
|
final var functionName = toVar(roleDef);
|
||||||
|
if (roleDef.getEntityAlias().isGlobal()) {
|
||||||
|
return functionName + "()";
|
||||||
|
}
|
||||||
|
return functionName + "(" + ref + ")";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ public class RbacIdentityViewGenerator {
|
|||||||
$idName$);
|
$idName$);
|
||||||
""";
|
""";
|
||||||
case SQL_QUERY -> """
|
case SQL_QUERY -> """
|
||||||
call generateRbacIdentityViewFromProjection('${rawTableName}', $idName$
|
call generateRbacIdentityViewFromQuery('${rawTableName}', $idName$
|
||||||
${identityViewSqlPart}
|
${identityViewSqlPart}
|
||||||
$idName$);
|
$idName$);
|
||||||
""";
|
""";
|
||||||
|
@ -24,7 +24,9 @@ public class RbacRestrictedViewGenerator {
|
|||||||
--changeset ${liquibaseTagPrefix}-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
--changeset ${liquibaseTagPrefix}-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
call generateRbacRestrictedView('${rawTableName}',
|
call generateRbacRestrictedView('${rawTableName}',
|
||||||
'${orderBy}',
|
$orderBy$
|
||||||
|
${orderBy}
|
||||||
|
$orderBy$,
|
||||||
$updates$
|
$updates$
|
||||||
${updates}
|
${updates}
|
||||||
$updates$);
|
$updates$);
|
||||||
|
@ -32,6 +32,7 @@ import java.util.stream.Stream;
|
|||||||
import static java.lang.reflect.Modifier.isStatic;
|
import static java.lang.reflect.Modifier.isStatic;
|
||||||
import static java.util.Arrays.stream;
|
import static java.util.Arrays.stream;
|
||||||
import static java.util.Optional.ofNullable;
|
import static java.util.Optional.ofNullable;
|
||||||
|
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL;
|
||||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR;
|
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR;
|
||||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.autoFetched;
|
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.autoFetched;
|
||||||
import static org.apache.commons.lang3.StringUtils.uncapitalize;
|
import static org.apache.commons.lang3.StringUtils.uncapitalize;
|
||||||
@ -141,35 +142,42 @@ public class RbacView {
|
|||||||
if (rootEntityAliasProxy != null) {
|
if (rootEntityAliasProxy != null) {
|
||||||
throw new IllegalStateException("there is already an entityAliasProxy: " + rootEntityAliasProxy);
|
throw new IllegalStateException("there is already an entityAliasProxy: " + rootEntityAliasProxy);
|
||||||
}
|
}
|
||||||
rootEntityAliasProxy = importEntityAliasImpl(aliasName, entityClass, fetchSql, dependsOnColum, false);
|
rootEntityAliasProxy = importEntityAliasImpl(aliasName, entityClass, fetchSql, dependsOnColum, false, NOT_NULL);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public RbacView importSubEntityAlias(
|
public RbacView importSubEntityAlias(
|
||||||
final String aliasName, final Class<? extends HasUuid> entityClass,
|
final String aliasName, final Class<? extends HasUuid> entityClass,
|
||||||
final SQL fetchSql, final Column dependsOnColum) {
|
final SQL fetchSql, final Column dependsOnColum) {
|
||||||
importEntityAliasImpl(aliasName, entityClass, fetchSql, dependsOnColum, true);
|
importEntityAliasImpl(aliasName, entityClass, fetchSql, dependsOnColum, true, NOT_NULL);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RbacView importEntityAlias(
|
||||||
|
final String aliasName, final Class<? extends HasUuid> entityClass,
|
||||||
|
final Column dependsOnColum, final SQL fetchSql, final Nullable nullable) {
|
||||||
|
importEntityAliasImpl(aliasName, entityClass, fetchSql, dependsOnColum, false, nullable);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public RbacView importEntityAlias(
|
public RbacView importEntityAlias(
|
||||||
final String aliasName, final Class<? extends HasUuid> entityClass,
|
final String aliasName, final Class<? extends HasUuid> entityClass,
|
||||||
final Column dependsOnColum, final SQL fetchSql) {
|
final Column dependsOnColum, final SQL fetchSql) {
|
||||||
importEntityAliasImpl(aliasName, entityClass, fetchSql, dependsOnColum, false);
|
importEntityAliasImpl(aliasName, entityClass, fetchSql, dependsOnColum, false, NOT_NULL);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public RbacView importEntityAlias(
|
public RbacView importEntityAlias(
|
||||||
final String aliasName, final Class<? extends HasUuid> entityClass,
|
final String aliasName, final Class<? extends HasUuid> entityClass,
|
||||||
final Column dependsOnColum) {
|
final Column dependsOnColum) {
|
||||||
importEntityAliasImpl(aliasName, entityClass, autoFetched(), dependsOnColum, false);
|
importEntityAliasImpl(aliasName, entityClass, autoFetched(), dependsOnColum, false, null);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
private EntityAlias importEntityAliasImpl(
|
private EntityAlias importEntityAliasImpl(
|
||||||
final String aliasName, final Class<? extends HasUuid> entityClass,
|
final String aliasName, final Class<? extends HasUuid> entityClass,
|
||||||
final SQL fetchSql, final Column dependsOnColum, boolean asSubEntity) {
|
final SQL fetchSql, final Column dependsOnColum, boolean asSubEntity, final Nullable nullable) {
|
||||||
final var entityAlias = new EntityAlias(aliasName, entityClass, fetchSql, dependsOnColum, asSubEntity);
|
final var entityAlias = new EntityAlias(aliasName, entityClass, fetchSql, dependsOnColum, asSubEntity, nullable);
|
||||||
entityAliases.put(aliasName, entityAlias);
|
entityAliases.put(aliasName, entityAlias);
|
||||||
try {
|
try {
|
||||||
importAsAlias(aliasName, rbacDefinition(entityClass), asSubEntity);
|
importAsAlias(aliasName, rbacDefinition(entityClass), asSubEntity);
|
||||||
@ -281,15 +289,19 @@ public class RbacView {
|
|||||||
return RbacView.this;
|
return RbacView.this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public RbacView grantPermission(final String entityAliasName, final Permission perm) {
|
public RbacView grantPermission(final Permission perm) {
|
||||||
final var entityAlias = findEntityAlias(entityAliasName);
|
final var forTable = rootEntityAlias.getRawTableName();
|
||||||
final var forTable = entityAlias.getRawTableName();
|
findOrCreateGrantDef(findRbacPerm(rootEntityAlias, perm, forTable), superRoleDef).toCreate();
|
||||||
findOrCreateGrantDef(findRbacPerm(entityAlias, perm, forTable), superRoleDef).toCreate();
|
|
||||||
return RbacView.this;
|
return RbacView.this;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum Nullable {
|
||||||
|
NOT_NULL, // DEFAULT
|
||||||
|
NULLABLE
|
||||||
|
}
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@EqualsAndHashCode
|
@EqualsAndHashCode
|
||||||
public class RbacGrantDefinition {
|
public class RbacGrantDefinition {
|
||||||
@ -560,14 +572,14 @@ public class RbacView {
|
|||||||
.orElseGet(() -> new RbacGrantDefinition(subRoleDefinition, superRoleDefinition));
|
.orElseGet(() -> new RbacGrantDefinition(subRoleDefinition, superRoleDefinition));
|
||||||
}
|
}
|
||||||
|
|
||||||
record EntityAlias(String aliasName, Class<? extends RbacObject> entityClass, SQL fetchSql, Column dependsOnColum, boolean isSubEntity) {
|
record EntityAlias(String aliasName, Class<? extends RbacObject> entityClass, SQL fetchSql, Column dependsOnColum, boolean isSubEntity, Nullable nullable) {
|
||||||
|
|
||||||
public EntityAlias(final String aliasName) {
|
public EntityAlias(final String aliasName) {
|
||||||
this(aliasName, null, null, null, false);
|
this(aliasName, null, null, null, false, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public EntityAlias(final String aliasName, final Class<? extends RbacObject> entityClass) {
|
public EntityAlias(final String aliasName, final Class<? extends RbacObject> entityClass) {
|
||||||
this(aliasName, entityClass, null, null, false);
|
this(aliasName, entityClass, null, null, false, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isGlobal() {
|
boolean isGlobal() {
|
||||||
@ -626,39 +638,35 @@ public class RbacView {
|
|||||||
return tableName.substring(0, tableName.length() - "_rv".length());
|
return tableName.substring(0, tableName.length() - "_rv".length());
|
||||||
}
|
}
|
||||||
|
|
||||||
public record Role(String roleName) {
|
public enum Role {
|
||||||
|
|
||||||
public static final Role OWNER = new Role("owner");
|
OWNER,
|
||||||
public static final Role ADMIN = new Role("admin");
|
ADMIN,
|
||||||
public static final Role AGENT = new Role("agent");
|
AGENT,
|
||||||
public static final Role TENANT = new Role("tenant");
|
TENANT,
|
||||||
public static final Role REFERRER = new Role("referrer");
|
REFERRER,
|
||||||
|
|
||||||
|
GUEST;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return ":" + roleName;
|
return ":" + roleName();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
String roleName() {
|
||||||
public boolean equals(final Object obj) {
|
return name().toLowerCase();
|
||||||
return ((obj instanceof Role) && ((Role) obj).roleName.equals(this.roleName));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public record Permission(String permission) {
|
public enum Permission {
|
||||||
|
INSERT,
|
||||||
public static final Permission INSERT = new Permission("INSERT");
|
DELETE,
|
||||||
public static final Permission DELETE = new Permission("DELETE");
|
UPDATE,
|
||||||
public static final Permission UPDATE = new Permission("UPDATE");
|
SELECT;
|
||||||
public static final Permission SELECT = new Permission("SELECT");
|
|
||||||
|
|
||||||
public static Permission custom(final String permission) {
|
|
||||||
return new Permission(permission);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return ":" + permission;
|
return ":" + name();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +37,10 @@ public class RbacViewPostgresGenerator {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return plPgSql.toString();
|
return plPgSql.toString()
|
||||||
|
.replace("\n\n\n", "\n\n")
|
||||||
|
.replace("-- ====", "\n-- ====")
|
||||||
|
.replace("\n\n--//", "\n--//");
|
||||||
}
|
}
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
|
@ -82,6 +82,7 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
plPgSql.writeLn("begin");
|
plPgSql.writeLn("begin");
|
||||||
plPgSql.indented(() -> {
|
plPgSql.indented(() -> {
|
||||||
plPgSql.writeLn("call enterTriggerForObjectUuid(NEW.uuid);");
|
plPgSql.writeLn("call enterTriggerForObjectUuid(NEW.uuid);");
|
||||||
|
plPgSql.writeLn();
|
||||||
generateCreateRolesAndGrantsAfterInsert(plPgSql);
|
generateCreateRolesAndGrantsAfterInsert(plPgSql);
|
||||||
plPgSql.ensureSingleEmptyLine();
|
plPgSql.ensureSingleEmptyLine();
|
||||||
plPgSql.writeLn("call leaveTriggerForObjectUuid(NEW.uuid);");
|
plPgSql.writeLn("call leaveTriggerForObjectUuid(NEW.uuid);");
|
||||||
@ -90,6 +91,37 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
plPgSql.writeLn();
|
plPgSql.writeLn();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void generateSimplifiedUpdateTriggerFunction(final StringWriter plPgSql) {
|
||||||
|
|
||||||
|
final var updateConditions = updatableEntityAliases()
|
||||||
|
.map(RbacView.EntityAlias::dependsOnColumName)
|
||||||
|
.distinct()
|
||||||
|
.map(columnName -> "NEW." + columnName + " is distinct from OLD." + columnName)
|
||||||
|
.collect(joining( "\n or "));
|
||||||
|
plPgSql.writeLn("""
|
||||||
|
/*
|
||||||
|
Called from the AFTER UPDATE TRIGGER to re-wire the grants.
|
||||||
|
*/
|
||||||
|
|
||||||
|
create or replace procedure updateRbacRulesFor${simpleEntityName}(
|
||||||
|
OLD ${rawTableName},
|
||||||
|
NEW ${rawTableName}
|
||||||
|
)
|
||||||
|
language plpgsql as $$
|
||||||
|
begin
|
||||||
|
|
||||||
|
if ${updateConditions} then
|
||||||
|
delete from rbacgrants g where g.grantedbytriggerof = OLD.uuid;
|
||||||
|
call buildRbacSystemFor${simpleEntityName}(NEW);
|
||||||
|
end if;
|
||||||
|
end; $$;
|
||||||
|
""",
|
||||||
|
with("simpleEntityName", simpleEntityName),
|
||||||
|
with("rawTableName", rawTableName),
|
||||||
|
with("updateConditions", updateConditions));
|
||||||
|
}
|
||||||
|
|
||||||
private void generateUpdateTriggerFunction(final StringWriter plPgSql) {
|
private void generateUpdateTriggerFunction(final StringWriter plPgSql) {
|
||||||
plPgSql.writeLn("""
|
plPgSql.writeLn("""
|
||||||
/*
|
/*
|
||||||
@ -109,7 +141,7 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
|
|
||||||
plPgSql.chopEmptyLines();
|
plPgSql.chopEmptyLines();
|
||||||
plPgSql.indented(() -> {
|
plPgSql.indented(() -> {
|
||||||
updatableEntityAliases()
|
referencedEntityAliases()
|
||||||
.forEach((ea) -> {
|
.forEach((ea) -> {
|
||||||
plPgSql.writeLn(entityRefVar(OLD, ea) + " " + ea.getRawTableName() + ";");
|
plPgSql.writeLn(entityRefVar(OLD, ea) + " " + ea.getRawTableName() + ";");
|
||||||
plPgSql.writeLn(entityRefVar(NEW, ea) + " " + ea.getRawTableName() + ";");
|
plPgSql.writeLn(entityRefVar(NEW, ea) + " " + ea.getRawTableName() + ";");
|
||||||
@ -120,6 +152,7 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
plPgSql.writeLn("begin");
|
plPgSql.writeLn("begin");
|
||||||
plPgSql.indented(() -> {
|
plPgSql.indented(() -> {
|
||||||
plPgSql.writeLn("call enterTriggerForObjectUuid(NEW.uuid);");
|
plPgSql.writeLn("call enterTriggerForObjectUuid(NEW.uuid);");
|
||||||
|
plPgSql.writeLn();
|
||||||
generateUpdateRolesAndGrantsAfterUpdate(plPgSql);
|
generateUpdateRolesAndGrantsAfterUpdate(plPgSql);
|
||||||
plPgSql.ensureSingleEmptyLine();
|
plPgSql.ensureSingleEmptyLine();
|
||||||
plPgSql.writeLn("call leaveTriggerForObjectUuid(NEW.uuid);");
|
plPgSql.writeLn("call leaveTriggerForObjectUuid(NEW.uuid);");
|
||||||
@ -132,11 +165,18 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
return updatableEntityAliases().anyMatch(e -> true);
|
return updatableEntityAliases().anyMatch(e -> true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean hasAnyUpdatableAndNullableEntityAliases() {
|
||||||
|
return updatableEntityAliases()
|
||||||
|
.filter(ea -> ea.nullable() == RbacView.Nullable.NULLABLE)
|
||||||
|
.anyMatch(e -> true);
|
||||||
|
}
|
||||||
|
|
||||||
private void generateCreateRolesAndGrantsAfterInsert(final StringWriter plPgSql) {
|
private void generateCreateRolesAndGrantsAfterInsert(final StringWriter plPgSql) {
|
||||||
referencedEntityAliases()
|
referencedEntityAliases()
|
||||||
.forEach((ea) -> plPgSql.writeLn(
|
.forEach((ea) -> {
|
||||||
ea.fetchSql().sql + " into " + entityRefVar(NEW, ea) + ";",
|
generateFetchedVars(plPgSql, ea, NEW);
|
||||||
with("ref", NEW.name())));
|
plPgSql.writeLn();
|
||||||
|
});
|
||||||
|
|
||||||
createRolesWithGrantsSql(plPgSql, OWNER);
|
createRolesWithGrantsSql(plPgSql, OWNER);
|
||||||
createRolesWithGrantsSql(plPgSql, ADMIN);
|
createRolesWithGrantsSql(plPgSql, ADMIN);
|
||||||
@ -165,14 +205,11 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
private void generateUpdateRolesAndGrantsAfterUpdate(final StringWriter plPgSql) {
|
private void generateUpdateRolesAndGrantsAfterUpdate(final StringWriter plPgSql) {
|
||||||
plPgSql.ensureSingleEmptyLine();
|
plPgSql.ensureSingleEmptyLine();
|
||||||
|
|
||||||
updatableEntityAliases()
|
referencedEntityAliases()
|
||||||
.forEach((ea) -> {
|
.forEach((ea) -> {
|
||||||
plPgSql.writeLn(
|
generateFetchedVars(plPgSql, ea, OLD);
|
||||||
ea.fetchSql().sql + " into " + entityRefVar(OLD, ea) + ";",
|
generateFetchedVars(plPgSql, ea, NEW);
|
||||||
with("ref", OLD.name()));
|
plPgSql.writeLn();
|
||||||
plPgSql.writeLn(
|
|
||||||
ea.fetchSql().sql + " into " + entityRefVar(NEW, ea) + ";",
|
|
||||||
with("ref", NEW.name()));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
updatableEntityAliases()
|
updatableEntityAliases()
|
||||||
@ -190,6 +227,23 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void generateFetchedVars(
|
||||||
|
final StringWriter plPgSql,
|
||||||
|
final RbacView.EntityAlias ea,
|
||||||
|
final PostgresTriggerReference old) {
|
||||||
|
plPgSql.writeLn(
|
||||||
|
ea.fetchSql().sql + " INTO " + entityRefVar(old, ea) + ";",
|
||||||
|
with("ref", old.name()));
|
||||||
|
if (ea.nullable() == RbacView.Nullable.NOT_NULL) {
|
||||||
|
plPgSql.writeLn(
|
||||||
|
"assert ${entityRefVar}.uuid is not null, format('${entityRefVar} must not be null for ${REF}.${dependsOnColumn} = %s', ${REF}.${dependsOnColumn});",
|
||||||
|
with("entityRefVar", entityRefVar(old, ea)),
|
||||||
|
with("dependsOnColumn", ea.dependsOnColumName()),
|
||||||
|
with("ref", old.name()));
|
||||||
|
plPgSql.writeLn();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private boolean isUpdatable(final RbacView.Column c) {
|
private boolean isUpdatable(final RbacView.Column c) {
|
||||||
return rbacDef.getUpdatableColumns().contains(c);
|
return rbacDef.getUpdatableColumns().contains(c);
|
||||||
}
|
}
|
||||||
@ -222,7 +276,7 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
.replace("${subRoleRef}", roleRef(OLD, grantDef.getSubRoleDef()))
|
.replace("${subRoleRef}", roleRef(OLD, grantDef.getSubRoleDef()))
|
||||||
.replace("${superRoleRef}", roleRef(OLD, grantDef.getSuperRoleDef()));
|
.replace("${superRoleRef}", roleRef(OLD, grantDef.getSuperRoleDef()));
|
||||||
case PERM_TO_ROLE -> "call revokePermissionFromRole(${permRef}, ${superRoleRef});"
|
case PERM_TO_ROLE -> "call revokePermissionFromRole(${permRef}, ${superRoleRef});"
|
||||||
.replace("${permRef}", findPerm(OLD, grantDef.getPermDef()))
|
.replace("${permRef}", getPerm(OLD, grantDef.getPermDef()))
|
||||||
.replace("${superRoleRef}", roleRef(OLD, grantDef.getSuperRoleDef()));
|
.replace("${superRoleRef}", roleRef(OLD, grantDef.getSuperRoleDef()));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -246,6 +300,10 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
return permRef("findPermissionId", ref, permDef);
|
return permRef("findPermissionId", ref, permDef);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String getPerm(final PostgresTriggerReference ref, final RbacPermissionDefinition permDef) {
|
||||||
|
return permRef("getPermissionId", ref, permDef);
|
||||||
|
}
|
||||||
|
|
||||||
private String createPerm(final PostgresTriggerReference ref, final RbacPermissionDefinition permDef) {
|
private String createPerm(final PostgresTriggerReference ref, final RbacPermissionDefinition permDef) {
|
||||||
return permRef("createPermission", ref, permDef);
|
return permRef("createPermission", ref, permDef);
|
||||||
}
|
}
|
||||||
@ -256,7 +314,7 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
.replace("${entityRef}", rbacDef.isRootEntityAlias(permDef.entityAlias)
|
.replace("${entityRef}", rbacDef.isRootEntityAlias(permDef.entityAlias)
|
||||||
? ref.name()
|
? ref.name()
|
||||||
: refVarName(ref, permDef.entityAlias))
|
: refVarName(ref, permDef.entityAlias))
|
||||||
.replace("${perm}", permDef.permission.permission());
|
.replace("${perm}", permDef.permission.name());
|
||||||
}
|
}
|
||||||
|
|
||||||
private String refVarName(final PostgresTriggerReference ref, final RbacView.EntityAlias entityAlias) {
|
private String refVarName(final PostgresTriggerReference ref, final RbacView.EntityAlias entityAlias) {
|
||||||
@ -301,12 +359,12 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
|
|
||||||
generatePermissionsForRole(plPgSql, role);
|
generatePermissionsForRole(plPgSql, role);
|
||||||
|
|
||||||
generateUserGrantsForRole(plPgSql, role);
|
|
||||||
|
|
||||||
generateIncomingSuperRolesForRole(plPgSql, role);
|
generateIncomingSuperRolesForRole(plPgSql, role);
|
||||||
|
|
||||||
generateOutgoingSubRolesForRole(plPgSql, role);
|
generateOutgoingSubRolesForRole(plPgSql, role);
|
||||||
|
|
||||||
|
generateUserGrantsForRole(plPgSql, role);
|
||||||
|
|
||||||
plPgSql.chopTail(",\n");
|
plPgSql.chopTail(",\n");
|
||||||
plPgSql.writeLn();
|
plPgSql.writeLn();
|
||||||
});
|
});
|
||||||
@ -333,7 +391,7 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
final var arrayElements = permissionGrantsForRole.stream()
|
final var arrayElements = permissionGrantsForRole.stream()
|
||||||
.map(RbacView.RbacGrantDefinition::getPermDef)
|
.map(RbacView.RbacGrantDefinition::getPermDef)
|
||||||
.map(RbacPermissionDefinition::getPermission)
|
.map(RbacPermissionDefinition::getPermission)
|
||||||
.map(RbacView.Permission::permission)
|
.map(RbacView.Permission::name)
|
||||||
.map(p -> "'" + p + "'")
|
.map(p -> "'" + p + "'")
|
||||||
.sorted()
|
.sorted()
|
||||||
.toList();
|
.toList();
|
||||||
@ -444,7 +502,11 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
private void generateUpdateTrigger(final StringWriter plPgSql) {
|
private void generateUpdateTrigger(final StringWriter plPgSql) {
|
||||||
|
|
||||||
generateHeader(plPgSql, "update");
|
generateHeader(plPgSql, "update");
|
||||||
|
if ( hasAnyUpdatableAndNullableEntityAliases() ) {
|
||||||
|
generateSimplifiedUpdateTriggerFunction(plPgSql);
|
||||||
|
} else {
|
||||||
generateUpdateTriggerFunction(plPgSql);
|
generateUpdateTriggerFunction(plPgSql);
|
||||||
|
}
|
||||||
|
|
||||||
plPgSql.writeLn("""
|
plPgSql.writeLn("""
|
||||||
/*
|
/*
|
||||||
|
@ -38,12 +38,26 @@ public class StringWriter {
|
|||||||
--indentLevel;
|
--indentLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void indent(int levels) {
|
||||||
|
indentLevel += levels;
|
||||||
|
}
|
||||||
|
|
||||||
|
void unindent(int levels) {
|
||||||
|
indentLevel -= levels;
|
||||||
|
}
|
||||||
|
|
||||||
void indented(final Runnable indented) {
|
void indented(final Runnable indented) {
|
||||||
indent();
|
indent();
|
||||||
indented.run();
|
indented.run();
|
||||||
unindent();
|
unindent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void indented(int levels, final Runnable indented) {
|
||||||
|
indent(levels);
|
||||||
|
indented.run();
|
||||||
|
unindent(levels);
|
||||||
|
}
|
||||||
|
|
||||||
boolean chopTail(final String tail) {
|
boolean chopTail(final String tail) {
|
||||||
if (string.toString().endsWith(tail)) {
|
if (string.toString().endsWith(tail)) {
|
||||||
string.setLength(string.length() - tail.length());
|
string.setLength(string.length() - tail.length());
|
||||||
@ -103,8 +117,8 @@ public class StringWriter {
|
|||||||
text = matcher.replaceAll(varDef.value());
|
text = matcher.replaceAll(varDef.value());
|
||||||
});
|
});
|
||||||
return text;
|
return text;
|
||||||
} catch (Exception exc) {
|
} catch (final RuntimeException exc) {
|
||||||
throw exc;
|
throw exc; // FIXME: just for debugging, remove try/catch before merging to master
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,8 @@ import static net.hostsharing.hsadminng.rbac.rbacgrant.RbacGrantsDiagramService.
|
|||||||
@Service
|
@Service
|
||||||
public class RbacGrantsDiagramService {
|
public class RbacGrantsDiagramService {
|
||||||
|
|
||||||
|
private static final int GRANT_LIMIT = 500;
|
||||||
|
|
||||||
public static void writeToFile(final String title, final String graph, final String fileName) {
|
public static void writeToFile(final String title, final String graph, final String fileName) {
|
||||||
|
|
||||||
try (BufferedWriter writer = new BufferedWriter(new FileWriter(fileName))) {
|
try (BufferedWriter writer = new BufferedWriter(new FileWriter(fileName))) {
|
||||||
@ -42,7 +44,11 @@ public class RbacGrantsDiagramService {
|
|||||||
PERMISSIONS,
|
PERMISSIONS,
|
||||||
NOT_ASSUMED,
|
NOT_ASSUMED,
|
||||||
TEST_ENTITIES,
|
TEST_ENTITIES,
|
||||||
NON_TEST_ENTITIES
|
NON_TEST_ENTITIES;
|
||||||
|
|
||||||
|
public static final EnumSet<Include> ALL = EnumSet.allOf(Include.class);
|
||||||
|
public static final EnumSet<Include> ALL_TEST_ENTITY_RELATED = EnumSet.of(USERS, DETAILS, NOT_ASSUMED, TEST_ENTITIES, PERMISSIONS);
|
||||||
|
public static final EnumSet<Include> ALL_NON_TEST_ENTITY_RELATED = EnumSet.of(USERS, DETAILS, NOT_ASSUMED, NON_TEST_ENTITIES, PERMISSIONS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
@ -55,7 +61,7 @@ public class RbacGrantsDiagramService {
|
|||||||
private EntityManager em;
|
private EntityManager em;
|
||||||
|
|
||||||
public String allGrantsToCurrentUser(final EnumSet<Include> includes) {
|
public String allGrantsToCurrentUser(final EnumSet<Include> includes) {
|
||||||
final var graph = new HashSet<RawRbacGrantEntity>();
|
final var graph = new LimitedHashSet<RawRbacGrantEntity>();
|
||||||
for ( UUID subjectUuid: context.currentSubjectsUuids() ) {
|
for ( UUID subjectUuid: context.currentSubjectsUuids() ) {
|
||||||
traverseGrantsTo(graph, subjectUuid, includes);
|
traverseGrantsTo(graph, subjectUuid, includes);
|
||||||
}
|
}
|
||||||
@ -65,6 +71,10 @@ public class RbacGrantsDiagramService {
|
|||||||
private void traverseGrantsTo(final Set<RawRbacGrantEntity> graph, final UUID refUuid, final EnumSet<Include> includes) {
|
private void traverseGrantsTo(final Set<RawRbacGrantEntity> graph, final UUID refUuid, final EnumSet<Include> includes) {
|
||||||
final var grants = rawGrantRepo.findByAscendingUuid(refUuid);
|
final var grants = rawGrantRepo.findByAscendingUuid(refUuid);
|
||||||
grants.forEach(g -> {
|
grants.forEach(g -> {
|
||||||
|
if ( g.getDescendantIdName() == null ) {
|
||||||
|
// FIXME: what's that?
|
||||||
hsh-michaelhoennig marked this conversation as resolved
Outdated
|
|||||||
|
return;
|
||||||
|
}
|
||||||
if (!includes.contains(PERMISSIONS) && g.getDescendantIdName().startsWith("perm ")) {
|
if (!includes.contains(PERMISSIONS) && g.getDescendantIdName().startsWith("perm ")) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -88,7 +98,7 @@ public class RbacGrantsDiagramService {
|
|||||||
.setParameter("targetObject", targetObject)
|
.setParameter("targetObject", targetObject)
|
||||||
.setParameter("op", op)
|
.setParameter("op", op)
|
||||||
.getSingleResult();
|
.getSingleResult();
|
||||||
final var graph = new HashSet<RawRbacGrantEntity>();
|
final var graph = new LimitedHashSet<RawRbacGrantEntity>();
|
||||||
traverseGrantsFrom(graph, refUuid, includes);
|
traverseGrantsFrom(graph, refUuid, includes);
|
||||||
return toMermaidFlowchart(graph, includes);
|
return toMermaidFlowchart(graph, includes);
|
||||||
}
|
}
|
||||||
@ -116,7 +126,7 @@ public class RbacGrantsDiagramService {
|
|||||||
)
|
)
|
||||||
.collect(groupingBy(RbacGrantsDiagramService::renderEntityIdName))
|
.collect(groupingBy(RbacGrantsDiagramService::renderEntityIdName))
|
||||||
.entrySet().stream()
|
.entrySet().stream()
|
||||||
.map(entity -> "subgraph " + quoted(entity.getKey()) + renderSubgraph(entity.getKey()) + "\n\n "
|
.map(entity -> "subgraph " + cleanId(entity.getKey()) + renderSubgraph(entity.getKey()) + "\n\n "
|
||||||
+ entity.getValue().stream()
|
+ entity.getValue().stream()
|
||||||
.map(n -> renderNode(n.idName(), n.uuid()).replace("\n", "\n "))
|
.map(n -> renderNode(n.idName(), n.uuid()).replace("\n", "\n "))
|
||||||
.sorted()
|
.sorted()
|
||||||
@ -127,14 +137,15 @@ public class RbacGrantsDiagramService {
|
|||||||
: "";
|
: "";
|
||||||
|
|
||||||
final var grants = graph.stream()
|
final var grants = graph.stream()
|
||||||
.map(g -> quoted(g.getAscendantIdName())
|
.map(g -> cleanId(g.getAscendantIdName())
|
||||||
+ " -->" + (g.isAssumed() ? " " : "|XX| ")
|
+ " -->" + (g.isAssumed() ? " " : "|XX| ")
|
||||||
+ quoted(g.getDescendantIdName()))
|
+ cleanId(g.getDescendantIdName()))
|
||||||
.sorted()
|
.sorted()
|
||||||
.collect(joining("\n"));
|
.collect(joining("\n"));
|
||||||
|
|
||||||
final var avoidCroppedNodeLabels = "%%{init:{'flowchart':{'htmlLabels':false}}}%%\n\n";
|
final var avoidCroppedNodeLabels = "%%{init:{'flowchart':{'htmlLabels':false}}}%%\n\n";
|
||||||
return (includes.contains(DETAILS) ? avoidCroppedNodeLabels : "")
|
return (includes.contains(DETAILS) ? avoidCroppedNodeLabels : "")
|
||||||
|
+ (grants.length() > GRANT_LIMIT ? "%% too many grants, graph is cropped\n" : "")
|
||||||
+ "flowchart TB\n\n"
|
+ "flowchart TB\n\n"
|
||||||
+ entities
|
+ entities
|
||||||
+ grants;
|
+ grants;
|
||||||
@ -151,7 +162,7 @@ public class RbacGrantsDiagramService {
|
|||||||
// }
|
// }
|
||||||
// return "[" + table + "\n" + entity + "]";
|
// return "[" + table + "\n" + entity + "]";
|
||||||
// }
|
// }
|
||||||
return "[" + entityId + "]";
|
return "[" + cleanId(entityId) + "]";
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String renderEntityIdName(final Node node) {
|
private static String renderEntityIdName(final Node node) {
|
||||||
@ -170,7 +181,7 @@ public class RbacGrantsDiagramService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String renderNode(final String idName, final UUID uuid) {
|
private String renderNode(final String idName, final UUID uuid) {
|
||||||
return quoted(idName) + renderNodeContent(idName, uuid);
|
return cleanId(idName) + renderNodeContent(idName, uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String renderNodeContent(final String idName, final UUID uuid) {
|
private String renderNodeContent(final String idName, final UUID uuid) {
|
||||||
@ -196,9 +207,24 @@ public class RbacGrantsDiagramService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private static String quoted(final String idName) {
|
private static String cleanId(final String idName) {
|
||||||
return idName.replace(" ", ":").replaceAll("@.*", "");
|
return idName.replace(" ", ":").replaceAll("@.*", "")
|
||||||
|
.replace("[", "").replace("]", "").replace("(", "").replace(")", "").replace(",", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class LimitedHashSet<T> extends HashSet<T> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean add(final T t) {
|
||||||
|
if (size() < GRANT_LIMIT ) {
|
||||||
|
return super.add(t);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
record Node(String idName, UUID uuid) {
|
record Node(String idName, UUID uuid) {
|
||||||
|
@ -53,7 +53,7 @@ public class TestDomainEntity implements HasUuid {
|
|||||||
SELECT * FROM test_package p
|
SELECT * FROM test_package p
|
||||||
WHERE p.uuid= ${ref}.packageUuid
|
WHERE p.uuid= ${ref}.packageUuid
|
||||||
"""))
|
"""))
|
||||||
.toRole("package", ADMIN).grantPermission("domain", INSERT)
|
.toRole("package", ADMIN).grantPermission(INSERT)
|
||||||
|
|
||||||
.createRole(OWNER, (with) -> {
|
.createRole(OWNER, (with) -> {
|
||||||
with.incomingSuperRole("package", ADMIN);
|
with.incomingSuperRole("package", ADMIN);
|
||||||
|
@ -54,7 +54,7 @@ public class TestPackageEntity implements HasUuid {
|
|||||||
SELECT * FROM test_customer c
|
SELECT * FROM test_customer c
|
||||||
WHERE c.uuid= ${ref}.customerUuid
|
WHERE c.uuid= ${ref}.customerUuid
|
||||||
"""))
|
"""))
|
||||||
.toRole("customer", ADMIN).grantPermission("package", INSERT)
|
.toRole("customer", ADMIN).grantPermission(INSERT)
|
||||||
|
|
||||||
.createRole(OWNER, (with) -> {
|
.createRole(OWNER, (with) -> {
|
||||||
with.incomingSuperRole("customer", ADMIN);
|
with.incomingSuperRole("customer", ADMIN);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
### rbac customer
|
### rbac customer
|
||||||
|
|
||||||
This code generated was by RbacViewMermaidFlowchartGenerator at 2024-03-11T11:29:11.571772062.
|
This code generated was by RbacViewMermaidFlowchartGenerator at 2024-03-21T09:53:18.451453701.
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
%%{init:{'flowchart':{'htmlLabels':false}}}%%
|
%%{init:{'flowchart':{'htmlLabels':false}}}%%
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
--liquibase formatted sql
|
--liquibase formatted sql
|
||||||
-- This code generated was by RbacViewPostgresGenerator at 2024-03-11T11:29:11.584886824.
|
-- This code generated was by RbacViewPostgresGenerator at 2024-03-21T09:53:18.467932975.
|
||||||
|
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset test-customer-rbac-OBJECT:1 endDelimiter:--//
|
--changeset test-customer-rbac-OBJECT:1 endDelimiter:--//
|
||||||
@ -36,8 +37,8 @@ begin
|
|||||||
perform createRoleWithGrants(
|
perform createRoleWithGrants(
|
||||||
testCustomerOwner(NEW),
|
testCustomerOwner(NEW),
|
||||||
permissions => array['DELETE'],
|
permissions => array['DELETE'],
|
||||||
userUuids => array[currentUserUuid()],
|
incomingSuperRoles => array[globalAdmin(unassumed())],
|
||||||
incomingSuperRoles => array[globalAdmin(unassumed())]
|
userUuids => array[currentUserUuid()]
|
||||||
);
|
);
|
||||||
|
|
||||||
perform createRoleWithGrants(
|
perform createRoleWithGrants(
|
||||||
@ -72,9 +73,9 @@ create trigger insertTriggerForTestCustomer_tg
|
|||||||
after insert on test_customer
|
after insert on test_customer
|
||||||
for each row
|
for each row
|
||||||
execute procedure insertTriggerForTestCustomer_tf();
|
execute procedure insertTriggerForTestCustomer_tf();
|
||||||
|
|
||||||
--//
|
--//
|
||||||
|
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset test-customer-rbac-INSERT:1 endDelimiter:--//
|
--changeset test-customer-rbac-INSERT:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
@ -97,8 +98,8 @@ create trigger test_customer_insert_permission_check_tg
|
|||||||
-- only global admins are allowed to insert any rows.
|
-- only global admins are allowed to insert any rows.
|
||||||
when ( not isGlobalAdmin() )
|
when ( not isGlobalAdmin() )
|
||||||
execute procedure test_customer_insert_permission_missing_tf();
|
execute procedure test_customer_insert_permission_missing_tf();
|
||||||
|
|
||||||
--//
|
--//
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset test-customer-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
--changeset test-customer-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
@ -106,13 +107,15 @@ create trigger test_customer_insert_permission_check_tg
|
|||||||
call generateRbacIdentityViewFromProjection('test_customer', $idName$
|
call generateRbacIdentityViewFromProjection('test_customer', $idName$
|
||||||
prefix
|
prefix
|
||||||
$idName$);
|
$idName$);
|
||||||
|
|
||||||
--//
|
--//
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset test-customer-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
--changeset test-customer-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
call generateRbacRestrictedView('test_customer',
|
call generateRbacRestrictedView('test_customer',
|
||||||
'reference',
|
$orderBy$
|
||||||
|
reference
|
||||||
|
$orderBy$,
|
||||||
$updates$
|
$updates$
|
||||||
reference = new.reference,
|
reference = new.reference,
|
||||||
prefix = new.prefix,
|
prefix = new.prefix,
|
||||||
@ -120,4 +123,3 @@ call generateRbacRestrictedView('test_customer',
|
|||||||
$updates$);
|
$updates$);
|
||||||
--//
|
--//
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
### rbac package
|
### rbac package
|
||||||
|
|
||||||
This code generated was by RbacViewMermaidFlowchartGenerator at 2024-03-11T11:29:11.624847792.
|
This code generated was by RbacViewMermaidFlowchartGenerator at 2024-03-21T09:53:51.758424330.
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
%%{init:{'flowchart':{'htmlLabels':false}}}%%
|
%%{init:{'flowchart':{'htmlLabels':false}}}%%
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
--liquibase formatted sql
|
--liquibase formatted sql
|
||||||
-- This code generated was by RbacViewPostgresGenerator at 2024-03-11T11:29:11.625353859.
|
-- This code generated was by RbacViewPostgresGenerator at 2024-03-21T09:53:51.767062425.
|
||||||
|
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset test-package-rbac-OBJECT:1 endDelimiter:--//
|
--changeset test-package-rbac-OBJECT:1 endDelimiter:--//
|
||||||
@ -33,9 +34,12 @@ declare
|
|||||||
|
|
||||||
begin
|
begin
|
||||||
call enterTriggerForObjectUuid(NEW.uuid);
|
call enterTriggerForObjectUuid(NEW.uuid);
|
||||||
|
|
||||||
SELECT * FROM test_customer c
|
SELECT * FROM test_customer c
|
||||||
WHERE c.uuid= NEW.customerUuid
|
WHERE c.uuid= NEW.customerUuid
|
||||||
into newCustomer;
|
INTO newCustomer;
|
||||||
|
assert newCustomer.uuid is not null, format('newCustomer must not be null for NEW.customerUuid = %s', NEW.customerUuid);
|
||||||
|
|
||||||
|
|
||||||
perform createRoleWithGrants(
|
perform createRoleWithGrants(
|
||||||
testPackageOwner(NEW),
|
testPackageOwner(NEW),
|
||||||
@ -75,9 +79,9 @@ create trigger insertTriggerForTestPackage_tg
|
|||||||
after insert on test_package
|
after insert on test_package
|
||||||
for each row
|
for each row
|
||||||
execute procedure insertTriggerForTestPackage_tf();
|
execute procedure insertTriggerForTestPackage_tf();
|
||||||
|
|
||||||
--//
|
--//
|
||||||
|
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset test-package-rbac-update-trigger:1 endDelimiter:--//
|
--changeset test-package-rbac-update-trigger:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
@ -101,14 +105,18 @@ begin
|
|||||||
|
|
||||||
SELECT * FROM test_customer c
|
SELECT * FROM test_customer c
|
||||||
WHERE c.uuid= OLD.customerUuid
|
WHERE c.uuid= OLD.customerUuid
|
||||||
into oldCustomer;
|
INTO oldCustomer;
|
||||||
|
assert oldCustomer.uuid is not null, format('oldCustomer must not be null for OLD.customerUuid = %s', OLD.customerUuid);
|
||||||
|
|
||||||
SELECT * FROM test_customer c
|
SELECT * FROM test_customer c
|
||||||
WHERE c.uuid= NEW.customerUuid
|
WHERE c.uuid= NEW.customerUuid
|
||||||
into newCustomer;
|
INTO newCustomer;
|
||||||
hsh-michaelhoennig marked this conversation as resolved
Outdated
|
|||||||
|
assert newCustomer.uuid is not null, format('newCustomer must not be null for NEW.customerUuid = %s', NEW.customerUuid);
|
||||||
|
|
||||||
|
|
||||||
if NEW.customerUuid <> OLD.customerUuid then
|
if NEW.customerUuid <> OLD.customerUuid then
|
||||||
|
|
||||||
call revokePermissionFromRole(findPermissionId(OLD.uuid, 'INSERT'), testCustomerAdmin(oldCustomer));
|
call revokePermissionFromRole(getPermissionId(OLD.uuid, 'INSERT'), testCustomerAdmin(oldCustomer));
|
||||||
|
|
||||||
call revokeRoleFromRole(testPackageOwner(OLD), testCustomerAdmin(oldCustomer));
|
call revokeRoleFromRole(testPackageOwner(OLD), testCustomerAdmin(oldCustomer));
|
||||||
call grantRoleToRole(testPackageOwner(NEW), testCustomerAdmin(newCustomer));
|
call grantRoleToRole(testPackageOwner(NEW), testCustomerAdmin(newCustomer));
|
||||||
@ -138,9 +146,9 @@ create trigger updateTriggerForTestPackage_tg
|
|||||||
after update on test_package
|
after update on test_package
|
||||||
for each row
|
for each row
|
||||||
execute procedure updateTriggerForTestPackage_tf();
|
execute procedure updateTriggerForTestPackage_tf();
|
||||||
|
|
||||||
--//
|
--//
|
||||||
|
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset test-package-rbac-INSERT:1 endDelimiter:--//
|
--changeset test-package-rbac-INSERT:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
@ -160,7 +168,7 @@ do language plpgsql $$
|
|||||||
LOOP
|
LOOP
|
||||||
roleUuid := findRoleId(testCustomerAdmin(row));
|
roleUuid := findRoleId(testCustomerAdmin(row));
|
||||||
permissionUuid := createPermission(row.uuid, 'INSERT', 'test_package');
|
permissionUuid := createPermission(row.uuid, 'INSERT', 'test_package');
|
||||||
call grantPermissionToRole(roleUuid, permissionUuid);
|
call grantPermissionToRole(permissionUuid, roleUuid);
|
||||||
END LOOP;
|
END LOOP;
|
||||||
END;
|
END;
|
||||||
$$;
|
$$;
|
||||||
@ -174,12 +182,13 @@ create or replace function test_package_test_customer_insert_tf()
|
|||||||
strict as $$
|
strict as $$
|
||||||
begin
|
begin
|
||||||
call grantPermissionToRole(
|
call grantPermissionToRole(
|
||||||
testCustomerAdmin(NEW),
|
createPermission(NEW.uuid, 'INSERT', 'test_package'),
|
||||||
createPermission(NEW.uuid, 'INSERT', 'test_package'));
|
testCustomerAdmin(NEW));
|
||||||
return NEW;
|
return NEW;
|
||||||
end; $$;
|
end; $$;
|
||||||
|
|
||||||
create trigger test_package_test_customer_insert_tg
|
-- z_... is to put it at the end of after insert triggers, to make sure the roles exist
|
||||||
|
create trigger z_test_package_test_customer_insert_tg
|
||||||
after insert on test_customer
|
after insert on test_customer
|
||||||
for each row
|
for each row
|
||||||
execute procedure test_package_test_customer_insert_tf();
|
execute procedure test_package_test_customer_insert_tf();
|
||||||
@ -191,17 +200,27 @@ create or replace function test_package_insert_permission_missing_tf()
|
|||||||
returns trigger
|
returns trigger
|
||||||
language plpgsql as $$
|
language plpgsql as $$
|
||||||
begin
|
begin
|
||||||
raise exception '[403] insert into test_package not allowed for current subjects % (%)',
|
if ( not hasInsertPermission(
|
||||||
|
( SELECT customer.uuid FROM
|
||||||
|
|
||||||
|
(SELECT * FROM test_customer c
|
||||||
|
WHERE c.uuid= NEW.customerUuid
|
||||||
|
) AS customer
|
||||||
|
|
||||||
|
), 'INSERT', 'test_package') ) then
|
||||||
|
raise exception
|
||||||
|
'[403] insert into test_package not allowed for current subjects % (%)',
|
||||||
currentSubjects(), currentSubjectsUuids();
|
currentSubjects(), currentSubjectsUuids();
|
||||||
|
end if;
|
||||||
|
return NEW;
|
||||||
end; $$;
|
end; $$;
|
||||||
|
|
||||||
create trigger test_package_insert_permission_check_tg
|
create trigger test_package_insert_permission_check_tg
|
||||||
before insert on test_package
|
before insert on test_package
|
||||||
for each row
|
for each row
|
||||||
when ( not hasInsertPermission(NEW.customerUuid, 'INSERT', 'test_package') )
|
|
||||||
execute procedure test_package_insert_permission_missing_tf();
|
execute procedure test_package_insert_permission_missing_tf();
|
||||||
|
|
||||||
--//
|
--//
|
||||||
|
|
||||||
hsh-michaelhoennig marked this conversation as resolved
Outdated
|
|||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset test-package-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
--changeset test-package-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
@ -209,13 +228,15 @@ create trigger test_package_insert_permission_check_tg
|
|||||||
call generateRbacIdentityViewFromProjection('test_package', $idName$
|
call generateRbacIdentityViewFromProjection('test_package', $idName$
|
||||||
name
|
name
|
||||||
$idName$);
|
$idName$);
|
||||||
|
|
||||||
--//
|
--//
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset test-package-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
--changeset test-package-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
call generateRbacRestrictedView('test_package',
|
call generateRbacRestrictedView('test_package',
|
||||||
'name',
|
$orderBy$
|
||||||
|
name
|
||||||
|
$orderBy$,
|
||||||
$updates$
|
$updates$
|
||||||
version = new.version,
|
version = new.version,
|
||||||
customerUuid = new.customerUuid,
|
customerUuid = new.customerUuid,
|
||||||
@ -223,4 +244,3 @@ call generateRbacRestrictedView('test_package',
|
|||||||
$updates$);
|
$updates$);
|
||||||
--//
|
--//
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
### rbac domain
|
### rbac domain
|
||||||
|
|
||||||
This code generated was by RbacViewMermaidFlowchartGenerator at 2024-03-11T11:29:11.644658132.
|
This code generated was by RbacViewMermaidFlowchartGenerator at 2024-03-21T09:53:31.860490657.
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
%%{init:{'flowchart':{'htmlLabels':false}}}%%
|
%%{init:{'flowchart':{'htmlLabels':false}}}%%
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
--liquibase formatted sql
|
--liquibase formatted sql
|
||||||
-- This code generated was by RbacViewPostgresGenerator at 2024-03-11T11:29:11.645391647.
|
-- This code generated was by RbacViewPostgresGenerator at 2024-03-21T09:53:31.873124905.
|
||||||
|
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset test-domain-rbac-OBJECT:1 endDelimiter:--//
|
--changeset test-domain-rbac-OBJECT:1 endDelimiter:--//
|
||||||
@ -33,9 +34,12 @@ declare
|
|||||||
|
|
||||||
begin
|
begin
|
||||||
call enterTriggerForObjectUuid(NEW.uuid);
|
call enterTriggerForObjectUuid(NEW.uuid);
|
||||||
|
|
||||||
SELECT * FROM test_package p
|
SELECT * FROM test_package p
|
||||||
WHERE p.uuid= NEW.packageUuid
|
WHERE p.uuid= NEW.packageUuid
|
||||||
into newPackage;
|
INTO newPackage;
|
||||||
|
assert newPackage.uuid is not null, format('newPackage must not be null for NEW.packageUuid = %s', NEW.packageUuid);
|
||||||
|
|
||||||
|
|
||||||
perform createRoleWithGrants(
|
perform createRoleWithGrants(
|
||||||
testDomainOwner(NEW),
|
testDomainOwner(NEW),
|
||||||
@ -71,9 +75,9 @@ create trigger insertTriggerForTestDomain_tg
|
|||||||
after insert on test_domain
|
after insert on test_domain
|
||||||
for each row
|
for each row
|
||||||
execute procedure insertTriggerForTestDomain_tf();
|
execute procedure insertTriggerForTestDomain_tf();
|
||||||
|
|
||||||
--//
|
--//
|
||||||
|
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset test-domain-rbac-update-trigger:1 endDelimiter:--//
|
--changeset test-domain-rbac-update-trigger:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
@ -97,14 +101,18 @@ begin
|
|||||||
|
|
||||||
SELECT * FROM test_package p
|
SELECT * FROM test_package p
|
||||||
WHERE p.uuid= OLD.packageUuid
|
WHERE p.uuid= OLD.packageUuid
|
||||||
into oldPackage;
|
INTO oldPackage;
|
||||||
|
assert oldPackage.uuid is not null, format('oldPackage must not be null for OLD.packageUuid = %s', OLD.packageUuid);
|
||||||
|
|
||||||
SELECT * FROM test_package p
|
SELECT * FROM test_package p
|
||||||
WHERE p.uuid= NEW.packageUuid
|
WHERE p.uuid= NEW.packageUuid
|
||||||
into newPackage;
|
INTO newPackage;
|
||||||
|
assert newPackage.uuid is not null, format('newPackage must not be null for NEW.packageUuid = %s', NEW.packageUuid);
|
||||||
|
|
||||||
|
|
||||||
if NEW.packageUuid <> OLD.packageUuid then
|
if NEW.packageUuid <> OLD.packageUuid then
|
||||||
|
|
||||||
call revokePermissionFromRole(findPermissionId(OLD.uuid, 'INSERT'), testPackageAdmin(oldPackage));
|
call revokePermissionFromRole(getPermissionId(OLD.uuid, 'INSERT'), testPackageAdmin(oldPackage));
|
||||||
|
|
||||||
call revokeRoleFromRole(testDomainOwner(OLD), testPackageAdmin(oldPackage));
|
call revokeRoleFromRole(testDomainOwner(OLD), testPackageAdmin(oldPackage));
|
||||||
call grantRoleToRole(testDomainOwner(NEW), testPackageAdmin(newPackage));
|
call grantRoleToRole(testDomainOwner(NEW), testPackageAdmin(newPackage));
|
||||||
@ -137,9 +145,9 @@ create trigger updateTriggerForTestDomain_tg
|
|||||||
after update on test_domain
|
after update on test_domain
|
||||||
for each row
|
for each row
|
||||||
execute procedure updateTriggerForTestDomain_tf();
|
execute procedure updateTriggerForTestDomain_tf();
|
||||||
|
|
||||||
--//
|
--//
|
||||||
|
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset test-domain-rbac-INSERT:1 endDelimiter:--//
|
--changeset test-domain-rbac-INSERT:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
@ -159,7 +167,7 @@ do language plpgsql $$
|
|||||||
LOOP
|
LOOP
|
||||||
roleUuid := findRoleId(testPackageAdmin(row));
|
roleUuid := findRoleId(testPackageAdmin(row));
|
||||||
permissionUuid := createPermission(row.uuid, 'INSERT', 'test_domain');
|
permissionUuid := createPermission(row.uuid, 'INSERT', 'test_domain');
|
||||||
call grantPermissionToRole(roleUuid, permissionUuid);
|
call grantPermissionToRole(permissionUuid, roleUuid);
|
||||||
END LOOP;
|
END LOOP;
|
||||||
END;
|
END;
|
||||||
$$;
|
$$;
|
||||||
@ -173,12 +181,13 @@ create or replace function test_domain_test_package_insert_tf()
|
|||||||
strict as $$
|
strict as $$
|
||||||
begin
|
begin
|
||||||
call grantPermissionToRole(
|
call grantPermissionToRole(
|
||||||
testPackageAdmin(NEW),
|
createPermission(NEW.uuid, 'INSERT', 'test_domain'),
|
||||||
createPermission(NEW.uuid, 'INSERT', 'test_domain'));
|
testPackageAdmin(NEW));
|
||||||
return NEW;
|
return NEW;
|
||||||
end; $$;
|
end; $$;
|
||||||
|
|
||||||
create trigger test_domain_test_package_insert_tg
|
-- z_... is to put it at the end of after insert triggers, to make sure the roles exist
|
||||||
|
create trigger z_test_domain_test_package_insert_tg
|
||||||
after insert on test_package
|
after insert on test_package
|
||||||
for each row
|
for each row
|
||||||
execute procedure test_domain_test_package_insert_tf();
|
execute procedure test_domain_test_package_insert_tf();
|
||||||
@ -190,17 +199,27 @@ create or replace function test_domain_insert_permission_missing_tf()
|
|||||||
returns trigger
|
returns trigger
|
||||||
language plpgsql as $$
|
language plpgsql as $$
|
||||||
begin
|
begin
|
||||||
raise exception '[403] insert into test_domain not allowed for current subjects % (%)',
|
if ( not hasInsertPermission(
|
||||||
|
( SELECT package.uuid FROM
|
||||||
|
|
||||||
|
(SELECT * FROM test_package p
|
||||||
|
WHERE p.uuid= NEW.packageUuid
|
||||||
|
) AS package
|
||||||
|
|
||||||
|
), 'INSERT', 'test_domain') ) then
|
||||||
|
raise exception
|
||||||
|
'[403] insert into test_domain not allowed for current subjects % (%)',
|
||||||
currentSubjects(), currentSubjectsUuids();
|
currentSubjects(), currentSubjectsUuids();
|
||||||
|
end if;
|
||||||
|
return NEW;
|
||||||
end; $$;
|
end; $$;
|
||||||
|
|
||||||
create trigger test_domain_insert_permission_check_tg
|
create trigger test_domain_insert_permission_check_tg
|
||||||
before insert on test_domain
|
before insert on test_domain
|
||||||
for each row
|
for each row
|
||||||
when ( not hasInsertPermission(NEW.packageUuid, 'INSERT', 'test_domain') )
|
|
||||||
execute procedure test_domain_insert_permission_missing_tf();
|
execute procedure test_domain_insert_permission_missing_tf();
|
||||||
|
|
||||||
--//
|
--//
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset test-domain-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
--changeset test-domain-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
@ -208,13 +227,15 @@ create trigger test_domain_insert_permission_check_tg
|
|||||||
call generateRbacIdentityViewFromProjection('test_domain', $idName$
|
call generateRbacIdentityViewFromProjection('test_domain', $idName$
|
||||||
name
|
name
|
||||||
$idName$);
|
$idName$);
|
||||||
|
|
||||||
--//
|
--//
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset test-domain-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
--changeset test-domain-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
call generateRbacRestrictedView('test_domain',
|
call generateRbacRestrictedView('test_domain',
|
||||||
'name',
|
$orderBy$
|
||||||
|
name
|
||||||
|
$orderBy$,
|
||||||
$updates$
|
$updates$
|
||||||
version = new.version,
|
version = new.version,
|
||||||
packageUuid = new.packageUuid,
|
packageUuid = new.packageUuid,
|
||||||
@ -222,4 +243,3 @@ call generateRbacRestrictedView('test_domain',
|
|||||||
$updates$);
|
$updates$);
|
||||||
--//
|
--//
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user
rename missing to check