fix indrirect permission by indirect foreign key

This commit is contained in:
Michael Hoennig 2024-03-25 05:57:58 +01:00
parent 78ecf98913
commit e6ef5b59c7
42 changed files with 532 additions and 245 deletions

View File

@ -21,6 +21,7 @@ import java.util.UUID;
import static java.util.Optional.ofNullable; import static java.util.Optional.ofNullable;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NULLABLE; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NULLABLE;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
@ -165,8 +166,7 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
FROM hs_office_bankaccount AS b FROM hs_office_bankaccount AS b
WHERE b.uuid = ${REF}.refundBankAccountUuid WHERE b.uuid = ${REF}.refundBankAccountUuid
"""), """),
NULLABLE NULLABLE)
)
.toRole("refundBankAccount", ADMIN).grantRole("debitorRel", AGENT) .toRole("refundBankAccount", ADMIN).grantRole("debitorRel", AGENT)
.toRole("debitorRel", AGENT).grantRole("refundBankAccount", REFERRER) .toRole("debitorRel", AGENT).grantRole("refundBankAccount", REFERRER)
@ -179,8 +179,8 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
ON debitorRel.type = 'DEBITOR' AND debitorRel.anchorUuid = partnerRel.holderUuid ON debitorRel.type = 'DEBITOR' AND debitorRel.anchorUuid = partnerRel.holderUuid
WHERE partnerRel.type = 'PARTNER' WHERE partnerRel.type = 'PARTNER'
AND ${REF}.debitorRelUuid = debitorRel.uuid AND ${REF}.debitorRelUuid = debitorRel.uuid
""") """),
) NOT_NULL)
.toRole("partnerRel", ADMIN).grantRole("debitorRel", ADMIN) .toRole("partnerRel", ADMIN).grantRole("debitorRel", ADMIN)
.toRole("partnerRel", AGENT).grantRole("debitorRel", AGENT) .toRole("partnerRel", AGENT).grantRole("debitorRel", AGENT)
.toRole("debitorRel", AGENT).grantRole("partnerRel", TENANT) .toRole("debitorRel", AGENT).grantRole("partnerRel", TENANT)

View File

@ -20,6 +20,7 @@ import java.util.UUID;
import static net.hostsharing.hsadminng.mapper.PostgresDateRange.*; import static net.hostsharing.hsadminng.mapper.PostgresDateRange.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.SELECT; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.SELECT;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR;
@ -135,7 +136,8 @@ public class HsOfficeMembershipEntity implements HasUuid, Stringifyable {
FROM hs_office_partner AS p FROM hs_office_partner AS p
JOIN hs_office_relation AS r ON r.uuid = p.partnerRelUuid JOIN hs_office_relation AS r ON r.uuid = p.partnerRelUuid
WHERE p.uuid = ${REF}.partnerUuid WHERE p.uuid = ${REF}.partnerUuid
""")) """),
NOT_NULL)
.toRole("partnerRel", ADMIN).grantPermission(INSERT) .toRole("partnerRel", ADMIN).grantPermission(INSERT)
.createRole(OWNER, (with) -> { .createRole(OWNER, (with) -> {

View File

@ -16,6 +16,7 @@ import java.util.UUID;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.GLOBAL; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.GLOBAL;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
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.Role.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
@ -90,16 +91,16 @@ public class HsOfficeRelationEntity implements HasUuid, Stringifyable {
.withUpdatableColumns("contactUuid") .withUpdatableColumns("contactUuid")
.importEntityAlias("anchorPerson", HsOfficePersonEntity.class, .importEntityAlias("anchorPerson", HsOfficePersonEntity.class,
dependsOnColumn("anchorUuid"), dependsOnColumn("anchorUuid"),
fetchedBySql("select * from hs_office_person as p where p.uuid = ${REF}.anchorUuid") fetchedBySql("select * from hs_office_person as p where p.uuid = ${REF}.anchorUuid"),
) NOT_NULL)
.importEntityAlias("holderPerson", HsOfficePersonEntity.class, .importEntityAlias("holderPerson", HsOfficePersonEntity.class,
dependsOnColumn("holderUuid"), dependsOnColumn("holderUuid"),
fetchedBySql("select * from hs_office_person as p where p.uuid = ${REF}.holderUuid") fetchedBySql("select * from hs_office_person as p where p.uuid = ${REF}.holderUuid"),
) NOT_NULL)
.importEntityAlias("contact", HsOfficeContactEntity.class, .importEntityAlias("contact", HsOfficeContactEntity.class,
dependsOnColumn("contactUuid"), dependsOnColumn("contactUuid"),
fetchedBySql("select * from hs_office_contact as c where c.uuid = ${REF}.contactUuid") fetchedBySql("select * from hs_office_contact as c where c.uuid = ${REF}.contactUuid"),
) NOT_NULL)
.createRole(OWNER, (with) -> { .createRole(OWNER, (with) -> {
with.owningUser(CREATOR); with.owningUser(CREATOR);
with.incomingSuperRole(GLOBAL, ADMIN); with.incomingSuperRole(GLOBAL, ADMIN);

View File

@ -21,6 +21,7 @@ import java.util.UUID;
import static net.hostsharing.hsadminng.mapper.PostgresDateRange.*; import static net.hostsharing.hsadminng.mapper.PostgresDateRange.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.GLOBAL; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.GLOBAL;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
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.Role.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
@ -110,12 +111,12 @@ public class HsOfficeSepaMandateEntity implements Stringifyable, HasUuid {
FROM hs_office_relation debitorRel FROM hs_office_relation debitorRel
JOIN hs_office_debitor debitor ON debitor.debitorRelUuid = debitorRel.uuid JOIN hs_office_debitor debitor ON debitor.debitorRelUuid = debitorRel.uuid
WHERE debitor.uuid = ${REF}.debitorUuid WHERE debitor.uuid = ${REF}.debitorUuid
""") """),
) NOT_NULL)
.importEntityAlias("bankAccount", HsOfficeBankAccountEntity.class, .importEntityAlias("bankAccount", HsOfficeBankAccountEntity.class,
dependsOnColumn("bankAccountUuid"), dependsOnColumn("bankAccountUuid"),
autoFetched() autoFetched(),
) NOT_NULL)
.createRole(OWNER, (with) -> { .createRole(OWNER, (with) -> {
with.owningUser(CREATOR); with.owningUser(CREATOR);

View File

@ -113,8 +113,17 @@ public class InsertTriggerGenerator {
"invalid global role for INSERT permission: " + g.getSuperRoleDef().getRole()); "invalid global role for INSERT permission: " + g.getSuperRoleDef().getRole());
} }
} }
} else {
final var superRoleEntityAlias = g.getSuperRoleDef().getEntityAlias();
// TODO: Maybe this should depend on the indirection degree of the fetchSql?
// Maybe we need a separate fetchedBy method for all the simple, direct cases?
if (superRoleEntityAlias.fetchSql().sql.contains("JOIN ")) {
generateInsertPermissionTriggerAllowByRoleOfIndirectForeignKey(plPgSql, g);
} else { } else {
generateInsertPermissionTriggerAllowByRoleOfDirectForeignKey(plPgSql, g); generateInsertPermissionTriggerAllowByRoleOfDirectForeignKey(plPgSql, g);
}
} }
}, },
() -> { () -> {
@ -149,6 +158,50 @@ public class InsertTriggerGenerator {
with("referenceColumn", g.getSuperRoleDef().getEntityAlias().dependsOnColumName())); with("referenceColumn", g.getSuperRoleDef().getEntityAlias().dependsOnColumName()));
} }
private void generateInsertPermissionTriggerAllowByRoleOfIndirectForeignKey(
final StringWriter plPgSql,
final RbacView.RbacGrantDefinition g) {
plPgSql.writeLn("""
/**
Checks if the user or assumed roles are allowed to insert a row to ${rawSubTable},
where the check is performed by an indirect role.
An indirect role is a role FIXME.
*/
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) { private void generateInsertPermissionTriggerAllowOnlyGlobalAdmin(final StringWriter plPgSql) {
plPgSql.writeLn(""" plPgSql.writeLn("""
/** /**

View File

@ -66,6 +66,21 @@ public class RbacView {
private EntityAlias rootEntityAliasProxy; private EntityAlias rootEntityAliasProxy;
private RbacRoleDefinition previousRoleDef; private RbacRoleDefinition previousRoleDef;
/** Crates an RBAC definition template for the given entity class and defining the given alias.
*
* @param alias
* an alias name for this entity/table, which can be used in further grants
*
* @param entityClass
* the Java class for which this RBAC definition is to be defined
* (the class to which the calling method belongs)
*
* @return
* the newly created RBAC definition template
*
* @param <E>
* a JPA entity class extending RbacObject
*/
public static <E extends RbacObject> RbacView rbacViewFor(final String alias, final Class<E> entityClass) { public static <E extends RbacObject> RbacView rbacViewFor(final String alias, final Class<E> entityClass) {
return new RbacView(alias, entityClass); return new RbacView(alias, entityClass);
} }
@ -77,22 +92,71 @@ public class RbacView {
entityAliases.put("global", new EntityAlias("global")); entityAliases.put("global", new EntityAlias("global"));
} }
/**
* Specifies, which columns of the restricted view are updatable at all.
*
* @param columnNames
* A list of the updatable columns.
*
* @return
* the `this` instance itself to allow chained calls.
*/
public RbacView withUpdatableColumns(final String... columnNames) { public RbacView withUpdatableColumns(final String... columnNames) {
Collections.addAll(updatableColumns, columnNames); Collections.addAll(updatableColumns, columnNames);
verifyVersionColumnExists(); verifyVersionColumnExists();
return this; return this;
} }
/** Specifies the SQL query which creates the identity view for this entity.
*
* <p>An identity view is a view which maps an objectUuid to an idName.
* The idName should be a human-readable representation of the row, but as short as possible.
* The idName must only consist of letters (A-Z, a-z), digits (0-9), dash (-), dot (.) and unserscore '_'.
* It's used to create the object-specific-role-names like test_customer#abc.admin - here 'abc' is the idName.
* The idName not necessarily unique in a table, but it should be avoided.
* </p>
*
* @param sqlExpression
* Either specify an SQL projection (the part between SELECT and FROM), e.g. `SQL.projection("columnName")
* or the whole SELECT query returning the uuid and idName columns,
* e.g. `SQL.query("SELECT ... AS uuid, ... AS idName FROM ... JOIN ...").
* Only add really important columns, just enough to create a short human-readable representation.
*
* @return
* the `this` instance itself to allow chained calls.
*/
public RbacView withIdentityView(final SQL sqlExpression) { public RbacView withIdentityView(final SQL sqlExpression) {
this.identityViewSqlQuery = sqlExpression; this.identityViewSqlQuery = sqlExpression;
return this; return this;
} }
/**
* Specifies a ORDER BY clause for the generated restricted view.
*
* <p>A restricted view is generated, no matter if the order was specified or not.</p>
*
* @param orderBySqlExpression
* That's the part behind `ORDER BY`, e.g. `SQL.expression("prefix").
*
* @return
* the `this` instance itself to allow chained calls.
*/
public RbacView withRestrictedViewOrderBy(final SQL orderBySqlExpression) { public RbacView withRestrictedViewOrderBy(final SQL orderBySqlExpression) {
this.orderBySqlExpression = orderBySqlExpression; this.orderBySqlExpression = orderBySqlExpression;
return this; return this;
} }
/**
* Specifies that the given role (OWNER, ADMIN, ...) is to be created for new/updated roles in this table.
*
* @param role
* OWNER, ADMIN, AGENT etc.
* @param with
* a lambda which receives the created role to create grants and permissions to and from the newly created role,
* e.g. the owning user, incoming superroles, outgoing subroles
* @return
* the `this` instance itself to allow chained calls.
*/
public RbacView createRole(final Role role, final Consumer<RbacRoleDefinition> with) { public RbacView createRole(final Role role, final Consumer<RbacRoleDefinition> with) {
final RbacRoleDefinition newRoleDef = findRbacRole(rootEntityAlias, role).toCreate(); final RbacRoleDefinition newRoleDef = findRbacRole(rootEntityAlias, role).toCreate();
with.accept(newRoleDef); with.accept(newRoleDef);
@ -100,6 +164,15 @@ public class RbacView {
return this; return this;
} }
/**
* Specifies that the given role (OWNER, ADMIN, ...) is to be created for new/updated roles in this table,
* which is becomes sub-role of the previously created role.
*
* @param role
* OWNER, ADMIN, AGENT etc.
* @return
* the `this` instance itself to allow chained calls.
*/
public RbacView createSubRole(final Role role) { public RbacView createSubRole(final Role role) {
final RbacRoleDefinition newRoleDef = findRbacRole(rootEntityAlias, role).toCreate(); final RbacRoleDefinition newRoleDef = findRbacRole(rootEntityAlias, role).toCreate();
findOrCreateGrantDef(newRoleDef, previousRoleDef).toCreate(); findOrCreateGrantDef(newRoleDef, previousRoleDef).toCreate();
@ -107,6 +180,19 @@ public class RbacView {
return this; return this;
} }
/**
* Specifies that the given role (OWNER, ADMIN, ...) is to be created for new/updated roles in this table,
* which is becomes sub-role of the previously created role.
*
* @param role
* OWNER, ADMIN, AGENT etc.
* @param with
* a lambda which receives the created role to create grants and permissions to and from the newly created role,
* e.g. the owning user, incoming superroles, outgoing subroles
* @return
* the `this` instance itself to allow chained calls.
*/
public RbacView createSubRole(final Role role, final Consumer<RbacRoleDefinition> with) { public RbacView createSubRole(final Role role, final Consumer<RbacRoleDefinition> with) {
final RbacRoleDefinition newRoleDef = findRbacRole(rootEntityAlias, role).toCreate(); final RbacRoleDefinition newRoleDef = findRbacRole(rootEntityAlias, role).toCreate();
findOrCreateGrantDef(newRoleDef, previousRoleDef).toCreate(); findOrCreateGrantDef(newRoleDef, previousRoleDef).toCreate();
@ -115,10 +201,38 @@ public class RbacView {
return this; return this;
} }
/**
* Specifies that the given permission is to be created for each new row in the target table.
*
* <p>Grants to permissions created by this method have to be specified separately,
* often it's easier to read to use createRole/createSubRole and use with.permission(...).</p>
*
* @param permission
* e.g. INSERT, SELECT, UPDATE, DELETE
*
* @return
* the newly created permission definition
*/
public RbacPermissionDefinition createPermission(final Permission permission) { public RbacPermissionDefinition createPermission(final Permission permission) {
return createPermission(rootEntityAlias, permission); return createPermission(rootEntityAlias, permission);
} }
/**
* Specifies that the given permission is to be created for each new row in the target table,
* but for another table, e.g. a table with details data with different access rights.
*
* <p>Grants to permissions created by this method have to be specified separately,
* often it's easier to read to use createRole/createSubRole and use with.permission(...).</p>
*
* @param entityAliasName
* A previously defined entity alias name.
*
* @param permission
* e.g. INSERT, SELECT, UPDATE, DELETE
*
* @return
* the newly created permission definition
*/
public RbacPermissionDefinition createPermission(final String entityAliasName, final Permission permission) { public RbacPermissionDefinition createPermission(final String entityAliasName, final Permission permission) {
return createPermission(findEntityAlias(entityAliasName), permission); return createPermission(findEntityAlias(entityAliasName), permission);
} }
@ -134,6 +248,32 @@ public class RbacView {
return this; return this;
} }
/**
* Imports the RBAC template from the given entity class and defines an alias name for it.
* This method is especially for proxy-entities, if the root entity does not have its own
* roles, a proxy-entity can be specified and its roles can be used instead.
*
* @param aliasName
* An alias name for the entity class. The same entity class can be imported multiple times,
* if multiple references to its table exist, then distinct alias names habe to be defined.
*
* @param entityClass
* A JPA entity class extending RbacObject which also implements an `rbac` method returning
* its RBAC specification.
*
* @param fetchSql
* An SQL SELECT statement which fetches the referenced row. Use `${REF}` to speficiy the
* newly created or updated row (will be replaced by NEW/OLD from the trigger method).
*
* @param dependsOnColum
* The column, usually containing an uuid, on which this other table depends.
*
* @return
* the newly created permission definition
*
* @param <EC>
* a JPA entity class extending RbacObject
*/
public <EC extends RbacObject> RbacView importRootEntityAliasProxy( public <EC extends RbacObject> RbacView importRootEntityAliasProxy(
final String aliasName, final String aliasName,
final Class<? extends HasUuid> entityClass, final Class<? extends HasUuid> entityClass,
@ -146,6 +286,18 @@ public class RbacView {
return this; return this;
} }
/**
* Imports the RBAC template from the given entity class and defines an alias name for it.
* This method is especially to declare sub-entities, e.g. details to a main object.
*
* @see {@link}
*
* @return
* the newly created permission definition
*
* @param <EC>
* a JPA entity class extending RbacObject
*/
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) {
@ -153,6 +305,33 @@ public class RbacView {
return this; return this;
} }
/**
* Imports the RBAC template from the given entity class and defines an anlias name for it.
*
* @param aliasName
* An alias name for the entity class. The same entity class can be imported multiple times,
* if multiple references to its table exist, then distinct alias names habe to be defined.
*
* @param entityClass
* A JPA entity class extending RbacObject which also implements an `rbac` method returning
* its RBAC specification.
*
* @param fetchSql
* An SQL SELECT statement which fetches the referenced row. Use `${REF}` to speficiy the
* newly created or updated row (will be replaced by NEW/OLD from the trigger method).
*
* @param dependsOnColum
* The column, usually containing an uuid, on which this other table depends.
*
* @param nullable
* Specifies whether the dependsOnColum is nullable or not.
*
* @return
* the newly created permission definition
*
* @param <EC>
* a JPA entity class extending RbacObject
*/
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 Nullable nullable) { final Column dependsOnColum, final SQL fetchSql, final Nullable nullable) {
@ -160,13 +339,7 @@ public class RbacView {
return this; return this;
} }
public RbacView importEntityAlias( // TODO: remove once it's not used in HsOffice...Entity anymore
final String aliasName, final Class<? extends HasUuid> entityClass,
final Column dependsOnColum, final SQL fetchSql) {
importEntityAliasImpl(aliasName, entityClass, fetchSql, dependsOnColum, false, NOT_NULL);
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) {
@ -232,6 +405,16 @@ public class RbacView {
} }
} }
/**
* Starts declaring a grant to a given role.
*
* @param entityAlias
* A previously speciried entity alias name.
* @param role
* OWNER, ADMIN, AGENT, ...
* @return
* a grant builder
*/
public RbacGrantBuilder toRole(final String entityAlias, final Role role) { public RbacGrantBuilder toRole(final String entityAlias, final Role role) {
return new RbacGrantBuilder(entityAlias, role); return new RbacGrantBuilder(entityAlias, role);
} }
@ -430,6 +613,16 @@ public class RbacView {
permDefs.add(this); permDefs.add(this);
} }
/**
* Grants the permission under definition to the given role.
*
* @param entityAlias
* A previously declared entity alias name.
* @param role
* OWNER, ADMIN, ...
* @return
* The RbacView specification to which this permission definition belongs.
*/
public RbacView grantedTo(final String entityAlias, final Role role) { public RbacView grantedTo(final String entityAlias, final Role role) {
findOrCreateGrantDef(this, findRbacRole(entityAlias, role)).toCreate(); findOrCreateGrantDef(this, findRbacRole(entityAlias, role)).toCreate();
return RbacView.this; return RbacView.this;
@ -460,19 +653,61 @@ public class RbacView {
return this; return this;
} }
/**
* Specifies which user becomes the owner of newly created objects.
* @param userRole
* GLOBAL_ADMIN, CREATOR, ...
* @return
* The grant definition for further chained calls.
*/
public RbacGrantDefinition owningUser(final RbacUserReference.UserRole userRole) { public RbacGrantDefinition owningUser(final RbacUserReference.UserRole userRole) {
return grantRoleToUser(this, findUserRef(userRole)); return grantRoleToUser(this, findUserRef(userRole));
} }
/**
* Specifies which permission is to be created for newly created objects.
* @param permission
* INSERT, SELECT, ...
* @return
* The grant definition for further chained calls.
*/
public RbacGrantDefinition permission(final Permission permission) { public RbacGrantDefinition permission(final Permission permission) {
return grantPermissionToRole(createPermission(entityAlias, permission), this); return grantPermissionToRole(createPermission(entityAlias, permission), this);
} }
/**
* Specifies in incoming super role which gets granted the role under definition.
*
* <p>Incoming means an incoming grant arrow in our grant-diagrams.
* Super-role means that it's the role to which another role is granted.
* Both means actually the same, just in different aspects.</p>
*
* @param entityAlias
* A previously declared entity alias name.
* @param role
* OWNER, ADMIN, ...
* @return
* The grant definition for further chained calls.
*/
public RbacGrantDefinition incomingSuperRole(final String entityAlias, final Role role) { public RbacGrantDefinition incomingSuperRole(final String entityAlias, final Role role) {
final var incomingSuperRole = findRbacRole(entityAlias, role); final var incomingSuperRole = findRbacRole(entityAlias, role);
return grantSubRoleToSuperRole(this, incomingSuperRole); return grantSubRoleToSuperRole(this, incomingSuperRole);
} }
/**
* Specifies in outgoing sub role which gets granted the role under definition.
*
* <p>Outgoing means an outgoing grant arrow in our grant-diagrams.
* Sub-role means which is granted to another role.
* Both means actually the same, just in different aspects.</p>
*
* @param entityAlias
* A previously declared entity alias name.
* @param role
* OWNER, ADMIN, ...
* @return
* The grant definition for further chained calls.
*/
public RbacGrantDefinition outgoingSubRole(final String entityAlias, final Role role) { public RbacGrantDefinition outgoingSubRole(final String entityAlias, final Role role) {
final var outgoingSubRole = findRbacRole(entityAlias, role); final var outgoingSubRole = findRbacRole(entityAlias, role);
return grantSubRoleToSuperRole(outgoingSubRole, this); return grantSubRoleToSuperRole(outgoingSubRole, this);
@ -802,6 +1037,26 @@ public class RbacView {
} }
} }
private static void generateRbacView(final Class<? extends HasUuid> c) {
final Method mainMethod = stream(c.getMethods()).filter(
m -> isStatic(m.getModifiers()) && m.getName().equals("main")
)
.findFirst()
.orElse(null);
if (mainMethod != null) {
try {
mainMethod.invoke(null, new Object[] { null });
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
} else {
System.err.println("WARNING: no main method in: " + c.getName() + " => no RBAC rules generated");
}
}
/**
* This main method generates the RbacViews (PostgreSQL+diagram) for all given entity classes.
*/
public static void main(String[] args) { public static void main(String[] args) {
Stream.of( Stream.of(
TestCustomerEntity.class, TestCustomerEntity.class,
@ -818,21 +1073,6 @@ public class RbacView {
HsOfficeSepaMandateEntity.class, HsOfficeSepaMandateEntity.class,
HsOfficeCoopSharesTransactionEntity.class, HsOfficeCoopSharesTransactionEntity.class,
HsOfficeMembershipEntity.class HsOfficeMembershipEntity.class
).forEach(c -> { ).forEach(RbacView::generateRbacView);
final Method mainMethod = stream(c.getMethods()).filter(
m -> isStatic(m.getModifiers()) && m.getName().equals("main")
)
.findFirst()
.orElse(null);
if (mainMethod != null) {
try {
mainMethod.invoke(null, new Object[] { null });
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
} else {
System.err.println("WARNING: no main method in: " + c.getName() + " => no RBAC rules generated");
}
});
} }
} }

View File

@ -4,7 +4,6 @@ import lombok.SneakyThrows;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import java.nio.file.*; import java.nio.file.*;
import java.time.LocalDateTime;
import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.joining;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacGrantDefinition.GrantType.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacGrantDefinition.GrantType.*;
@ -149,14 +148,13 @@ public class RbacViewMermaidFlowchartGenerator {
""" """
### rbac %{entityAlias} ### rbac %{entityAlias}
This code generated was by RbacViewMermaidFlowchartGenerator at %{timestamp}. This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually.
```mermaid ```mermaid
%{flowchart} %{flowchart}
``` ```
""" """
.replace("%{entityAlias}", rbacDef.getRootEntityAlias().aliasName()) .replace("%{entityAlias}", rbacDef.getRootEntityAlias().aliasName())
.replace("%{timestamp}", LocalDateTime.now().toString())
.replace("%{flowchart}", flowchart.toString()), .replace("%{flowchart}", flowchart.toString()),
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
System.out.println("Markdown-File: " + path.toAbsolutePath()); System.out.println("Markdown-File: " + path.toAbsolutePath());

View File

@ -5,7 +5,6 @@ import lombok.SneakyThrows;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.StandardOpenOption; import java.nio.file.StandardOpenOption;
import java.time.LocalDateTime;
import static net.hostsharing.hsadminng.rbac.rbacdef.PostgresTriggerReference.NEW; import static net.hostsharing.hsadminng.rbac.rbacdef.PostgresTriggerReference.NEW;
import static net.hostsharing.hsadminng.rbac.rbacdef.StringWriter.with; import static net.hostsharing.hsadminng.rbac.rbacdef.StringWriter.with;
@ -21,10 +20,9 @@ public class RbacViewPostgresGenerator {
liqibaseTagPrefix = rbacDef.getRootEntityAlias().getRawTableName().replace("_", "-"); liqibaseTagPrefix = rbacDef.getRootEntityAlias().getRawTableName().replace("_", "-");
plPgSql.writeLn(""" plPgSql.writeLn("""
--liquibase formatted sql --liquibase formatted sql
-- This code generated was by ${generator} at ${timestamp}. -- This code generated was by ${generator}, do not amend manually.
""", """,
with("generator", getClass().getSimpleName()), with("generator", getClass().getSimpleName()),
with("timestamp", LocalDateTime.now().toString()),
with("ref", NEW.name())); with("ref", NEW.name()));
new RbacObjectGenerator(rbacDef, liqibaseTagPrefix).generateTo(plPgSql); new RbacObjectGenerator(rbacDef, liqibaseTagPrefix).generateTo(plPgSql);

View File

@ -28,8 +28,8 @@ public class RawRbacGrantEntity implements Comparable {
@Column(name = "grantedbyroleidname", updatable = false, insertable = false) @Column(name = "grantedbyroleidname", updatable = false, insertable = false)
private String grantedByRoleIdName; private String grantedByRoleIdName;
@Column(name = "usergrantsbyroleuuid", updatable = false, insertable = false) @Column(name = "grantedbyroleuuid", updatable = false, insertable = false)
private UUID userGrantsByRoleUuid; private UUID grantedByRoleUuid;
@Column(name = "ascendantidname", updatable = false, insertable = false) @Column(name = "ascendantidname", updatable = false, insertable = false)
private String ascendantIdName; private String ascendantIdName;
@ -50,7 +50,7 @@ public class RawRbacGrantEntity implements Comparable {
// @formatter:off // @formatter:off
return "{ grant " + descendantIdName + return "{ grant " + descendantIdName +
" to " + ascendantIdName + " to " + ascendantIdName +
" by " + ( userGrantsByRoleUuid == null " by " + ( grantedByRoleUuid == null
? "system" ? "system"
: grantedByRoleIdName ) + : grantedByRoleIdName ) +
( assumed ? " and assume" : "") + ( assumed ? " and assume" : "") +

View File

@ -22,8 +22,8 @@ public class RbacGrantEntity {
@Column(name = "grantedbyroleidname", updatable = false, insertable = false) @Column(name = "grantedbyroleidname", updatable = false, insertable = false)
private String grantedByRoleIdName; private String grantedByRoleIdName;
@Column(name = "usergrantsbyroleuuid", updatable = false, insertable = false) @Column(name = "grantedbyroleuuid", updatable = false, insertable = false)
private UUID userGrantsByRoleUuid; private UUID grantedByRoleUuid;
@Column(name = "grantedroleidname", updatable = false, insertable = false) @Column(name = "grantedroleidname", updatable = false, insertable = false)
private String grantedRoleIdName; private String grantedRoleIdName;

View File

@ -14,6 +14,7 @@ import java.io.IOException;
import java.util.UUID; import java.util.UUID;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.fetchedBySql; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.fetchedBySql;
@ -52,7 +53,8 @@ public class TestDomainEntity implements HasUuid {
fetchedBySql(""" fetchedBySql("""
SELECT * FROM test_package p SELECT * FROM test_package p
WHERE p.uuid= ${ref}.packageUuid WHERE p.uuid= ${ref}.packageUuid
""")) """),
NOT_NULL)
.toRole("package", ADMIN).grantPermission(INSERT) .toRole("package", ADMIN).grantPermission(INSERT)
.createRole(OWNER, (with) -> { .createRole(OWNER, (with) -> {

View File

@ -14,6 +14,7 @@ import java.io.IOException;
import java.util.UUID; import java.util.UUID;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.*;
@ -53,7 +54,8 @@ public class TestPackageEntity implements HasUuid {
fetchedBySql(""" fetchedBySql("""
SELECT * FROM test_customer c SELECT * FROM test_customer c
WHERE c.uuid= ${ref}.customerUuid WHERE c.uuid= ${ref}.customerUuid
""")) """),
NOT_NULL)
.toRole("customer", ADMIN).grantPermission(INSERT) .toRole("customer", ADMIN).grantPermission(INSERT)
.createRole(OWNER, (with) -> { .createRole(OWNER, (with) -> {

View File

@ -160,6 +160,7 @@ create or replace function cleanIdentifier(rawIdentifier varchar)
declare declare
cleanIdentifier varchar; cleanIdentifier varchar;
begin begin
-- TODO: remove the ':' from the list of allowed characters as soon as it's not used anymore
cleanIdentifier := regexp_replace(rawIdentifier, '[^A-Za-z0-9\-._:]+', '', 'g'); cleanIdentifier := regexp_replace(rawIdentifier, '[^A-Za-z0-9\-._:]+', '', 'g');
return cleanIdentifier; return cleanIdentifier;
end; $$; end; $$;

View File

@ -300,7 +300,7 @@ create or replace function deleteRbacGrantsOfRbacRole()
strict as $$ strict as $$
begin begin
if TG_OP = 'DELETE' then if TG_OP = 'DELETE' then
delete from RbacGrants g where old.uuid in (g.userGrantsByRoleUuid, g.ascendantuuid, g.descendantuuid); delete from RbacGrants g where old.uuid in (g.grantedbyroleuuid, g.ascendantuuid, g.descendantuuid);
else else
raise exception 'invalid usage of TRIGGER BEFORE DELETE'; raise exception 'invalid usage of TRIGGER BEFORE DELETE';
end if; end if;
@ -519,12 +519,12 @@ create table RbacGrants
( (
uuid uuid primary key default uuid_generate_v4(), uuid uuid primary key default uuid_generate_v4(),
grantedByTriggerOf uuid references RbacObject (uuid) on delete cascade initially deferred , grantedByTriggerOf uuid references RbacObject (uuid) on delete cascade initially deferred ,
userGrantsByRoleUuid uuid references RbacRole (uuid), grantedByRoleUuid uuid references RbacRole (uuid),
ascendantUuid uuid references RbacReference (uuid), ascendantUuid uuid references RbacReference (uuid),
descendantUuid uuid references RbacReference (uuid), descendantUuid uuid references RbacReference (uuid),
assumed boolean not null default true, -- auto assumed (true) vs. needs assumeRoles (false) assumed boolean not null default true, -- auto assumed (true) vs. needs assumeRoles (false)
unique (ascendantUuid, descendantUuid), unique (ascendantUuid, descendantUuid),
constraint rbacGrant_createdBy check ( userGrantsByRoleUuid is null or grantedByTriggerOf is null) ); constraint rbacGrant_createdBy check ( grantedByRoleUuid is null or grantedByTriggerOf is null) );
create index on RbacGrants (ascendantUuid); create index on RbacGrants (ascendantUuid);
create index on RbacGrants (descendantUuid); create index on RbacGrants (descendantUuid);

View File

@ -20,52 +20,50 @@ begin
return currentSubjectsUuids[1]; return currentSubjectsUuids[1];
end; $$; end; $$;
create or replace procedure grantRoleToUserUnchecked(userGrantsByRoleUuid uuid, roleUuid uuid, userUuid uuid, doAssume boolean = true) create or replace procedure grantRoleToUserUnchecked(grantedByRoleUuid uuid, roleUuid uuid, userUuid uuid, doAssume boolean = true)
language plpgsql as $$ language plpgsql as $$
begin begin
perform assertReferenceType('grantingRoleUuid', userGrantsByRoleUuid, 'RbacRole'); perform assertReferenceType('grantingRoleUuid', grantedByRoleUuid, 'RbacRole');
perform assertReferenceType('roleId (descendant)', roleUuid, 'RbacRole'); perform assertReferenceType('roleId (descendant)', roleUuid, 'RbacRole');
perform assertReferenceType('userId (ascendant)', userUuid, 'RbacUser'); perform assertReferenceType('userId (ascendant)', userUuid, 'RbacUser');
raise notice 'role % grants role % to user %, assumed=%', userGrantsByRoleUuid, roleUuid, userUuid, doAssume;
insert insert
into RbacGrants (userGrantsByRoleUuid, ascendantUuid, descendantUuid, assumed) into RbacGrants (grantedByRoleUuid, ascendantUuid, descendantUuid, assumed)
values (userGrantsByRoleUuid, userUuid, roleUuid, doAssume); values (grantedByRoleUuid, userUuid, roleUuid, doAssume);
-- TODO.spec: What should happen on multiple grants? What if options (doAssume) are not the same? -- TODO.spec: What should happen on multiple grants? What if options (doAssume) are not the same?
-- Most powerful or latest grant wins? What about managed? -- Most powerful or latest grant wins? What about managed?
-- on conflict do nothing; -- allow granting multiple times -- on conflict do nothing; -- allow granting multiple times
end; $$; end; $$;
create or replace procedure grantRoleToUser(userGrantsByRoleUuid uuid, grantedRoleUuid uuid, userUuid uuid, doAssume boolean = true) create or replace procedure grantRoleToUser(grantedByRoleUuid uuid, grantedRoleUuid uuid, userUuid uuid, doAssume boolean = true)
language plpgsql as $$ language plpgsql as $$
declare declare
grantedByRoleIdName text; grantedByRoleIdName text;
grantedRoleIdName text; grantedRoleIdName text;
begin begin
perform assertReferenceType('grantingRoleUuid', userGrantsByRoleUuid, 'RbacRole'); perform assertReferenceType('grantingRoleUuid', grantedByRoleUuid, 'RbacRole');
perform assertReferenceType('grantedRoleUuid (descendant)', grantedRoleUuid, 'RbacRole'); perform assertReferenceType('grantedRoleUuid (descendant)', grantedRoleUuid, 'RbacRole');
perform assertReferenceType('userUuid (ascendant)', userUuid, 'RbacUser'); perform assertReferenceType('userUuid (ascendant)', userUuid, 'RbacUser');
assert userGrantsByRoleUuid is not null, 'userGrantsByRoleUuid must not be null'; assert grantedByRoleUuid is not null, 'grantedByRoleUuid must not be null';
assert grantedRoleUuid is not null, 'grantedRoleUuid must not be null'; assert grantedRoleUuid is not null, 'grantedRoleUuid must not be null';
assert userUuid is not null, 'userUuid must not be null'; assert userUuid is not null, 'userUuid must not be null';
if NOT isGranted(currentSubjectsUuids(), userGrantsByRoleUuid) then if NOT isGranted(currentSubjectsUuids(), grantedByRoleUuid) then
select roleIdName from rbacRole_ev where uuid=userGrantsByRoleUuid into grantedByRoleIdName; select roleIdName from rbacRole_ev where uuid=grantedByRoleUuid into grantedByRoleIdName;
raise exception '[403] Access to granted-by-role % (%) forbidden for % (%)', raise exception '[403] Access to granted-by-role % (%) forbidden for % (%)',
grantedByRoleIdName, userGrantsByRoleUuid, currentSubjects(), currentSubjectsUuids(); grantedByRoleIdName, grantedByRoleUuid, currentSubjects(), currentSubjectsUuids();
end if; end if;
if NOT isGranted(userGrantsByRoleUuid, grantedRoleUuid) then if NOT isGranted(grantedByRoleUuid, grantedRoleUuid) then
select roleIdName from rbacRole_ev where uuid=userGrantsByRoleUuid into grantedByRoleIdName; select roleIdName from rbacRole_ev where uuid=grantedByRoleUuid into grantedByRoleIdName;
select roleIdName from rbacRole_ev where uuid=grantedRoleUuid into grantedRoleIdName; select roleIdName from rbacRole_ev where uuid=grantedRoleUuid into grantedRoleIdName;
raise exception '[403] Access to granted role % (%) forbidden for % (%)', raise exception '[403] Access to granted role % (%) forbidden for % (%)',
grantedRoleIdName, grantedRoleUuid, grantedByRoleIdName, userGrantsByRoleUuid; grantedRoleIdName, grantedRoleUuid, grantedByRoleIdName, grantedByRoleUuid;
end if; end if;
insert insert
into RbacGrants (userGrantsByRoleUuid, ascendantUuid, descendantUuid, assumed) into RbacGrants (grantedByRoleUuid, ascendantUuid, descendantUuid, assumed)
values (userGrantsByRoleUuid, userUuid, grantedRoleUuid, doAssume); values (grantedByRoleUuid, userUuid, grantedRoleUuid, doAssume);
-- TODO.spec: What should happen on mupltiple grants? What if options (doAssume) are not the same? -- TODO.spec: What should happen on mupltiple grants? What if options (doAssume) are not the same?
-- Most powerful or latest grant wins? What about managed? -- Most powerful or latest grant wins? What about managed?
-- on conflict do nothing; -- allow granting multiple times -- on conflict do nothing; -- allow granting multiple times
@ -77,40 +75,40 @@ end; $$;
--changeset rbac-user-grant-REVOKE-ROLE-FROM-USER:1 endDelimiter:--// --changeset rbac-user-grant-REVOKE-ROLE-FROM-USER:1 endDelimiter:--//
-- ---------------------------------------------------------------------------- -- ----------------------------------------------------------------------------
create or replace procedure checkRevokeRoleFromUserPreconditions(userGrantsByRoleUuid uuid, grantedRoleUuid uuid, userUuid uuid) create or replace procedure checkRevokeRoleFromUserPreconditions(grantedByRoleUuid uuid, grantedRoleUuid uuid, userUuid uuid)
language plpgsql as $$ language plpgsql as $$
begin begin
perform assertReferenceType('userGrantsByRoleUuid', userGrantsByRoleUuid, 'RbacRole'); perform assertReferenceType('grantedByRoleUuid', grantedByRoleUuid, 'RbacRole');
perform assertReferenceType('grantedRoleUuid (descendant)', grantedRoleUuid, 'RbacRole'); perform assertReferenceType('grantedRoleUuid (descendant)', grantedRoleUuid, 'RbacRole');
perform assertReferenceType('userUuid (ascendant)', userUuid, 'RbacUser'); perform assertReferenceType('userUuid (ascendant)', userUuid, 'RbacUser');
if NOT isGranted(currentSubjectsUuids(), userGrantsByRoleUuid) then if NOT isGranted(currentSubjectsUuids(), grantedByRoleUuid) then
raise exception '[403] Revoking role created by % is forbidden for %.', userGrantsByRoleUuid, currentSubjects(); raise exception '[403] Revoking role created by % is forbidden for %.', grantedByRoleUuid, currentSubjects();
end if; end if;
if NOT isGranted(userGrantsByRoleUuid, grantedRoleUuid) then if NOT isGranted(grantedByRoleUuid, grantedRoleUuid) then
raise exception '[403] Revoking role % is forbidden for %.', grantedRoleUuid, currentSubjects(); raise exception '[403] Revoking role % is forbidden for %.', grantedRoleUuid, currentSubjects();
end if; end if;
--raise exception 'isGranted(%, %)', currentSubjectsUuids(), userGrantsByRoleUuid; --raise exception 'isGranted(%, %)', currentSubjectsUuids(), grantedByRoleUuid;
if NOT isGranted(currentSubjectsUuids(), userGrantsByRoleUuid) then if NOT isGranted(currentSubjectsUuids(), grantedByRoleUuid) then
raise exception '[403] Revoking role granted by % is forbidden for %.', userGrantsByRoleUuid, currentSubjects(); raise exception '[403] Revoking role granted by % is forbidden for %.', grantedByRoleUuid, currentSubjects();
end if; end if;
if NOT isGranted(userUuid, grantedRoleUuid) then if NOT isGranted(userUuid, grantedRoleUuid) then
raise exception '[404] No such grant found granted by % for user % to role %.', userGrantsByRoleUuid, userUuid, grantedRoleUuid; raise exception '[404] No such grant found granted by % for user % to role %.', grantedByRoleUuid, userUuid, grantedRoleUuid;
end if; end if;
end; $$; end; $$;
create or replace procedure revokeRoleFromUser(userGrantsByRoleUuid uuid, grantedRoleUuid uuid, userUuid uuid) create or replace procedure revokeRoleFromUser(grantedByRoleUuid uuid, grantedRoleUuid uuid, userUuid uuid)
language plpgsql as $$ language plpgsql as $$
begin begin
call checkRevokeRoleFromUserPreconditions(userGrantsByRoleUuid, grantedRoleUuid, userUuid); call checkRevokeRoleFromUserPreconditions(grantedByRoleUuid, grantedRoleUuid, userUuid);
raise INFO 'delete from RbacGrants where ascendantUuid = % and descendantUuid = %', userUuid, grantedRoleUuid; raise INFO 'delete from RbacGrants where ascendantUuid = % and descendantUuid = %', userUuid, grantedRoleUuid;
delete from RbacGrants as g delete from RbacGrants as g
where g.ascendantUuid = userUuid and g.descendantUuid = grantedRoleUuid where g.ascendantUuid = userUuid and g.descendantUuid = grantedRoleUuid
and g.userGrantsByRoleUuid = revokeRoleFromUser.userGrantsByRoleUuid; and g.grantedByRoleUuid = revokeRoleFromUser.grantedByRoleUuid;
end; $$; end; $$;
--// --//

View File

@ -139,7 +139,6 @@ begin
raise exception '[401] currentUserUuid cannot be determined, please call `defineContext(...)` first;"'; raise exception '[401] currentUserUuid cannot be determined, please call `defineContext(...)` first;"';
end if; end if;
end if; end if;
raise notice 'currentUserUuid %', currentUserUuid;
return currentUserUuid::uuid; return currentUserUuid::uuid;
end; $$; end; $$;
--// --//

View File

@ -60,14 +60,14 @@ create or replace view rbacgrants_ev as
go.objectTable || '#' || findIdNameByObjectUuid(go.objectTable, go.uuid) || '.' || r.roletype as grantedByRoleIdName, go.objectTable || '#' || findIdNameByObjectUuid(go.objectTable, go.uuid) || '.' || r.roletype as grantedByRoleIdName,
x.ascendingIdName as ascendantIdName, x.ascendingIdName as ascendantIdName,
x.descendingIdName as descendantIdName, x.descendingIdName as descendantIdName,
x.userGrantsByRoleUuid, x.grantedByRoleUuid,
x.ascendantUuid as ascendantUuid, x.ascendantUuid as ascendantUuid,
x.descendantUuid as descendantUuid, x.descendantUuid as descendantUuid,
x.assumed x.assumed
from ( from (
select g.uuid as grantUuid, select g.uuid as grantUuid,
g.grantedbytriggerof as grantedbytriggerof, g.grantedbytriggerof as grantedbytriggerof,
g.userGrantsByRoleUuid, g.ascendantuuid, g.descendantuuid, g.assumed, g.grantedbyroleuuid, g.ascendantuuid, g.descendantuuid, g.assumed,
coalesce( coalesce(
'user ' || au.name, 'user ' || au.name,
@ -91,7 +91,7 @@ create or replace view rbacgrants_ev as
left outer join rbacpermission dp on dp.uuid = g.descendantUuid left outer join rbacpermission dp on dp.uuid = g.descendantUuid
left outer join rbacobject as dpo on dpo.uuid = dp.objectUuid left outer join rbacobject as dpo on dpo.uuid = dp.objectUuid
) as x ) as x
left outer join rbacrole as r on r.uuid = userGrantsByRoleUuid left outer join rbacrole as r on r.uuid = grantedByRoleUuid
left outer join rbacuser u on u.uuid = x.ascendantuuid left outer join rbacuser u on u.uuid = x.ascendantuuid
left outer join rbacobject go on go.uuid = r.objectuuid left outer join rbacobject go on go.uuid = r.objectuuid
@ -112,10 +112,10 @@ create or replace view rbacgrants_rv as
-- @formatter:off -- @formatter:off
select o.objectTable || '#' || findIdNameByObjectUuid(o.objectTable, o.uuid) || '.' || r.roletype as grantedByRoleIdName, select o.objectTable || '#' || findIdNameByObjectUuid(o.objectTable, o.uuid) || '.' || r.roletype as grantedByRoleIdName,
g.objectTable || '#' || g.objectIdName || '.' || g.roletype as grantedRoleIdName, g.userName, g.assumed, g.objectTable || '#' || g.objectIdName || '.' || g.roletype as grantedRoleIdName, g.userName, g.assumed,
g.userGrantsByRoleUuid, g.descendantUuid as grantedRoleUuid, g.ascendantUuid as userUuid, g.grantedByRoleUuid, g.descendantUuid as grantedRoleUuid, g.ascendantUuid as userUuid,
g.objectTable, g.objectUuid, g.objectIdName, g.roleType as grantedRoleType g.objectTable, g.objectUuid, g.objectIdName, g.roleType as grantedRoleType
from ( from (
select g.userGrantsByRoleUuid, g.ascendantuuid, g.descendantuuid, g.assumed, select g.grantedbyroleuuid, g.ascendantuuid, g.descendantuuid, g.assumed,
u.name as userName, o.objecttable, r.objectuuid, r.roletype, u.name as userName, o.objecttable, r.objectuuid, r.roletype,
findIdNameByObjectUuid(o.objectTable, o.uuid) as objectIdName findIdNameByObjectUuid(o.objectTable, o.uuid) as objectIdName
from rbacgrants as g from rbacgrants as g
@ -124,7 +124,7 @@ select o.objectTable || '#' || findIdNameByObjectUuid(o.objectTable, o.uuid) ||
left outer join rbacuser u on u.uuid = g.ascendantuuid left outer join rbacuser u on u.uuid = g.ascendantuuid
where isGranted(currentSubjectsUuids(), r.uuid) where isGranted(currentSubjectsUuids(), r.uuid)
) as g ) as g
join RbacRole as r on r.uuid = userGrantsByRoleUuid join RbacRole as r on r.uuid = grantedByRoleUuid
join RbacObject as o on o.uuid = r.objectUuid join RbacObject as o on o.uuid = r.objectUuid
order by grantedRoleIdName; order by grantedRoleIdName;
-- @formatter:on -- @formatter:on
@ -177,7 +177,7 @@ create or replace function deleteRbacGrant()
returns trigger returns trigger
language plpgsql as $$ language plpgsql as $$
begin begin
call revokeRoleFromUser(old.userGrantsByRoleUuid, old.grantedRoleUuid, old.userUuid); call revokeRoleFromUser(old.grantedByRoleUuid, old.grantedRoleUuid, old.userUuid);
return old; return old;
end; $$; end; $$;

View File

@ -1,6 +1,6 @@
### rbac customer ### rbac customer
This code generated was by RbacViewMermaidFlowchartGenerator at 2024-03-22T14:44:19.425403022. This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually.
```mermaid ```mermaid
%%{init:{'flowchart':{'htmlLabels':false}}}%% %%{init:{'flowchart':{'htmlLabels':false}}}%%
@ -21,6 +21,7 @@ subgraph customer["`**customer**`"]
subgraph customer:permissions[ ] subgraph customer:permissions[ ]
style customer:permissions fill:#dd4901,stroke:white style customer:permissions fill:#dd4901,stroke:white
perm:customer:INSERT{{customer:INSERT}}
perm:customer:DELETE{{customer:DELETE}} perm:customer:DELETE{{customer:DELETE}}
perm:customer:UPDATE{{customer:UPDATE}} perm:customer:UPDATE{{customer:UPDATE}}
perm:customer:SELECT{{customer:SELECT}} perm:customer:SELECT{{customer:SELECT}}
@ -36,6 +37,7 @@ role:customer:owner ==> role:customer:admin
role:customer:admin ==> role:customer:tenant role:customer:admin ==> role:customer:tenant
%% granting permissions to roles %% granting permissions to roles
role:global:admin ==> perm:customer:INSERT
role:customer:owner ==> perm:customer:DELETE role:customer:owner ==> perm:customer:DELETE
role:customer:admin ==> perm:customer:UPDATE role:customer:admin ==> perm:customer:UPDATE
role:customer:tenant ==> perm:customer:SELECT role:customer:tenant ==> perm:customer:SELECT

View File

@ -1,5 +1,5 @@
--liquibase formatted sql --liquibase formatted sql
-- This code generated was by RbacViewPostgresGenerator at 2024-03-22T14:44:19.441879428. -- This code generated was by RbacViewPostgresGenerator, do not amend manually.
-- ============================================================================ -- ============================================================================
@ -80,6 +80,46 @@ execute procedure insertTriggerForTestCustomer_tf();
--changeset test-customer-rbac-INSERT:1 endDelimiter:--// --changeset test-customer-rbac-INSERT:1 endDelimiter:--//
-- ---------------------------------------------------------------------------- -- ----------------------------------------------------------------------------
/*
Creates INSERT INTO test_customer permissions for the related global rows.
*/
do language plpgsql $$
declare
row global;
permissionUuid uuid;
roleUuid uuid;
begin
call defineContext('create INSERT INTO test_customer permissions for the related global rows');
FOR row IN SELECT * FROM global
LOOP
roleUuid := findRoleId(globalAdmin());
permissionUuid := createPermission(row.uuid, 'INSERT', 'test_customer');
call grantPermissionToRole(permissionUuid, roleUuid);
END LOOP;
END;
$$;
/**
Adds test_customer INSERT permission to specified role of new global rows.
*/
create or replace function test_customer_global_insert_tf()
returns trigger
language plpgsql
strict as $$
begin
call grantPermissionToRole(
createPermission(NEW.uuid, 'INSERT', 'test_customer'),
globalAdmin());
return NEW;
end; $$;
-- z_... is to put it at the end of after insert triggers, to make sure the roles exist
create trigger z_test_customer_global_insert_tg
after insert on global
for each row
execute procedure test_customer_global_insert_tf();
/** /**
Checks if the user or assumed roles are allowed to insert a row to test_customer, Checks if the user or assumed roles are allowed to insert a row to test_customer,
where only global-admin has that permission. where only global-admin has that permission.

View File

@ -1,6 +1,6 @@
### rbac package ### rbac package
This code generated was by RbacViewMermaidFlowchartGenerator at 2024-03-22T14:44:19.484173294. This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually.
```mermaid ```mermaid
%%{init:{'flowchart':{'htmlLabels':false}}}%% %%{init:{'flowchart':{'htmlLabels':false}}}%%

View File

@ -1,5 +1,5 @@
--liquibase formatted sql --liquibase formatted sql
-- This code generated was by RbacViewPostgresGenerator at 2024-03-22T12:01:44.554331877. -- This code generated was by RbacViewPostgresGenerator, do not amend manually.
-- ============================================================================ -- ============================================================================
@ -195,32 +195,22 @@ execute procedure test_package_test_customer_insert_tf();
/** /**
Checks if the user or assumed roles are allowed to insert a row to test_package, Checks if the user or assumed roles are allowed to insert a row to test_package,
where the check is performed by an indirect role. where the check is performed by a direct role.
An indirect role is a role FIXME. A direct role is a role depending on a foreign key directly available in the NEW row.
*/ */
create or replace function test_package_insert_permission_missing_tf() create or replace function test_package_insert_permission_missing_tf()
returns trigger returns trigger
language plpgsql as $$ language plpgsql as $$
begin begin
if ( not hasInsertPermission( raise exception '[403] insert into test_package not allowed for current subjects % (%)',
( 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();
--// --//

View File

@ -1,6 +1,6 @@
### rbac domain ### rbac domain
This code generated was by RbacViewMermaidFlowchartGenerator at 2024-03-22T14:44:19.510830235. This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually.
```mermaid ```mermaid
%%{init:{'flowchart':{'htmlLabels':false}}}%% %%{init:{'flowchart':{'htmlLabels':false}}}%%

View File

@ -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, do not amend manually.
-- ============================================================================ -- ============================================================================
--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:--//
-- ---------------------------------------------------------------------------- -- ----------------------------------------------------------------------------
@ -178,13 +186,17 @@ begin
return NEW; return NEW;
end; $$; end; $$;
-- 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 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();
/** /**
Checks if the user or assumed roles are allowed to insert a row to test_domain. Checks if the user or assumed roles are allowed to insert a row to test_domain,
where the check is performed by a direct role.
A direct role is a role depending on a foreign key directly available in the NEW row.
*/ */
create or replace function test_domain_insert_permission_missing_tf() create or replace function test_domain_insert_permission_missing_tf()
returns trigger returns trigger
@ -199,8 +211,8 @@ create trigger test_domain_insert_permission_check_tg
for each row for each row
when ( not hasInsertPermission(NEW.packageUuid, 'INSERT', 'test_domain') ) 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 +220,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 +236,3 @@ call generateRbacRestrictedView('test_domain',
$updates$); $updates$);
--// --//

View File

@ -1,6 +1,6 @@
### rbac contact ### rbac contact
This code generated was by RbacViewMermaidFlowchartGenerator at 2024-03-14T09:00:15.762621659. This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually.
```mermaid ```mermaid
%%{init:{'flowchart':{'htmlLabels':false}}}%% %%{init:{'flowchart':{'htmlLabels':false}}}%%

View File

@ -1,5 +1,5 @@
--liquibase formatted sql --liquibase formatted sql
-- This code generated was by RbacViewPostgresGenerator at 2024-03-14T09:00:15.769718298. -- This code generated was by RbacViewPostgresGenerator, do not amend manually.
-- ============================================================================ -- ============================================================================
@ -109,26 +109,16 @@ create or replace function hs_office_contact_global_insert_tf()
strict as $$ strict as $$
begin begin
call grantPermissionToRole( call grantPermissionToRole(
globalGuest(), createPermission(NEW.uuid, 'INSERT', 'hs_office_contact'),
createPermission(NEW.uuid, 'INSERT', 'hs_office_contact')); globalGuest());
return NEW; return NEW;
end; $$; end; $$;
-- z_... is to put it at the end of after insert triggers, to make sure the roles exist
create trigger z_hs_office_contact_global_insert_tg create trigger z_hs_office_contact_global_insert_tg
after insert on global after insert on global
for each row for each row
execute procedure hs_office_contact_global_insert_tf(); execute procedure hs_office_contact_global_insert_tf();
/**
Checks if the user or assumed roles are allowed to insert a row to hs_office_contact.
*/
create or replace function hs_office_contact_insert_permission_missing_tf()
returns trigger
language plpgsql as $$
begin
raise exception '[403] insert into hs_office_contact not allowed for current subjects % (%)',
currentSubjects(), currentSubjectsUuids();
end; $$;
--// --//
-- ============================================================================ -- ============================================================================

View File

@ -1,6 +1,6 @@
### rbac person ### rbac person
This code generated was by RbacViewMermaidFlowchartGenerator at 2024-03-18T13:35:44.716916229. This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually.
```mermaid ```mermaid
%%{init:{'flowchart':{'htmlLabels':false}}}%% %%{init:{'flowchart':{'htmlLabels':false}}}%%

View File

@ -1,5 +1,5 @@
--liquibase formatted sql --liquibase formatted sql
-- This code generated was by RbacViewPostgresGenerator at 2024-03-18T13:35:44.726508114. -- This code generated was by RbacViewPostgresGenerator, do not amend manually.
-- ============================================================================ -- ============================================================================
@ -142,15 +142,6 @@ call generateRbacRestrictedView('hs_office_person',
tradeName = new.tradeName, tradeName = new.tradeName,
givenName = new.givenName, givenName = new.givenName,
familyName = new.familyName familyName = new.familyName
$updates$ $updates$);
,
$columns$
uuid,
personType,
tradeName,
givenName,
familyName
$columns$
);
--// --//

View File

@ -1,6 +1,6 @@
### rbac relation ### rbac relation
This code generated was by RbacViewMermaidFlowchartGenerator at 2024-03-15T17:17:00.854621634. This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually.
```mermaid ```mermaid
%%{init:{'flowchart':{'htmlLabels':false}}}%% %%{init:{'flowchart':{'htmlLabels':false}}}%%

View File

@ -1,5 +1,5 @@
--liquibase formatted sql --liquibase formatted sql
-- This code generated was by RbacViewPostgresGenerator at 2024-03-15T17:17:00.864301165. -- This code generated was by RbacViewPostgresGenerator, do not amend manually.
-- ============================================================================ -- ============================================================================
@ -65,21 +65,21 @@ begin
perform createRoleWithGrants( perform createRoleWithGrants(
hsOfficeRelationAgent(NEW), hsOfficeRelationAgent(NEW),
incomingSuperRoles => array[ incomingSuperRoles => array[
hsOfficePersonAdmin(newHolderPerson), hsOfficeRelationAdmin(NEW),
hsOfficeRelationAdmin(NEW)] hsOfficePersonAdmin(newHolderPerson)]
); );
perform createRoleWithGrants( perform createRoleWithGrants(
hsOfficeRelationTenant(NEW), hsOfficeRelationTenant(NEW),
permissions => array['SELECT'], permissions => array['SELECT'],
incomingSuperRoles => array[ incomingSuperRoles => array[
hsOfficeRelationAgent(NEW), hsOfficePersonAdmin(newHolderPerson),
hsOfficeContactAdmin(newContact), hsOfficeContactAdmin(newContact),
hsOfficePersonAdmin(newHolderPerson)], hsOfficeRelationAgent(NEW)],
outgoingSubRoles => array[ outgoingSubRoles => array[
hsOfficeContactReferrer(newContact),
hsOfficePersonReferrer(newHolderPerson), hsOfficePersonReferrer(newHolderPerson),
hsOfficePersonReferrer(newAnchorPerson)] hsOfficePersonReferrer(newAnchorPerson),
hsOfficeContactReferrer(newContact)]
); );
call leaveTriggerForObjectUuid(NEW.uuid); call leaveTriggerForObjectUuid(NEW.uuid);
@ -220,34 +220,30 @@ begin
return NEW; return NEW;
end; $$; end; $$;
-- z_... is to put it at the end of after insert triggers, to make sure the roles exist
create trigger z_hs_office_relation_hs_office_person_insert_tg create trigger z_hs_office_relation_hs_office_person_insert_tg
after insert on hs_office_person after insert on hs_office_person
for each row for each row
execute procedure hs_office_relation_hs_office_person_insert_tf(); execute procedure hs_office_relation_hs_office_person_insert_tf();
/** /**
Checks if the user or assumed roles are allowed to insert a row to hs_office_relation. Checks if the user or assumed roles are allowed to insert a row to hs_office_relation,
where the check is performed by a direct role.
A direct role is a role depending on a foreign key directly available in the NEW row.
*/ */
create or replace function hs_office_relation_insert_permission_missing_tf() create or replace function hs_office_relation_insert_permission_missing_tf()
returns trigger returns trigger
language plpgsql as $$ language plpgsql as $$
begin begin
if ( not hasInsertPermission( raise exception '[403] insert into hs_office_relation not allowed for current subjects % (%)',
( SELECT anchorPerson.uuid FROM
(select * from hs_office_person as p where p.uuid = NEW.anchorUuid) AS anchorPerson
), 'INSERT', 'hs_office_relation') ) then
raise exception
'[403] insert into hs_office_relation not allowed for current subjects % (%)',
currentSubjects(), currentSubjectsUuids(); currentSubjects(), currentSubjectsUuids();
end if;
return NEW;
end; $$; end; $$;
create trigger hs_office_relation_insert_permission_check_tg create trigger hs_office_relation_insert_permission_check_tg
before insert on hs_office_relation before insert on hs_office_relation
for each row for each row
when ( not hasInsertPermission(NEW.anchorUuid, 'INSERT', 'hs_office_relation') )
execute procedure hs_office_relation_insert_permission_missing_tf(); execute procedure hs_office_relation_insert_permission_missing_tf();
--// --//

View File

@ -1,6 +1,6 @@
### rbac partner ### rbac partner
This code generated was by RbacViewMermaidFlowchartGenerator at 2024-03-18T09:31:47.361311186. This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually.
```mermaid ```mermaid
%%{init:{'flowchart':{'htmlLabels':false}}}%% %%{init:{'flowchart':{'htmlLabels':false}}}%%

View File

@ -1,5 +1,5 @@
--liquibase formatted sql --liquibase formatted sql
-- This code generated was by RbacViewPostgresGenerator at 2024-03-18T09:31:47.368892199. -- This code generated was by RbacViewPostgresGenerator, do not amend manually.
-- ============================================================================ -- ============================================================================
@ -131,36 +131,6 @@ begin
end if; end if;
call leaveTriggerForObjectUuid(NEW.uuid); call leaveTriggerForObjectUuid(NEW.uuid);
-- raise exception 'RBAC updated from rel % to %', OLD.partnerReluuid, NEW.partnerReluuid;
end; $$;
create or replace procedure updateRbacRulesForHsOfficePartnerX(
OLD hs_office_partner,
NEW hs_office_partner
)
language plpgsql as $$
declare
partnerRel hs_office_relation;
grantCount int;
begin
assert OLD.uuid = NEW.uuid, 'uuid did change, but should not';
assert OLD.partnerReluuid <> NEW.partnerReluuid, 'partnerReluuid did not change, but should have';
delete from rbacgrants where grantedbytriggerof = OLD.uuid;
select count(*) from rbacgrants where grantedbytriggerof=NEW.uuid into grantCount;
assert grantCount=0, format('unexpected grantCount>0: %d', grantCount);
call buildRbacSystemForHsOfficePartner(NEW);
select * from hs_office_relation where uuid=NEW.partnerReluuid into partnerRel;
call grantPermissionToRole(createPermission(NEW.uuid, 'SELECT'), hsOfficeRelationTenant(partnerRel));
select count(*) from rbacgrants where grantedbytriggerof=NEW.uuid into grantCount;
assert grantCount>0, format('unexpected grantCount=0: %d', grantCount);
raise warning 'WARNING grantCount=%', grantCount;
end; $$; end; $$;
/* /*
@ -172,7 +142,7 @@ create or replace function updateTriggerForHsOfficePartner_tf()
language plpgsql language plpgsql
strict as $$ strict as $$
begin begin
call updateRbacRulesForHsOfficePartnerX(OLD, NEW); call updateRbacRulesForHsOfficePartner(OLD, NEW);
return NEW; return NEW;
end; $$; end; $$;
@ -228,7 +198,8 @@ create trigger z_hs_office_partner_global_insert_tg
execute procedure hs_office_partner_global_insert_tf(); execute procedure hs_office_partner_global_insert_tf();
/** /**
Checks if the user or assumed roles are allowed to insert a row to hs_office_partner. Checks if the user or assumed roles are allowed to insert a row to hs_office_partner,
where only global-admin has that permission.
*/ */
create or replace function hs_office_partner_insert_permission_missing_tf() create or replace function hs_office_partner_insert_permission_missing_tf()
returns trigger returns trigger

View File

@ -1,6 +1,6 @@
### rbac partnerDetails ### rbac partnerDetails
This code generated was by RbacViewMermaidFlowchartGenerator at 2024-03-16T12:04:37.309540020. This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually.
```mermaid ```mermaid
%%{init:{'flowchart':{'htmlLabels':false}}}%% %%{init:{'flowchart':{'htmlLabels':false}}}%%

View File

@ -1,5 +1,5 @@
--liquibase formatted sql --liquibase formatted sql
-- This code generated was by RbacViewPostgresGenerator at 2024-03-16T12:04:37.319601283. -- This code generated was by RbacViewPostgresGenerator, do not amend manually.
-- ============================================================================ -- ============================================================================
@ -102,7 +102,8 @@ create trigger z_hs_office_partner_details_global_insert_tg
execute procedure hs_office_partner_details_global_insert_tf(); execute procedure hs_office_partner_details_global_insert_tf();
/** /**
Checks if the user or assumed roles are allowed to insert a row to hs_office_partner_details. Checks if the user or assumed roles are allowed to insert a row to hs_office_partner_details,
where only global-admin has that permission.
*/ */
create or replace function hs_office_partner_details_insert_permission_missing_tf() create or replace function hs_office_partner_details_insert_permission_missing_tf()
returns trigger returns trigger

View File

@ -1,6 +1,6 @@
### rbac bankAccount ### rbac bankAccount
This code generated was by RbacViewMermaidFlowchartGenerator at 2024-03-14T08:55:11.118624882. This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually.
```mermaid ```mermaid
%%{init:{'flowchart':{'htmlLabels':false}}}%% %%{init:{'flowchart':{'htmlLabels':false}}}%%

View File

@ -1,5 +1,5 @@
--liquibase formatted sql --liquibase formatted sql
-- This code generated was by RbacViewPostgresGenerator at 2024-03-14T08:55:11.127959896. -- This code generated was by RbacViewPostgresGenerator, do not amend manually.
-- ============================================================================ -- ============================================================================
@ -109,26 +109,16 @@ create or replace function hs_office_bankaccount_global_insert_tf()
strict as $$ strict as $$
begin begin
call grantPermissionToRole( call grantPermissionToRole(
globalGuest(), createPermission(NEW.uuid, 'INSERT', 'hs_office_bankaccount'),
createPermission(NEW.uuid, 'INSERT', 'hs_office_bankaccount')); globalGuest());
return NEW; return NEW;
end; $$; end; $$;
-- z_... is to put it at the end of after insert triggers, to make sure the roles exist
create trigger z_hs_office_bankaccount_global_insert_tg create trigger z_hs_office_bankaccount_global_insert_tg
after insert on global after insert on global
for each row for each row
execute procedure hs_office_bankaccount_global_insert_tf(); execute procedure hs_office_bankaccount_global_insert_tf();
/**
Checks if the user or assumed roles are allowed to insert a row to hs_office_bankaccount.
*/
create or replace function hs_office_bankaccount_insert_permission_missing_tf()
returns trigger
language plpgsql as $$
begin
raise exception '[403] insert into hs_office_bankaccount not allowed for current subjects % (%)',
currentSubjects(), currentSubjectsUuids();
end; $$;
--// --//
-- ============================================================================ -- ============================================================================

View File

@ -1,6 +1,6 @@
### rbac sepaMandate ### rbac sepaMandate
This code generated was by RbacViewMermaidFlowchartGenerator at 2024-03-18T16:07:14.011240343. This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually.
```mermaid ```mermaid
%%{init:{'flowchart':{'htmlLabels':false}}}%% %%{init:{'flowchart':{'htmlLabels':false}}}%%

View File

@ -1,5 +1,5 @@
--liquibase formatted sql --liquibase formatted sql
-- This code generated was by RbacViewPostgresGenerator at 2024-03-18T16:07:14.019894954. -- This code generated was by RbacViewPostgresGenerator, do not amend manually.
-- ============================================================================ -- ============================================================================
@ -64,8 +64,8 @@ begin
hsOfficeSepaMandateAgent(NEW), hsOfficeSepaMandateAgent(NEW),
incomingSuperRoles => array[hsOfficeSepaMandateAdmin(NEW)], incomingSuperRoles => array[hsOfficeSepaMandateAdmin(NEW)],
outgoingSubRoles => array[ outgoingSubRoles => array[
hsOfficeRelationAgent(newDebitorRel), hsOfficeBankAccountReferrer(newBankAccount),
hsOfficeBankAccountReferrer(newBankAccount)] hsOfficeRelationAgent(newDebitorRel)]
); );
perform createRoleWithGrants( perform createRoleWithGrants(
@ -146,7 +146,10 @@ create trigger z_hs_office_sepamandate_hs_office_relation_insert_tg
execute procedure hs_office_sepamandate_hs_office_relation_insert_tf(); execute procedure hs_office_sepamandate_hs_office_relation_insert_tf();
/** /**
Checks if the user or assumed roles are allowed to insert a row to hs_office_sepamandate. Checks if the user or assumed roles are allowed to insert a row to hs_office_sepamandate,
where the check is performed by an indirect role.
An indirect role is a role FIXME.
*/ */
create or replace function hs_office_sepamandate_insert_permission_missing_tf() create or replace function hs_office_sepamandate_insert_permission_missing_tf()
returns trigger returns trigger

View File

@ -1,6 +1,6 @@
### rbac debitor ### rbac debitor
This code generated was by RbacViewMermaidFlowchartGenerator at 2024-03-16T13:52:18.484919583. This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually.
```mermaid ```mermaid
%%{init:{'flowchart':{'htmlLabels':false}}}%% %%{init:{'flowchart':{'htmlLabels':false}}}%%

View File

@ -1,5 +1,5 @@
--liquibase formatted sql --liquibase formatted sql
-- This code generated was by RbacViewPostgresGenerator at 2024-03-20T13:55:16.722860098. -- This code generated was by RbacViewPostgresGenerator, do not amend manually.
-- ============================================================================ -- ============================================================================
@ -177,7 +177,8 @@ create trigger z_hs_office_debitor_global_insert_tg
execute procedure hs_office_debitor_global_insert_tf(); execute procedure hs_office_debitor_global_insert_tf();
/** /**
Checks if the user or assumed roles are allowed to insert a row to hs_office_debitor. Checks if the user or assumed roles are allowed to insert a row to hs_office_debitor,
where only global-admin has that permission.
*/ */
create or replace function hs_office_debitor_insert_permission_missing_tf() create or replace function hs_office_debitor_insert_permission_missing_tf()
returns trigger returns trigger

View File

@ -1,6 +1,6 @@
### rbac membership ### rbac membership
This code generated was by RbacViewMermaidFlowchartGenerator at 2024-03-21T17:09:08.826781619. This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually.
```mermaid ```mermaid
%%{init:{'flowchart':{'htmlLabels':false}}}%% %%{init:{'flowchart':{'htmlLabels':false}}}%%

View File

@ -1,5 +1,5 @@
--liquibase formatted sql --liquibase formatted sql
-- This code generated was by RbacViewPostgresGenerator at 2024-03-21T17:09:08.832004329. -- This code generated was by RbacViewPostgresGenerator, do not amend manually.
-- ============================================================================ -- ============================================================================
@ -54,8 +54,8 @@ begin
hsOfficeMembershipAdmin(NEW), hsOfficeMembershipAdmin(NEW),
permissions => array['UPDATE'], permissions => array['UPDATE'],
incomingSuperRoles => array[ incomingSuperRoles => array[
hsOfficeMembershipOwner(NEW), hsOfficeRelationAgent(newPartnerRel),
hsOfficeRelationAgent(newPartnerRel)] hsOfficeMembershipOwner(NEW)]
); );
perform createRoleWithGrants( perform createRoleWithGrants(
@ -133,7 +133,10 @@ create trigger z_hs_office_membership_hs_office_relation_insert_tg
execute procedure hs_office_membership_hs_office_relation_insert_tf(); execute procedure hs_office_membership_hs_office_relation_insert_tf();
/** /**
Checks if the user or assumed roles are allowed to insert a row to hs_office_membership. Checks if the user or assumed roles are allowed to insert a row to hs_office_membership,
where the check is performed by an indirect role.
An indirect role is a role FIXME.
*/ */
create or replace function hs_office_membership_insert_permission_missing_tf() create or replace function hs_office_membership_insert_permission_missing_tf()
returns trigger returns trigger

View File

@ -4,8 +4,9 @@ spring:
platform: postgres platform: postgres
datasource: datasource:
url: jdbc:tc:postgresql:15.5-bookworm:///spring_boot_testcontainers url-tc: jdbc:tc:postgresql:15.5-bookworm:///spring_boot_testcontainers
url-local: jdbc:postgresql://localhost:5432/postgres url-local: jdbc:postgresql://localhost:5432/postgres
url: ${spring.datasource.url-tc}
username: postgres username: postgres
password: password password: password