RBAC Diagram+PostgreSQL Generator #21
@ -130,7 +130,7 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
|
|||||||
"vatBusiness",
|
"vatBusiness",
|
||||||
"vatReverseCharge",
|
"vatReverseCharge",
|
||||||
"defaultPrefix" /* TODO: do we want that updatable? */)
|
"defaultPrefix" /* TODO: do we want that updatable? */)
|
||||||
.createPermission(custom("new-debitor")).grantedTo("global", ADMIN).pop()
|
.createPermission(custom("new-debitor")).grantedTo("global", ADMIN)
|
||||||
|
|
||||||
.importRootEntityAliasProxy("debitorRel", HsOfficeRelationshipEntity.class,
|
.importRootEntityAliasProxy("debitorRel", HsOfficeRelationshipEntity.class,
|
||||||
fetchedBySql("""
|
fetchedBySql("""
|
||||||
@ -139,9 +139,9 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
|
|||||||
WHERE r.relType = 'ACCOUNTING' AND r.relHolderUuid = ${REF}.debitorRelUuid
|
WHERE r.relType = 'ACCOUNTING' AND r.relHolderUuid = ${REF}.debitorRelUuid
|
||||||
"""),
|
"""),
|
||||||
dependsOnColumn("debitorRelUuid"))
|
dependsOnColumn("debitorRelUuid"))
|
||||||
.createPermission(ALL).grantedTo("debitorRel", OWNER).pop()
|
.createPermission(ALL).grantedTo("debitorRel", OWNER)
|
||||||
.createPermission(EDIT).grantedTo("debitorRel", ADMIN).pop()
|
.createPermission(EDIT).grantedTo("debitorRel", ADMIN)
|
||||||
.createPermission(VIEW).grantedTo("debitorRel", TENANT).pop()
|
.createPermission(VIEW).grantedTo("debitorRel", TENANT)
|
||||||
|
|
||||||
.importEntityAlias("refundBankAccount", HsOfficeBankAccountEntity.class,
|
.importEntityAlias("refundBankAccount", HsOfficeBankAccountEntity.class,
|
||||||
fetchedBySql("""
|
fetchedBySql("""
|
||||||
|
@ -2,7 +2,9 @@ package net.hostsharing.hsadminng.hs.office.partner;
|
|||||||
|
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
import net.hostsharing.hsadminng.errors.DisplayName;
|
import net.hostsharing.hsadminng.errors.DisplayName;
|
||||||
|
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity;
|
||||||
import net.hostsharing.hsadminng.persistence.HasUuid;
|
import net.hostsharing.hsadminng.persistence.HasUuid;
|
||||||
|
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
|
||||||
import net.hostsharing.hsadminng.stringify.Stringify;
|
import net.hostsharing.hsadminng.stringify.Stringify;
|
||||||
import net.hostsharing.hsadminng.stringify.Stringifyable;
|
import net.hostsharing.hsadminng.stringify.Stringifyable;
|
||||||
|
|
||||||
@ -10,6 +12,11 @@ import jakarta.persistence.*;
|
|||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
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.Permission.*;
|
||||||
|
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.rbacViewFor;
|
||||||
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@ -55,6 +62,41 @@ public class HsOfficePartnerDetailsEntity implements HasUuid, Stringifyable {
|
|||||||
return registrationNumber != null ? registrationNumber
|
return registrationNumber != null ? registrationNumber
|
||||||
: birthName != null ? birthName
|
: birthName != null ? birthName
|
||||||
: birthday != null ? birthday.toString()
|
: birthday != null ? birthday.toString()
|
||||||
: dateOfDeath != null ? dateOfDeath.toString() : "<empty details>";
|
: dateOfDeath != null ? dateOfDeath.toString()
|
||||||
|
: "<empty details>";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static RbacView rbac() {
|
||||||
|
return rbacViewFor("partnerDetails", HsOfficePartnerDetailsEntity.class)
|
||||||
|
.withIdentityView(RbacView.SQL.query("""
|
||||||
|
SELECT partner_iv.idName || '-details'
|
||||||
|
FROM hs_office_partner_details AS partnerDetails
|
||||||
|
JOIN hs_office_partner partner ON partner.detailsUuid = partnerDetails.uuid
|
||||||
|
JOIN hs_office_partner_iv partner_iv ON partner_iv.uuid = partner.uuid
|
||||||
|
"""))
|
||||||
|
.withUpdatableColumns(
|
||||||
|
"registrationOffice",
|
||||||
|
"registrationNumber",
|
||||||
|
"birthPlace",
|
||||||
|
"birthName",
|
||||||
|
"birthday",
|
||||||
|
"dateOfDeath")
|
||||||
|
.createPermission(custom("new-partner-details")).grantedTo("global", ADMIN)
|
||||||
|
|
||||||
|
.importRootEntityAliasProxy("partnerRel", HsOfficeRelationshipEntity.class,
|
||||||
|
fetchedBySql("""
|
||||||
|
SELECT partnerRel.*
|
||||||
|
FROM hs_office_relationship AS partnerRel
|
||||||
|
JOIN hs_office_partner AS partner
|
||||||
|
ON partner.detailsUuid = ${ref}.uuid
|
||||||
|
WHERE partnerRel.uuid = partner.partnerRoleUuid
|
||||||
|
"""),
|
||||||
|
dependsOnColumn("partnerRoleUuid"))
|
||||||
|
|
||||||
|
// The grants are defined in HsOfficePartnerEntity.rbac()
|
||||||
|
// because they have to be changed when its partnerRel changes,
|
||||||
|
// not when anything in partner details changes.
|
||||||
|
;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,12 +7,15 @@ import net.hostsharing.hsadminng.persistence.HasUuid;
|
|||||||
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
|
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
|
||||||
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity;
|
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity;
|
||||||
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
|
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
|
||||||
|
import net.hostsharing.hsadminng.rbac.rbacdef.RbacViewMermaidFlowchart;
|
||||||
|
import net.hostsharing.hsadminng.rbac.rbacdef.RbacViewPostgresGenerator;
|
||||||
import net.hostsharing.hsadminng.stringify.Stringify;
|
import net.hostsharing.hsadminng.stringify.Stringify;
|
||||||
import net.hostsharing.hsadminng.stringify.Stringifyable;
|
import net.hostsharing.hsadminng.stringify.Stringifyable;
|
||||||
import org.hibernate.annotations.NotFound;
|
import org.hibernate.annotations.NotFound;
|
||||||
import org.hibernate.annotations.NotFoundAction;
|
import org.hibernate.annotations.NotFoundAction;
|
||||||
|
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
@ -83,19 +86,32 @@ public class HsOfficePartnerEntity implements Stringifyable, HasUuid {
|
|||||||
|| ':' || (SELECT idName FROM hs_office_person_iv p WHERE p.uuid = partner.personuuid)
|
|| ':' || (SELECT idName FROM hs_office_person_iv p WHERE p.uuid = partner.personuuid)
|
||||||
|| '-' || (SELECT idName FROM hs_office_contact_iv c WHERE c.uuid = partner.contactuuid)
|
|| '-' || (SELECT idName FROM hs_office_contact_iv c WHERE c.uuid = partner.contactuuid)
|
||||||
FROM hs_office_partner AD partner
|
FROM hs_office_partner AD partner
|
||||||
$idName$)
|
|
||||||
"""))
|
"""))
|
||||||
.withUpdatableColumns(
|
.withUpdatableColumns(
|
||||||
"partnerRoleUuid",
|
"partnerRoleUuid",
|
||||||
"personUuid",
|
"personUuid",
|
||||||
"contactUuid")
|
"contactUuid")
|
||||||
.createPermission(custom("new-partner")).grantedTo("global", ADMIN).pop()
|
.createPermission(custom("new-partner")).grantedTo("global", ADMIN)
|
||||||
|
|
||||||
.importRootEntityAliasProxy("partnerRel", HsOfficeRelationshipEntity.class,
|
.importRootEntityAliasProxy("partnerRel", HsOfficeRelationshipEntity.class,
|
||||||
fetchedBySql("SELECT * FROM hs_office_relationship AS r WHERE r.uuid = ${ref}.partnerRoleUuid"),
|
fetchedBySql("SELECT * FROM hs_office_relationship AS r WHERE r.uuid = ${ref}.partnerRoleUuid"),
|
||||||
dependsOnColumn("partnerRelUuid"))
|
dependsOnColumn("partnerRelUuid"))
|
||||||
.createPermission(ALL).grantedTo("partnerRel", ADMIN).pop()
|
.createPermission(ALL).grantedTo("partnerRel", ADMIN)
|
||||||
.createPermission(EDIT).grantedTo("partnerRel", AGENT).pop()
|
.createPermission(EDIT).grantedTo("partnerRel", AGENT)
|
||||||
.createPermission(VIEW).grantedTo("partnerRel", TENANT).pop();
|
.createPermission(VIEW).grantedTo("partnerRel", TENANT)
|
||||||
|
|
||||||
|
.importSubEntityAlias("partnerDetails", HsOfficePartnerDetailsEntity.class,
|
||||||
|
fetchedBySql("SELECT * FROM hs_office_partner_details AS d WHERE d.uuid = ${ref}.detailsUuid"),
|
||||||
|
dependsOnColumn("detailsUuid"))
|
||||||
|
.createPermission("partnerDetails", ALL).grantedTo("partnerRel", ADMIN)
|
||||||
|
.createPermission("partnerDetails", EDIT).grantedTo("partnerRel", AGENT)
|
||||||
|
.createPermission("partnerDetails", VIEW).grantedTo("partnerRel", AGENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws IOException {
|
||||||
|
final RbacView rbac = HsOfficePartnerEntity.rbac();
|
||||||
|
new RbacViewMermaidFlowchart(rbac).generateToMarkdownFile();
|
||||||
|
new RbacViewPostgresGenerator(rbac).generateToChangeLog("233-hs-office-partner-rbac.sql");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@ import java.util.function.Consumer;
|
|||||||
|
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity;
|
|
||||||
import net.hostsharing.hsadminng.persistence.HasUuid;
|
import net.hostsharing.hsadminng.persistence.HasUuid;
|
||||||
import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
|
import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
|
||||||
|
|
||||||
@ -89,6 +88,10 @@ public class RbacView {
|
|||||||
return createPermission(rootEntityAlias, permission);
|
return createPermission(rootEntityAlias, permission);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public RbacPermissionDefinition createPermission(final String entityAliasName, final Permission permission) {
|
||||||
|
return createPermission(findEntityAlias(entityAliasName), permission);
|
||||||
|
}
|
||||||
|
|
||||||
private RbacPermissionDefinition createPermission(final EntityAlias entityAlias, final Permission permission) {
|
private RbacPermissionDefinition createPermission(final EntityAlias entityAlias, final Permission permission) {
|
||||||
final RbacPermissionDefinition permDef = new RbacPermissionDefinition(entityAlias, permission, true);
|
final RbacPermissionDefinition permDef = new RbacPermissionDefinition(entityAlias, permission, true);
|
||||||
permDefs.add(permDef);
|
permDefs.add(permDef);
|
||||||
@ -107,21 +110,31 @@ public class RbacView {
|
|||||||
if ( rootEntityAliasProxy != null ) {
|
if ( rootEntityAliasProxy != null ) {
|
||||||
throw new IllegalStateException("there is already an entityAliasProxy: " + rootEntityAliasProxy);
|
throw new IllegalStateException("there is already an entityAliasProxy: " + rootEntityAliasProxy);
|
||||||
}
|
}
|
||||||
rootEntityAliasProxy = importEntityAliasImpl(aliasName, entityClass, fetchSql, dependsOnColum);
|
rootEntityAliasProxy = importEntityAliasImpl(aliasName, entityClass, fetchSql, dependsOnColum, false);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RbacView importSubEntityAlias(
|
||||||
|
final String aliasName, final Class<? extends HasUuid> entityClass,
|
||||||
|
final SQL fetchSql, final Column dependsOnColum) {
|
||||||
|
importEntityAliasImpl(aliasName, entityClass, fetchSql, dependsOnColum, true);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public RbacView importEntityAlias(
|
public RbacView importEntityAlias(
|
||||||
final String aliasName, final Class<? extends HasUuid> entityClass, final SQL fetchSql, final Column dependsOnColum) {
|
final String aliasName, final Class<? extends HasUuid> entityClass,
|
||||||
importEntityAliasImpl(aliasName, entityClass, fetchSql, dependsOnColum);
|
final SQL fetchSql, final Column dependsOnColum) {
|
||||||
|
importEntityAliasImpl(aliasName, entityClass, fetchSql, dependsOnColum, false);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
private EntityAlias importEntityAliasImpl(final String aliasName, final Class<? extends HasUuid> entityClass, final SQL fetchSql, final Column dependsOnColum) {
|
private EntityAlias importEntityAliasImpl(
|
||||||
final var entityAlias = new EntityAlias(aliasName, entityClass, fetchSql, dependsOnColum);
|
final String aliasName, final Class<? extends HasUuid> entityClass,
|
||||||
|
final SQL fetchSql, final Column dependsOnColum, boolean asSubEntity) {
|
||||||
|
final var entityAlias = new EntityAlias(aliasName, entityClass, fetchSql, dependsOnColum, asSubEntity);
|
||||||
entityAliases.put(aliasName, entityAlias);
|
entityAliases.put(aliasName, entityAlias);
|
||||||
try {
|
try {
|
||||||
importAsAlias(aliasName, rbacDefinition(entityClass));
|
importAsAlias(aliasName, rbacDefinition(entityClass), asSubEntity);
|
||||||
} catch ( final ReflectiveOperationException exc) {
|
} catch ( final ReflectiveOperationException exc) {
|
||||||
throw new RuntimeException("cannot import entity: " + entityClass, exc);
|
throw new RuntimeException("cannot import entity: " + entityClass, exc);
|
||||||
}
|
}
|
||||||
@ -133,11 +146,13 @@ public class RbacView {
|
|||||||
return (RbacView) entityClass.getMethod("rbac").invoke(null);
|
return (RbacView) entityClass.getMethod("rbac").invoke(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private RbacView importAsAlias(final String aliasName, final RbacView importedRbacView) {
|
private RbacView importAsAlias(final String aliasName, final RbacView importedRbacView, final boolean asSubEntity) {
|
||||||
final var mapper = new AliasNameMapper(importedRbacView, aliasName);
|
final var mapper = new AliasNameMapper(importedRbacView, aliasName,
|
||||||
|
asSubEntity ? entityAliases.keySet() : null);
|
||||||
importedRbacView.getEntityAliases().values().stream()
|
importedRbacView.getEntityAliases().values().stream()
|
||||||
.filter(entityAlias -> !importedRbacView.isRootEntityAlias(entityAlias))
|
.filter(entityAlias -> !importedRbacView.isRootEntityAlias(entityAlias))
|
||||||
.filter(entityAlias -> !entityAlias.isGlobal())
|
.filter(entityAlias -> !entityAlias.isGlobal())
|
||||||
|
.filter(entityAlias -> !asSubEntity || !entityAliases.containsKey(entityAlias.aliasName))
|
||||||
.forEach(entityAlias -> {
|
.forEach(entityAlias -> {
|
||||||
final String mappedAliasName = mapper.map(entityAlias.aliasName);
|
final String mappedAliasName = mapper.map(entityAlias.aliasName);
|
||||||
entityAliases.put(mappedAliasName, new EntityAlias(mappedAliasName, entityAlias.entityClass));
|
entityAliases.put(mappedAliasName, new EntityAlias(mappedAliasName, entityAlias.entityClass));
|
||||||
@ -302,26 +317,15 @@ public class RbacView {
|
|||||||
final Permission permission;
|
final Permission permission;
|
||||||
final boolean toCreate;
|
final boolean toCreate;
|
||||||
|
|
||||||
public RbacPermissionDefinition(final EntityAlias entityAlias, final Permission permission, final boolean toCreate) {
|
private RbacPermissionDefinition(final EntityAlias entityAlias, final Permission permission, final boolean toCreate) {
|
||||||
this.entityAlias = entityAlias;
|
this.entityAlias = entityAlias;
|
||||||
this.permission = permission;
|
this.permission = permission;
|
||||||
this.toCreate = toCreate;
|
this.toCreate = toCreate;
|
||||||
}
|
}
|
||||||
|
|
||||||
public RbacView pop() {
|
public RbacView grantedTo(final String entityAlias, final Role role) {
|
||||||
return RbacView.this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public RbacPermissionDefinition withIncomingSuperRole(
|
|
||||||
final Class<HsOfficeRelationshipEntity> hsOfficeRelationshipEntityClass,
|
|
||||||
final Role owner) {
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public RbacPermissionDefinition grantedTo(final String entityAlias, final Role role) {
|
|
||||||
findOrCreateGrantDef(this, findRbacRole(entityAlias, role) ).toCreate();
|
findOrCreateGrantDef(this, findRbacRole(entityAlias, role) ).toCreate();
|
||||||
return this;
|
return RbacView.this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -441,14 +445,14 @@ public class RbacView {
|
|||||||
.orElseGet(() -> new RbacGrantDefinition(subRoleDefinition, superRoleDefinition));
|
.orElseGet(() -> new RbacGrantDefinition(subRoleDefinition, superRoleDefinition));
|
||||||
}
|
}
|
||||||
|
|
||||||
record EntityAlias(String aliasName, Class<? extends RbacObject> entityClass, SQL fetchSql, Column dependsOnColum) {
|
record EntityAlias(String aliasName, Class<? extends RbacObject> entityClass, SQL fetchSql, Column dependsOnColum, boolean isSubEntity) {
|
||||||
|
|
||||||
public EntityAlias(final String aliasName) {
|
public EntityAlias(final String aliasName) {
|
||||||
this(aliasName, null, null, null);
|
this(aliasName, null, null, null, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public EntityAlias(final String aliasName, final Class<? extends RbacObject> entityClass) {
|
public EntityAlias(final String aliasName, final Class<? extends RbacObject> entityClass) {
|
||||||
this(aliasName, entityClass, null, null);
|
this(aliasName, entityClass, null, null, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isGlobal() {
|
boolean isGlobal() {
|
||||||
@ -459,8 +463,6 @@ public class RbacView {
|
|||||||
return entityClass == null;
|
return entityClass == null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private String withoutEntitySuffix(final String simpleEntityName) {
|
private String withoutEntitySuffix(final String simpleEntityName) {
|
||||||
return simpleEntityName.substring(0, simpleEntityName.length()-"Entity".length());
|
return simpleEntityName.substring(0, simpleEntityName.length()-"Entity".length());
|
||||||
}
|
}
|
||||||
@ -507,14 +509,27 @@ public class RbacView {
|
|||||||
|
|
||||||
public static class SQL {
|
public static class SQL {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DSL methid to specify an SQL SELECT expression which fetches the related entity,
|
||||||
|
* using the reference `${ref}` of the root entity.
|
||||||
|
* `${ref}` is going to be replaced by either `NEW` or `OLD` of the trigger function.
|
||||||
|
* `into ...` will be added with a variable name prefixed with either `new` or `old`.
|
||||||
|
*
|
||||||
|
* @param sql an SQL SELECT expression (not ending with ';)
|
||||||
|
* @return the wrapped SQL expression
|
||||||
|
*/
|
||||||
public static SQL fetchedBySql(final String sql) {
|
public static SQL fetchedBySql(final String sql) {
|
||||||
|
validateExpression(sql);
|
||||||
return new SQL(sql);
|
return new SQL(sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Generic DSL method to specify an SQL SELECT expression.
|
||||||
|
*
|
||||||
|
* @param sql an SQL SELECT expression (not ending with ';)
|
||||||
|
* @return the wrapped SQL expression
|
||||||
|
*/
|
||||||
public static SQL query(final String sql) {
|
public static SQL query(final String sql) {
|
||||||
if (sql.matches(";[ \t]*$")) {
|
validateExpression(sql);
|
||||||
throw new IllegalArgumentException("SQL expression must not end with ';': " + sql);
|
|
||||||
}
|
|
||||||
return new SQL(sql);
|
return new SQL(sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -523,6 +538,12 @@ public class RbacView {
|
|||||||
private SQL(final String sql) {
|
private SQL(final String sql) {
|
||||||
this.sql = sql;
|
this.sql = sql;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void validateExpression(final String sql) {
|
||||||
|
if (sql.matches(";[ \t]*$")) {
|
||||||
|
throw new IllegalArgumentException("SQL expression must not end with ';': " + sql);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Column {
|
public static class Column {
|
||||||
@ -543,18 +564,21 @@ public class RbacView {
|
|||||||
private final RbacView importedRbacView;
|
private final RbacView importedRbacView;
|
||||||
private final String outerAliasName;
|
private final String outerAliasName;
|
||||||
|
|
||||||
AliasNameMapper(final RbacView importedRbacView, final String outerAliasName) {
|
private final Set<String> outerAliasNames;
|
||||||
|
|
||||||
|
AliasNameMapper(final RbacView importedRbacView, final String outerAliasName, final Set<String> outerAliasNames) {
|
||||||
this.importedRbacView = importedRbacView;
|
this.importedRbacView = importedRbacView;
|
||||||
this.outerAliasName = outerAliasName;
|
this.outerAliasName = outerAliasName;
|
||||||
|
this.outerAliasNames = (outerAliasNames == null) ? Collections.emptySet() : outerAliasNames;
|
||||||
}
|
}
|
||||||
|
|
||||||
String map(final String originalAliasName) {
|
String map(final String originalAliasName) {
|
||||||
|
if (outerAliasNames.contains(originalAliasName) || originalAliasName.equals("global")) {
|
||||||
|
return originalAliasName;
|
||||||
|
}
|
||||||
if (originalAliasName.equals(importedRbacView.rootEntityAlias.aliasName) ) {
|
if (originalAliasName.equals(importedRbacView.rootEntityAlias.aliasName) ) {
|
||||||
return outerAliasName;
|
return outerAliasName;
|
||||||
}
|
}
|
||||||
if (originalAliasName.equals("global") ) {
|
|
||||||
return originalAliasName;
|
|
||||||
}
|
|
||||||
return outerAliasName + "." + originalAliasName;
|
return outerAliasName + "." + originalAliasName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package net.hostsharing.hsadminng.rbac.rbacdef;
|
|||||||
|
|
||||||
import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity;
|
import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity;
|
||||||
import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity;
|
import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity;
|
||||||
|
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerDetailsEntity;
|
||||||
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity;
|
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity;
|
||||||
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity;
|
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
@ -16,6 +17,7 @@ import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacGrantDefinitio
|
|||||||
public class RbacViewMermaidFlowchart {
|
public class RbacViewMermaidFlowchart {
|
||||||
|
|
||||||
public static final String HOSTSHARING_ORANGE = "#dd4901";
|
public static final String HOSTSHARING_ORANGE = "#dd4901";
|
||||||
|
public static final String HOSTSHARING_ORANGE_LIGHT = "#feb28c";
|
||||||
public static final String HOSTSHARING_LIGHTBLUE = "#99bcdb";
|
public static final String HOSTSHARING_LIGHTBLUE = "#99bcdb";
|
||||||
private final RbacView rbacDef;
|
private final RbacView rbacDef;
|
||||||
private final StringWriter flowchart = new StringWriter();
|
private final StringWriter flowchart = new StringWriter();
|
||||||
@ -37,7 +39,9 @@ public class RbacViewMermaidFlowchart {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void renderEntitySubgraph(final RbacView.EntityAlias entity) {
|
private void renderEntitySubgraph(final RbacView.EntityAlias entity) {
|
||||||
final var color = rbacDef.isRootEntityAlias(entity) ? HOSTSHARING_ORANGE : HOSTSHARING_LIGHTBLUE;
|
final var color = rbacDef.isRootEntityAlias(entity) ? HOSTSHARING_ORANGE
|
||||||
|
: entity.isSubEntity() ? HOSTSHARING_ORANGE_LIGHT
|
||||||
|
: HOSTSHARING_LIGHTBLUE;
|
||||||
flowchart.writeLn("""
|
flowchart.writeLn("""
|
||||||
subgraph %{aliasName}["`**%{aliasName}**`"]
|
subgraph %{aliasName}["`**%{aliasName}**`"]
|
||||||
direction TB
|
direction TB
|
||||||
@ -142,7 +146,7 @@ public class RbacViewMermaidFlowchart {
|
|||||||
return flowchart.toString();
|
return flowchart.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
void generateToMarkdownFile() throws IOException {
|
public void generateToMarkdownFile() throws IOException {
|
||||||
final Path path = Paths.get("doc", rbacDef.getRootEntityAlias().simpleName() + ".md");
|
final Path path = Paths.get("doc", rbacDef.getRootEntityAlias().simpleName() + ".md");
|
||||||
Files.writeString(
|
Files.writeString(
|
||||||
path,
|
path,
|
||||||
@ -164,6 +168,7 @@ public class RbacViewMermaidFlowchart {
|
|||||||
new RbacViewMermaidFlowchart(HsOfficeBankAccountEntity.rbac()).generateToMarkdownFile();
|
new RbacViewMermaidFlowchart(HsOfficeBankAccountEntity.rbac()).generateToMarkdownFile();
|
||||||
new RbacViewMermaidFlowchart(HsOfficeRelationshipEntity.rbac()).generateToMarkdownFile();
|
new RbacViewMermaidFlowchart(HsOfficeRelationshipEntity.rbac()).generateToMarkdownFile();
|
||||||
new RbacViewMermaidFlowchart(HsOfficePartnerEntity.rbac()).generateToMarkdownFile();
|
new RbacViewMermaidFlowchart(HsOfficePartnerEntity.rbac()).generateToMarkdownFile();
|
||||||
|
new RbacViewMermaidFlowchart(HsOfficePartnerDetailsEntity.rbac()).generateToMarkdownFile();
|
||||||
new RbacViewMermaidFlowchart(HsOfficeDebitorEntity.rbac()).generateToMarkdownFile();
|
new RbacViewMermaidFlowchart(HsOfficeDebitorEntity.rbac()).generateToMarkdownFile();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,8 +24,9 @@ public class RbacViewPostgresGenerator {
|
|||||||
liqibaseTagPrefix = rbacDef.getRootEntityAlias().entityClass().getSimpleName();
|
liqibaseTagPrefix = rbacDef.getRootEntityAlias().entityClass().getSimpleName();
|
||||||
plPgSql.writeLn("""
|
plPgSql.writeLn("""
|
||||||
--liquibase formatted sql
|
--liquibase formatted sql
|
||||||
-- generated at: %{timestamp}
|
-- This code generated was by ${generator} at %{timestamp}.
|
||||||
"""
|
"""
|
||||||
|
.replace("${generator}", getClass().getSimpleName())
|
||||||
.replace("%{timestamp}", LocalDateTime.now().toString()));
|
.replace("%{timestamp}", LocalDateTime.now().toString()));
|
||||||
|
|
||||||
new RolesGrantsAndPermissionsGenerator(rbacDef, liqibaseTagPrefix).generateTo(plPgSql);
|
new RolesGrantsAndPermissionsGenerator(rbacDef, liqibaseTagPrefix).generateTo(plPgSql);
|
||||||
@ -46,6 +47,16 @@ public class RbacViewPostgresGenerator {
|
|||||||
System.out.println(outputPath.toAbsolutePath());
|
System.out.println(outputPath.toAbsolutePath());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void generateToChangeLog(final String fileName) throws IOException {
|
||||||
|
final Path outputPath = Path.of("src/main/resources/db/changelog", fileName);
|
||||||
|
Files.writeString(
|
||||||
|
outputPath,
|
||||||
|
toString(),
|
||||||
|
StandardOpenOption.CREATE,
|
||||||
|
StandardOpenOption.TRUNCATE_EXISTING);
|
||||||
|
System.out.println(outputPath.toAbsolutePath());
|
||||||
|
}
|
||||||
|
|
||||||
public static void main(String[] args) throws IOException {
|
public static void main(String[] args) throws IOException {
|
||||||
generatePostgres(HsOfficeRelationshipEntity.rbac());
|
generatePostgres(HsOfficeRelationshipEntity.rbac());
|
||||||
generatePostgres(HsOfficePartnerEntity.rbac());
|
generatePostgres(HsOfficePartnerEntity.rbac());
|
||||||
|
@ -98,6 +98,7 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
createRolesWithGrantsSql(plPgSql, REFERRER);
|
createRolesWithGrantsSql(plPgSql, REFERRER);
|
||||||
|
|
||||||
plPgSql.writeLn();
|
plPgSql.writeLn();
|
||||||
|
// TODO: we need to group and sort the grants, similar to the Flowchart generator
|
||||||
rbacGrants.forEach(g -> plPgSql.writeLn(generateGrant(g)));
|
rbacGrants.forEach(g -> plPgSql.writeLn(generateGrant(g)));
|
||||||
|
|
||||||
plPgSql.writeLn("return NEW;");
|
plPgSql.writeLn("return NEW;");
|
||||||
@ -123,10 +124,14 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
return "createPermissions(${entityRef}.uuid, array ['${perm}']"
|
return "createPermissions(${entityRef}.uuid, array ['${perm}']"
|
||||||
.replace("${entityRef}", rbacDef.isRootEntityAlias(permDef.entityAlias)
|
.replace("${entityRef}", rbacDef.isRootEntityAlias(permDef.entityAlias)
|
||||||
? ref.name()
|
? ref.name()
|
||||||
: "not implemented yet" ) // TODO
|
: refVarName(ref, permDef.entityAlias))
|
||||||
.replace("${perm}", permDef.permission.permission());
|
.replace("${perm}", permDef.permission.permission());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String refVarName(final PostgresTriggerReference ref, final RbacView.EntityAlias entityAlias) {
|
||||||
|
return ref.name().toLowerCase() + capitalize(entityAlias.aliasName());
|
||||||
|
}
|
||||||
|
|
||||||
private String getRawTableName(final Class<?> entityClass) {
|
private String getRawTableName(final Class<?> entityClass) {
|
||||||
return withoutRvSuffix(entityClass.getAnnotation(Table.class).name());
|
return withoutRvSuffix(entityClass.getAnnotation(Table.class).name());
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user