improved RBAC generators #26
@ -19,6 +19,7 @@ import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
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.Role.*;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.fetchedBySql;
|
||||
@ -149,8 +150,8 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
|
||||
SELECT *
|
||||
FROM hs_office_relation AS r
|
||||
WHERE r.type = 'DEBITOR' AND r.holderUuid = ${REF}.debitorRelUuid
|
||||
""")
|
||||
)
|
||||
"""),
|
||||
NULLABLE)
|
||||
.toRole("refundBankAccount", ADMIN).grantRole("debitorRel", AGENT)
|
||||
.toRole("debitorRel", AGENT).grantRole("refundBankAccount", REFERRER)
|
||||
|
||||
@ -159,8 +160,8 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
|
||||
SELECT *
|
||||
FROM hs_office_relation AS partnerRel
|
||||
WHERE ${debitorRel}.anchorUuid = partnerRel.holderUuid
|
||||
""")
|
||||
)
|
||||
"""),
|
||||
NULLABLE)
|
||||
.toRole("partnerRel", ADMIN).grantRole("debitorRel", ADMIN)
|
||||
.toRole("partnerRel", AGENT).grantRole("debitorRel", AGENT)
|
||||
.toRole("debitorRel", AGENT).grantRole("partnerRel", TENANT)
|
||||
|
@ -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.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.RbacUserReference.UserRole.CREATOR;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
|
||||
@ -90,16 +91,16 @@ public class HsOfficeRelationEntity implements HasUuid, Stringifyable {
|
||||
.withUpdatableColumns("contactUuid")
|
||||
.importEntityAlias("anchorPerson", HsOfficePersonEntity.class,
|
||||
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,
|
||||
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,
|
||||
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) -> {
|
||||
with.owningUser(CREATOR);
|
||||
with.incomingSuperRole(GLOBAL, ADMIN);
|
||||
|
@ -66,6 +66,21 @@ public class RbacView {
|
||||
private EntityAlias rootEntityAliasProxy;
|
||||
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) {
|
||||
return new RbacView(alias, entityClass);
|
||||
}
|
||||
@ -77,22 +92,71 @@ public class RbacView {
|
||||
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) {
|
||||
Collections.addAll(updatableColumns, columnNames);
|
||||
verifyVersionColumnExists();
|
||||
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) {
|
||||
this.identityViewSqlQuery = sqlExpression;
|
||||
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) {
|
||||
this.orderBySqlExpression = orderBySqlExpression;
|
||||
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) {
|
||||
final RbacRoleDefinition newRoleDef = findRbacRole(rootEntityAlias, role).toCreate();
|
||||
with.accept(newRoleDef);
|
||||
@ -100,6 +164,15 @@ public class RbacView {
|
||||
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) {
|
||||
final RbacRoleDefinition newRoleDef = findRbacRole(rootEntityAlias, role).toCreate();
|
||||
findOrCreateGrantDef(newRoleDef, previousRoleDef).toCreate();
|
||||
@ -107,6 +180,19 @@ public class RbacView {
|
||||
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) {
|
||||
final RbacRoleDefinition newRoleDef = findRbacRole(rootEntityAlias, role).toCreate();
|
||||
findOrCreateGrantDef(newRoleDef, previousRoleDef).toCreate();
|
||||
@ -115,10 +201,38 @@ public class RbacView {
|
||||
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) {
|
||||
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) {
|
||||
return createPermission(findEntityAlias(entityAliasName), permission);
|
||||
}
|
||||
@ -134,6 +248,32 @@ public class RbacView {
|
||||
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(
|
||||
final String aliasName,
|
||||
final Class<? extends HasUuid> entityClass,
|
||||
@ -146,6 +286,18 @@ public class RbacView {
|
||||
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(
|
||||
final String aliasName, final Class<? extends HasUuid> entityClass,
|
||||
final SQL fetchSql, final Column dependsOnColum) {
|
||||
@ -153,6 +305,33 @@ public class RbacView {
|
||||
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(
|
||||
final String aliasName, final Class<? extends HasUuid> entityClass,
|
||||
final Column dependsOnColum, final SQL fetchSql, final Nullable nullable) {
|
||||
@ -160,13 +339,7 @@ public class RbacView {
|
||||
return this;
|
||||
}
|
||||
|
||||
public RbacView importEntityAlias(
|
||||
final String aliasName, final Class<? extends HasUuid> entityClass,
|
||||
final Column dependsOnColum, final SQL fetchSql) {
|
||||
importEntityAliasImpl(aliasName, entityClass, fetchSql, dependsOnColum, false, NOT_NULL);
|
||||
return this;
|
||||
}
|
||||
|
||||
// TODO: remove once it's not used in HsOffice...Entity anymore
|
||||
public RbacView importEntityAlias(
|
||||
final String aliasName, final Class<? extends HasUuid> entityClass,
|
||||
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) {
|
||||
return new RbacGrantBuilder(entityAlias, role);
|
||||
}
|
||||
@ -430,6 +613,16 @@ public class RbacView {
|
||||
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) {
|
||||
findOrCreateGrantDef(this, findRbacRole(entityAlias, role)).toCreate();
|
||||
return RbacView.this;
|
||||
@ -460,19 +653,61 @@ public class RbacView {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
final var incomingSuperRole = findRbacRole(entityAlias, role);
|
||||
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) {
|
||||
final var outgoingSubRole = findRbacRole(entityAlias, role);
|
||||
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) {
|
||||
Stream.of(
|
||||
TestCustomerEntity.class,
|
||||
|
@ -4,7 +4,6 @@ import lombok.SneakyThrows;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.nio.file.*;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static java.util.stream.Collectors.joining;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacGrantDefinition.GrantType.*;
|
||||
|
@ -5,7 +5,6 @@ import lombok.SneakyThrows;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
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.StringWriter.with;
|
||||
|
@ -14,6 +14,7 @@ import java.io.IOException;
|
||||
import java.util.UUID;
|
||||
|
||||
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.Role.*;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.fetchedBySql;
|
||||
@ -52,7 +53,8 @@ public class TestDomainEntity implements HasUuid {
|
||||
fetchedBySql("""
|
||||
SELECT * FROM test_package p
|
||||
WHERE p.uuid= ${ref}.packageUuid
|
||||
"""))
|
||||
"""),
|
||||
NULLABLE)
|
||||
.toRole("package", ADMIN).grantPermission(INSERT)
|
||||
|
||||
.createRole(OWNER, (with) -> {
|
||||
|
@ -14,6 +14,7 @@ import java.io.IOException;
|
||||
import java.util.UUID;
|
||||
|
||||
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.Role.*;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.*;
|
||||
@ -53,7 +54,8 @@ public class TestPackageEntity implements HasUuid {
|
||||
fetchedBySql("""
|
||||
SELECT * FROM test_customer c
|
||||
WHERE c.uuid= ${ref}.customerUuid
|
||||
"""))
|
||||
"""),
|
||||
NOT_NULL)
|
||||
.toRole("customer", ADMIN).grantPermission(INSERT)
|
||||
|
||||
.createRole(OWNER, (with) -> {
|
||||
|
@ -160,6 +160,7 @@ create or replace function cleanIdentifier(rawIdentifier varchar)
|
||||
declare
|
||||
cleanIdentifier varchar;
|
||||
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');
|
||||
return cleanIdentifier;
|
||||
end; $$;
|
||||
|
Loading…
Reference in New Issue
Block a user