JavaDoc for RbacView

This commit is contained in:
Michael Hoennig 2024-03-23 15:00:07 +01:00
parent 87c23d2aa9
commit fbe2204d72
8 changed files with 264 additions and 21 deletions

View File

@ -19,6 +19,7 @@ import java.util.Optional;
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.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.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.fetchedBySql; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.fetchedBySql;
@ -149,8 +150,8 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
SELECT * SELECT *
FROM hs_office_relation AS r FROM hs_office_relation AS r
WHERE r.type = 'DEBITOR' AND r.holderUuid = ${REF}.debitorRelUuid WHERE r.type = 'DEBITOR' AND r.holderUuid = ${REF}.debitorRelUuid
""") """),
) 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)
@ -159,8 +160,8 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
SELECT * SELECT *
FROM hs_office_relation AS partnerRel FROM hs_office_relation AS partnerRel
WHERE ${debitorRel}.anchorUuid = partnerRel.holderUuid WHERE ${debitorRel}.anchorUuid = partnerRel.holderUuid
""") """),
) NULLABLE)
.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

@ -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.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.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"),
) NULLABLE)
.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"),
) NULLABLE)
.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"),
) NULLABLE)
.createRole(OWNER, (with) -> { .createRole(OWNER, (with) -> {
with.owningUser(CREATOR); with.owningUser(CREATOR);
with.incomingSuperRole(GLOBAL, ADMIN); with.incomingSuperRole(GLOBAL, ADMIN);

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,9 @@ public class RbacView {
} }
} }
/**
* 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,

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.*;

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;

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.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.*;
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
""")) """),
NULLABLE)
.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; $$;