RBAC Diagram+PostgreSQL Generator #21
@ -8,6 +8,7 @@ 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 net.hostsharing.hsadminng.persistence.HasUuid;
|
import net.hostsharing.hsadminng.persistence.HasUuid;
|
||||||
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
|
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
|
||||||
|
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
|
||||||
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.GenericGenerator;
|
import org.hibernate.annotations.GenericGenerator;
|
||||||
@ -107,7 +108,7 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
|
|||||||
|
|
||||||
public static RbacView rbac() {
|
public static RbacView rbac() {
|
||||||
return rbacViewFor("debitor", HsOfficeDebitorEntity.class)
|
return rbacViewFor("debitor", HsOfficeDebitorEntity.class)
|
||||||
.withIdentityView(RbacView.SQL.query("""
|
.withIdentityView(SQL.query("""
|
||||||
SELECT debitor.uuid,
|
SELECT debitor.uuid,
|
||||||
'D-' || (SELECT partner.partnerNumber
|
'D-' || (SELECT partner.partnerNumber
|
||||||
FROM hs_office_partner partner
|
FROM hs_office_partner partner
|
||||||
@ -117,7 +118,7 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
|
|||||||
ON debitorRel.relAnchorUuid = partnerRel.relHolderUuid AND partnerRel.relType = 'ACCOUNTING'
|
ON debitorRel.relAnchorUuid = partnerRel.relHolderUuid AND partnerRel.relType = 'ACCOUNTING'
|
||||||
WHERE debitorRel.uuid = debitor.debitorRelUuid)
|
WHERE debitorRel.uuid = debitor.debitorRelUuid)
|
||||||
|| to_char(debitorNumberSuffix, 'fm00')
|
|| to_char(debitorNumberSuffix, 'fm00')
|
||||||
from hs_office_debitor as debitor;
|
from hs_office_debitor as debitor
|
||||||
"""))
|
"""))
|
||||||
.withUpdatableColumns(
|
.withUpdatableColumns(
|
||||||
"debitorRel",
|
"debitorRel",
|
||||||
@ -131,11 +132,11 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
|
|||||||
"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).pop()
|
||||||
|
|
||||||
.importProxyEntity("debitorRel", HsOfficeRelationshipEntity.class,
|
.importRootEntityAliasProxy("debitorRel", HsOfficeRelationshipEntity.class,
|
||||||
fetchedBySql("""
|
fetchedBySql("""
|
||||||
SELECT *
|
SELECT *
|
||||||
FROM hs_office_relationship AS r
|
FROM hs_office_relationship AS r
|
||||||
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).pop()
|
||||||
@ -146,7 +147,7 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
|
|||||||
fetchedBySql("""
|
fetchedBySql("""
|
||||||
SELECT *
|
SELECT *
|
||||||
FROM hs_office_relationship AS r
|
FROM hs_office_relationship AS r
|
||||||
WHERE r.relType = 'ACCOUNTING' AND r.relHolderUuid = ${REF}.debitorRelUuid;
|
WHERE r.relType = 'ACCOUNTING' AND r.relHolderUuid = ${REF}.debitorRelUuid
|
||||||
"""),
|
"""),
|
||||||
dependsOnColumn("bankAccountUuid"))
|
dependsOnColumn("bankAccountUuid"))
|
||||||
.toRole("refundBankAccount", ADMIN).grantRole("debitorRel", AGENT)
|
.toRole("refundBankAccount", ADMIN).grantRole("debitorRel", AGENT)
|
||||||
@ -156,7 +157,7 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
|
|||||||
fetchedBySql("""
|
fetchedBySql("""
|
||||||
SELECT *
|
SELECT *
|
||||||
FROM hs_office_relationship AS partnerRel
|
FROM hs_office_relationship AS partnerRel
|
||||||
WHERE ${debitorRel}.relAnchorUuid = partnerRel.relHolderUuid;
|
WHERE ${debitorRel}.relAnchorUuid = partnerRel.relHolderUuid
|
||||||
"""),
|
"""),
|
||||||
dependsOnColumn("debitorRelUuid"))
|
dependsOnColumn("debitorRelUuid"))
|
||||||
.toRole("partnerRel", ADMIN).grantRole("debitorRel", ADMIN)
|
.toRole("partnerRel", ADMIN).grantRole("debitorRel", ADMIN)
|
||||||
|
@ -6,6 +6,7 @@ import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
|
|||||||
import net.hostsharing.hsadminng.persistence.HasUuid;
|
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.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;
|
||||||
@ -15,6 +16,12 @@ import jakarta.persistence.*;
|
|||||||
import java.util.Optional;
|
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.Permission.*;
|
||||||
|
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.VIEW;
|
||||||
|
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
|
||||||
@ -68,4 +75,27 @@ public class HsOfficePartnerEntity implements Stringifyable, HasUuid {
|
|||||||
public String toShortString() {
|
public String toShortString() {
|
||||||
return Optional.ofNullable(person).map(HsOfficePersonEntity::toShortString).orElse("<person=null>");
|
return Optional.ofNullable(person).map(HsOfficePersonEntity::toShortString).orElse("<person=null>");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static RbacView rbac() {
|
||||||
|
return rbacViewFor("partner", HsOfficePartnerEntity.class)
|
||||||
|
.withIdentityView(RbacView.SQL.query("""
|
||||||
|
SELECT partner.partnerNumber
|
||||||
|
|| ':' || (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)
|
||||||
|
FROM hs_office_partner AD partner
|
||||||
|
$idName$)
|
||||||
|
"""))
|
||||||
|
.withUpdatableColumns(
|
||||||
|
"partnerRoleUuid",
|
||||||
|
"personUuid",
|
||||||
|
"contactUuid")
|
||||||
|
.createPermission(custom("new-partner")).grantedTo("global", ADMIN).pop()
|
||||||
|
|
||||||
|
.importRootEntityAliasProxy("partnerRel", HsOfficeRelationshipEntity.class,
|
||||||
|
fetchedBySql("SELECT * FROM hs_office_relationship AS r WHERE r.uuid = ${ref}.partnerRoleUuid"),
|
||||||
|
dependsOnColumn("partnerRelUuid"))
|
||||||
|
.createPermission(ALL).grantedTo("partnerRel", ADMIN).pop()
|
||||||
|
.createPermission(EDIT).grantedTo("partnerRel", AGENT).pop()
|
||||||
|
.createPermission(VIEW).grantedTo("partnerRel", TENANT).pop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -97,10 +97,10 @@ public class HsOfficeRelationshipEntity implements HasUuid, Stringifyable {
|
|||||||
.createRole(OWNER, (with) -> {
|
.createRole(OWNER, (with) -> {
|
||||||
with.owningUser(CREATOR);
|
with.owningUser(CREATOR);
|
||||||
with.incomingSuperRole(GLOBAL, ADMIN);
|
with.incomingSuperRole(GLOBAL, ADMIN);
|
||||||
with.incomingSuperRole("anchorPerson", ADMIN);
|
|
||||||
with.permission(ALL);
|
with.permission(ALL);
|
||||||
})
|
})
|
||||||
.createSubRole(ADMIN, (with) -> {
|
.createSubRole(ADMIN, (with) -> {
|
||||||
|
with.incomingSuperRole("anchorPerson", ADMIN);
|
||||||
with.permission(EDIT);
|
with.permission(EDIT);
|
||||||
})
|
})
|
||||||
.createSubRole(AGENT, (with) -> {
|
.createSubRole(AGENT, (with) -> {
|
||||||
|
@ -101,7 +101,7 @@ public class RbacView {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public <EC extends RbacObject> RbacView importProxyEntity(
|
public <EC extends RbacObject> RbacView importRootEntityAliasProxy(
|
||||||
final String aliasName, final Class entityClass, final SQL fetchSql, final Column dependsOnColum) {
|
final String aliasName, final Class entityClass, final SQL fetchSql, final Column dependsOnColum) {
|
||||||
if ( rootEntityAliasProxy != null ) {
|
if ( rootEntityAliasProxy != null ) {
|
||||||
throw new IllegalStateException("there is already an entityAliasProxy: " + rootEntityAliasProxy);
|
throw new IllegalStateException("there is already an entityAliasProxy: " + rootEntityAliasProxy);
|
||||||
|
@ -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.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;
|
||||||
|
|
||||||
@ -162,6 +163,7 @@ public class RbacViewMermaidFlowchart {
|
|||||||
public static void main(String[] args) throws IOException {
|
public static void main(String[] args) throws IOException {
|
||||||
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(HsOfficeDebitorEntity.rbac()).generateToMarkdownFile();
|
new RbacViewMermaidFlowchart(HsOfficeDebitorEntity.rbac()).generateToMarkdownFile();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package net.hostsharing.hsadminng.rbac.rbacdef;
|
package net.hostsharing.hsadminng.rbac.rbacdef;
|
||||||
|
|
||||||
|
import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity;
|
||||||
|
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 java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -26,19 +28,15 @@ public class RbacViewPostgresGenerator {
|
|||||||
"""
|
"""
|
||||||
.replace("%{timestamp}", LocalDateTime.now().toString()));
|
.replace("%{timestamp}", LocalDateTime.now().toString()));
|
||||||
|
|
||||||
// generateSqlForRelatedRbacObject();
|
|
||||||
|
|
||||||
new RolesGrantsAndPermissionsGenerator(rbacDef, liqibaseTagPrefix).generateTo(plPgSql);
|
new RolesGrantsAndPermissionsGenerator(rbacDef, liqibaseTagPrefix).generateTo(plPgSql);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return plPgSql.toString();
|
return plPgSql.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) throws IOException {
|
private static void generatePostgres(final RbacView rbac) throws IOException {
|
||||||
final var rbac = HsOfficeRelationshipEntity.rbac();
|
|
||||||
final Path outputPath = Paths.get("doc", rbac.getRootEntityAlias().simpleName() + ".sql");
|
final Path outputPath = Paths.get("doc", rbac.getRootEntityAlias().simpleName() + ".sql");
|
||||||
Files.writeString(
|
Files.writeString(
|
||||||
outputPath,
|
outputPath,
|
||||||
@ -47,4 +45,10 @@ public class RbacViewPostgresGenerator {
|
|||||||
|
|
||||||
System.out.println(outputPath.toAbsolutePath());
|
System.out.println(outputPath.toAbsolutePath());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws IOException {
|
||||||
|
generatePostgres(HsOfficeRelationshipEntity.rbac());
|
||||||
|
generatePostgres(HsOfficePartnerEntity.rbac());
|
||||||
|
generatePostgres(HsOfficeDebitorEntity.rbac());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -84,12 +84,11 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
|
|
||||||
plPgSql.indented(() -> {
|
plPgSql.indented(() -> {
|
||||||
|
|
||||||
plPgSql.writeLn();
|
|
||||||
rbacDef.getEntityAliases().values().stream()
|
rbacDef.getEntityAliases().values().stream()
|
||||||
.filter((ea) -> !rbacDef.isRootEntityAlias(ea))
|
.filter((ea) -> !rbacDef.isRootEntityAlias(ea))
|
||||||
.filter((ea) -> ea.fetchSql() != null)
|
.filter((ea) -> ea.fetchSql() != null)
|
||||||
.forEach((ea) -> {
|
.forEach((ea) -> {
|
||||||
plPgSql.writeLn( ea.fetchSql().sql + " into " + entityRefVar(NEW, ea) + ";");
|
plPgSql.writeLn( ea.fetchSql().sql.replace("${ref}", NEW.name()) + " into " + entityRefVar(NEW, ea) + ";");
|
||||||
});
|
});
|
||||||
|
|
||||||
createRolesWithGrantsSql(plPgSql, OWNER);
|
createRolesWithGrantsSql(plPgSql, OWNER);
|
||||||
@ -99,12 +98,7 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
createRolesWithGrantsSql(plPgSql, REFERRER);
|
createRolesWithGrantsSql(plPgSql, REFERRER);
|
||||||
|
|
||||||
plPgSql.writeLn();
|
plPgSql.writeLn();
|
||||||
rbacGrants
|
rbacGrants.forEach(g -> plPgSql.writeLn(generateGrant(g)));
|
||||||
.forEach(g -> plPgSql.writeLn(
|
|
||||||
"call grantRoleToRole(${subRoleRef}, ${superRoleRef});"
|
|
||||||
.replace("${subRoleRef}", roleRef(NEW, g.getSubRoleDef()) )
|
|
||||||
.replace("${superRoleRef}", roleRef(NEW, g.getSuperRoleDef()) ))
|
|
||||||
);
|
|
||||||
|
|
||||||
plPgSql.writeLn("return NEW;");
|
plPgSql.writeLn("return NEW;");
|
||||||
});
|
});
|
||||||
@ -113,11 +107,34 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
plPgSql.writeLn();
|
plPgSql.writeLn();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String generateGrant(RbacView.RbacGrantDefinition grantDef) {
|
||||||
|
return switch (grantDef.grantType()) {
|
||||||
|
case ROLE_TO_USER -> throw new IllegalArgumentException("unexpected grant");
|
||||||
|
case ROLE_TO_ROLE -> "call grantRoleToRole(${subRoleRef}, ${superRoleRef}));"
|
||||||
|
.replace("${subRoleRef}", roleRef(NEW, grantDef.getSubRoleDef()) )
|
||||||
|
.replace("${superRoleRef}", roleRef(NEW, grantDef.getSuperRoleDef()) );
|
||||||
|
case PERM_TO_ROLE -> "call grantPermissionsToRole(${permRef}, ${superRoleRef}));"
|
||||||
|
.replace("${permRef}", permRef(NEW, grantDef.getPermDef()) )
|
||||||
|
.replace("${superRoleRef}", roleRef(NEW, grantDef.getSuperRoleDef()) );
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private String permRef(final PostgresTriggerReference ref, final RbacPermissionDefinition permDef) {
|
||||||
|
return "createPermissions(${entityRef}.uuid, array ['${perm}']"
|
||||||
|
.replace("${entityRef}", rbacDef.isRootEntityAlias(permDef.entityAlias)
|
||||||
|
? ref.name()
|
||||||
|
: "not implemented yet" ) // TODO
|
||||||
|
.replace("${perm}", permDef.permission.permission());
|
||||||
|
}
|
||||||
|
|
||||||
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());
|
||||||
}
|
}
|
||||||
|
|
||||||
private String roleRef(final PostgresTriggerReference rootRefVar, final RbacView.RbacRoleDefinition roleDef) {
|
private String roleRef(final PostgresTriggerReference rootRefVar, final RbacView.RbacRoleDefinition roleDef) {
|
||||||
|
if ( roleDef == null ) {
|
||||||
|
System.out.println("null");
|
||||||
|
}
|
||||||
if ( roleDef.getEntityAlias().isGlobal()) {
|
if ( roleDef.getEntityAlias().isGlobal()) {
|
||||||
return "globalAdmin()";
|
return "globalAdmin()";
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user