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.persistence.HasUuid;
|
||||
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.Stringifyable;
|
||||
import org.hibernate.annotations.GenericGenerator;
|
||||
@ -107,7 +108,7 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
|
||||
|
||||
public static RbacView rbac() {
|
||||
return rbacViewFor("debitor", HsOfficeDebitorEntity.class)
|
||||
.withIdentityView(RbacView.SQL.query("""
|
||||
.withIdentityView(SQL.query("""
|
||||
SELECT debitor.uuid,
|
||||
'D-' || (SELECT partner.partnerNumber
|
||||
FROM hs_office_partner partner
|
||||
@ -117,7 +118,7 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
|
||||
ON debitorRel.relAnchorUuid = partnerRel.relHolderUuid AND partnerRel.relType = 'ACCOUNTING'
|
||||
WHERE debitorRel.uuid = debitor.debitorRelUuid)
|
||||
|| to_char(debitorNumberSuffix, 'fm00')
|
||||
from hs_office_debitor as debitor;
|
||||
from hs_office_debitor as debitor
|
||||
"""))
|
||||
.withUpdatableColumns(
|
||||
"debitorRel",
|
||||
@ -131,11 +132,11 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
|
||||
"defaultPrefix" /* TODO: do we want that updatable? */)
|
||||
.createPermission(custom("new-debitor")).grantedTo("global", ADMIN).pop()
|
||||
|
||||
.importProxyEntity("debitorRel", HsOfficeRelationshipEntity.class,
|
||||
.importRootEntityAliasProxy("debitorRel", HsOfficeRelationshipEntity.class,
|
||||
fetchedBySql("""
|
||||
SELECT *
|
||||
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"))
|
||||
.createPermission(ALL).grantedTo("debitorRel", OWNER).pop()
|
||||
@ -146,7 +147,7 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
|
||||
fetchedBySql("""
|
||||
SELECT *
|
||||
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"))
|
||||
.toRole("refundBankAccount", ADMIN).grantRole("debitorRel", AGENT)
|
||||
@ -156,7 +157,7 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
|
||||
fetchedBySql("""
|
||||
SELECT *
|
||||
FROM hs_office_relationship AS partnerRel
|
||||
WHERE ${debitorRel}.relAnchorUuid = partnerRel.relHolderUuid;
|
||||
WHERE ${debitorRel}.relAnchorUuid = partnerRel.relHolderUuid
|
||||
"""),
|
||||
dependsOnColumn("debitorRelUuid"))
|
||||
.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.hs.office.person.HsOfficePersonEntity;
|
||||
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.Stringifyable;
|
||||
import org.hibernate.annotations.NotFound;
|
||||
@ -15,6 +16,12 @@ import jakarta.persistence.*;
|
||||
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.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;
|
||||
|
||||
@Entity
|
||||
@ -68,4 +75,27 @@ public class HsOfficePartnerEntity implements Stringifyable, HasUuid {
|
||||
public String toShortString() {
|
||||
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) -> {
|
||||
with.owningUser(CREATOR);
|
||||
with.incomingSuperRole(GLOBAL, ADMIN);
|
||||
with.incomingSuperRole("anchorPerson", ADMIN);
|
||||
with.permission(ALL);
|
||||
})
|
||||
.createSubRole(ADMIN, (with) -> {
|
||||
with.incomingSuperRole("anchorPerson", ADMIN);
|
||||
with.permission(EDIT);
|
||||
})
|
||||
.createSubRole(AGENT, (with) -> {
|
||||
|
@ -101,7 +101,7 @@ public class RbacView {
|
||||
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) {
|
||||
if ( rootEntityAliasProxy != null ) {
|
||||
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.debitor.HsOfficeDebitorEntity;
|
||||
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity;
|
||||
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@ -162,6 +163,7 @@ public class RbacViewMermaidFlowchart {
|
||||
public static void main(String[] args) throws IOException {
|
||||
new RbacViewMermaidFlowchart(HsOfficeBankAccountEntity.rbac()).generateToMarkdownFile();
|
||||
new RbacViewMermaidFlowchart(HsOfficeRelationshipEntity.rbac()).generateToMarkdownFile();
|
||||
new RbacViewMermaidFlowchart(HsOfficePartnerEntity.rbac()).generateToMarkdownFile();
|
||||
new RbacViewMermaidFlowchart(HsOfficeDebitorEntity.rbac()).generateToMarkdownFile();
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
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 java.io.IOException;
|
||||
@ -26,19 +28,15 @@ public class RbacViewPostgresGenerator {
|
||||
"""
|
||||
.replace("%{timestamp}", LocalDateTime.now().toString()));
|
||||
|
||||
// generateSqlForRelatedRbacObject();
|
||||
|
||||
new RolesGrantsAndPermissionsGenerator(rbacDef, liqibaseTagPrefix).generateTo(plPgSql);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return plPgSql.toString();
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
final var rbac = HsOfficeRelationshipEntity.rbac();
|
||||
private static void generatePostgres(final RbacView rbac) throws IOException {
|
||||
final Path outputPath = Paths.get("doc", rbac.getRootEntityAlias().simpleName() + ".sql");
|
||||
Files.writeString(
|
||||
outputPath,
|
||||
@ -47,4 +45,10 @@ public class RbacViewPostgresGenerator {
|
||||
|
||||
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.writeLn();
|
||||
rbacDef.getEntityAliases().values().stream()
|
||||
.filter((ea) -> !rbacDef.isRootEntityAlias(ea))
|
||||
.filter((ea) -> ea.fetchSql() != null)
|
||||
.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);
|
||||
@ -99,12 +98,7 @@ class RolesGrantsAndPermissionsGenerator {
|
||||
createRolesWithGrantsSql(plPgSql, REFERRER);
|
||||
|
||||
plPgSql.writeLn();
|
||||
rbacGrants
|
||||
.forEach(g -> plPgSql.writeLn(
|
||||
"call grantRoleToRole(${subRoleRef}, ${superRoleRef});"
|
||||
.replace("${subRoleRef}", roleRef(NEW, g.getSubRoleDef()) )
|
||||
.replace("${superRoleRef}", roleRef(NEW, g.getSuperRoleDef()) ))
|
||||
);
|
||||
rbacGrants.forEach(g -> plPgSql.writeLn(generateGrant(g)));
|
||||
|
||||
plPgSql.writeLn("return NEW;");
|
||||
});
|
||||
@ -113,11 +107,34 @@ class RolesGrantsAndPermissionsGenerator {
|
||||
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) {
|
||||
return withoutRvSuffix(entityClass.getAnnotation(Table.class).name());
|
||||
}
|
||||
|
||||
private String roleRef(final PostgresTriggerReference rootRefVar, final RbacView.RbacRoleDefinition roleDef) {
|
||||
if ( roleDef == null ) {
|
||||
System.out.println("null");
|
||||
}
|
||||
if ( roleDef.getEntityAlias().isGlobal()) {
|
||||
return "globalAdmin()";
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user