RBAC Diagram+PostgreSQL Generator #21
@ -58,7 +58,7 @@ public class HsOfficeBankAccountEntity implements HasUuid, Stringifyable {
|
|||||||
|
|
||||||
public static RbacView rbac() {
|
public static RbacView rbac() {
|
||||||
return rbacViewFor("bankAccount", HsOfficeBankAccountEntity.class)
|
return rbacViewFor("bankAccount", HsOfficeBankAccountEntity.class)
|
||||||
.withIdentityView(SQL.query("target.iban || ':' || target.holder"))
|
.withIdentityView(SQL.projection("iban || ':' || holder"))
|
||||||
.withUpdatableColumns("holder", "iban", "bic")
|
.withUpdatableColumns("holder", "iban", "bic")
|
||||||
.createRole(OWNER, (with) -> {
|
.createRole(OWNER, (with) -> {
|
||||||
with.owningUser(CREATOR);
|
with.owningUser(CREATOR);
|
||||||
|
@ -5,6 +5,7 @@ import lombok.experimental.FieldNameConstants;
|
|||||||
import net.hostsharing.hsadminng.errors.DisplayName;
|
import net.hostsharing.hsadminng.errors.DisplayName;
|
||||||
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;
|
||||||
@ -61,7 +62,7 @@ public class HsOfficeContactEntity implements Stringifyable, HasUuid {
|
|||||||
|
|
||||||
public static RbacView rbac() {
|
public static RbacView rbac() {
|
||||||
return rbacViewFor("contact", HsOfficeContactEntity.class)
|
return rbacViewFor("contact", HsOfficeContactEntity.class)
|
||||||
.withIdentityView(RbacView.SQL.query("target.label"))
|
.withIdentityView(SQL.projection("label"))
|
||||||
.withUpdatableColumns("label", "postalAddress", "emailAddresses", "phoneNumbers")
|
.withUpdatableColumns("label", "postalAddress", "emailAddresses", "phoneNumbers")
|
||||||
.createRole(OWNER, (with) -> {
|
.createRole(OWNER, (with) -> {
|
||||||
with.owningUser(CREATOR);
|
with.owningUser(CREATOR);
|
||||||
|
@ -144,22 +144,22 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
|
|||||||
.createPermission(VIEW).grantedTo("debitorRel", TENANT)
|
.createPermission(VIEW).grantedTo("debitorRel", TENANT)
|
||||||
|
|
||||||
.importEntityAlias("refundBankAccount", HsOfficeBankAccountEntity.class,
|
.importEntityAlias("refundBankAccount", HsOfficeBankAccountEntity.class,
|
||||||
fetchedBySql("""
|
dependsOnColumn("bankAccountUuid"), 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"))
|
)
|
||||||
.toRole("refundBankAccount", ADMIN).grantRole("debitorRel", AGENT)
|
.toRole("refundBankAccount", ADMIN).grantRole("debitorRel", AGENT)
|
||||||
.toRole("debitorRel", AGENT).grantRole("refundBankAccount", REFERRER)
|
.toRole("debitorRel", AGENT).grantRole("refundBankAccount", REFERRER)
|
||||||
|
|
||||||
.importEntityAlias("partnerRel", HsOfficeRelationshipEntity.class,
|
.importEntityAlias("partnerRel", HsOfficeRelationshipEntity.class,
|
||||||
fetchedBySql("""
|
dependsOnColumn("debitorRelUuid"), 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"))
|
)
|
||||||
.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)
|
||||||
|
@ -5,6 +5,7 @@ import net.hostsharing.hsadminng.errors.DisplayName;
|
|||||||
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;
|
||||||
|
|
||||||
@ -69,7 +70,7 @@ public class HsOfficePartnerDetailsEntity implements HasUuid, Stringifyable {
|
|||||||
|
|
||||||
public static RbacView rbac() {
|
public static RbacView rbac() {
|
||||||
return rbacViewFor("partnerDetails", HsOfficePartnerDetailsEntity.class)
|
return rbacViewFor("partnerDetails", HsOfficePartnerDetailsEntity.class)
|
||||||
.withIdentityView(RbacView.SQL.query("""
|
.withIdentityView(SQL.query("""
|
||||||
SELECT partner_iv.idName || '-details'
|
SELECT partner_iv.idName || '-details'
|
||||||
FROM hs_office_partner_details AS partnerDetails
|
FROM hs_office_partner_details AS partnerDetails
|
||||||
JOIN hs_office_partner partner ON partner.detailsUuid = partnerDetails.uuid
|
JOIN hs_office_partner partner ON partner.detailsUuid = partnerDetails.uuid
|
||||||
|
@ -7,8 +7,7 @@ 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.RbacView.SQL;
|
||||||
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;
|
||||||
@ -81,11 +80,11 @@ public class HsOfficePartnerEntity implements Stringifyable, HasUuid {
|
|||||||
|
|
||||||
public static RbacView rbac() {
|
public static RbacView rbac() {
|
||||||
return rbacViewFor("partner", HsOfficePartnerEntity.class)
|
return rbacViewFor("partner", HsOfficePartnerEntity.class)
|
||||||
.withIdentityView(RbacView.SQL.query("""
|
.withIdentityView(SQL.query("""
|
||||||
SELECT partner.partnerNumber
|
SELECT partner.partnerNumber
|
||||||
|| ':' || (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 AS partner
|
||||||
"""))
|
"""))
|
||||||
.withUpdatableColumns(
|
.withUpdatableColumns(
|
||||||
"partnerRoleUuid",
|
"partnerRoleUuid",
|
||||||
@ -109,9 +108,6 @@ public class HsOfficePartnerEntity implements Stringifyable, HasUuid {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) throws IOException {
|
public static void main(String[] args) throws IOException {
|
||||||
final RbacView rbac = HsOfficePartnerEntity.rbac();
|
HsOfficePartnerEntity.rbac().generateWithBaseFileName("233-hs-office-partner-rbac");
|
||||||
new RbacViewMermaidFlowchart(rbac).generateToMarkdownFile();
|
|
||||||
new RbacViewPostgresGenerator(rbac).generateToChangeLog("233-hs-office-partner-rbac.sql");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.GLOBAL;
|
|||||||
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.*;
|
||||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.query;
|
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.projection;
|
||||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
|
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;
|
||||||
|
|
||||||
@ -66,7 +66,7 @@ public class HsOfficePersonEntity implements HasUuid, Stringifyable {
|
|||||||
|
|
||||||
public static RbacView rbac() {
|
public static RbacView rbac() {
|
||||||
return rbacViewFor("person", HsOfficePersonEntity.class)
|
return rbacViewFor("person", HsOfficePersonEntity.class)
|
||||||
.withIdentityView(query("concat(target.tradeName, target.familyName, target.givenName)"))
|
.withIdentityView(projection("concat(tradeName, familyName, givenName)"))
|
||||||
.withUpdatableColumns("personType", "tradeName", "givenName", "familyName")
|
.withUpdatableColumns("personType", "tradeName", "givenName", "familyName")
|
||||||
.createRole(OWNER, (with) -> {
|
.createRole(OWNER, (with) -> {
|
||||||
with.permission(ALL);
|
with.permission(ALL);
|
||||||
|
@ -79,21 +79,21 @@ public class HsOfficeRelationshipEntity implements HasUuid, Stringifyable {
|
|||||||
|
|
||||||
public static RbacView rbac() {
|
public static RbacView rbac() {
|
||||||
return rbacViewFor("relationship", HsOfficeRelationshipEntity.class)
|
return rbacViewFor("relationship", HsOfficeRelationshipEntity.class)
|
||||||
.withIdentityView(SQL.query("""
|
.withIdentityView(SQL.projection("""
|
||||||
(select idName from hs_office_person_iv p where p.uuid = target.relAnchorUuid)
|
(select idName from hs_office_person_iv p where p.uuid = relAnchorUuid)
|
||||||
|| '-with-' || target.relType || '-'
|
|| '-with-' || target.relType || '-'
|
||||||
|| (select idName from hs_office_person_iv p where p.uuid = target.relHolderUuid)
|
|| (select idName from hs_office_person_iv p where p.uuid = relHolderUuid)
|
||||||
"""))
|
"""))
|
||||||
.withUpdatableColumns("contactUuid")
|
.withUpdatableColumns("contactUuid")
|
||||||
.importEntityAlias("anchorPerson", HsOfficePersonEntity.class,
|
.importEntityAlias("anchorPerson", HsOfficePersonEntity.class,
|
||||||
fetchedBySql("select * from hs_office_person as p where p.uuid = ${REF}.relAnchorUuid"),
|
dependsOnColumn("relAnchorUuid"), fetchedBySql("select * from hs_office_person as p where p.uuid = ${REF}.relAnchorUuid")
|
||||||
dependsOnColumn("relAnchorUuid"))
|
)
|
||||||
.importEntityAlias("holderPerson", HsOfficePersonEntity.class,
|
.importEntityAlias("holderPerson", HsOfficePersonEntity.class,
|
||||||
fetchedBySql("select * from hs_office_person as p where p.uuid = ${REF}.relHolderUuid"),
|
dependsOnColumn("relHolderUuid"), fetchedBySql("select * from hs_office_person as p where p.uuid = ${REF}.relHolderUuid")
|
||||||
dependsOnColumn("relHolderUuid"))
|
)
|
||||||
.importEntityAlias("contact", HsOfficeContactEntity.class,
|
.importEntityAlias("contact", HsOfficeContactEntity.class,
|
||||||
fetchedBySql("select * from hs_office_contact as c where c.uuid = ${REF}.contactUuid"),
|
dependsOnColumn("contactUuid"), fetchedBySql("select * from hs_office_contact as c where c.uuid = ${REF}.contactUuid")
|
||||||
dependsOnColumn("contactUuid"))
|
)
|
||||||
.createRole(OWNER, (with) -> {
|
.createRole(OWNER, (with) -> {
|
||||||
with.owningUser(CREATOR);
|
with.owningUser(CREATOR);
|
||||||
with.incomingSuperRole(GLOBAL, ADMIN);
|
with.incomingSuperRole(GLOBAL, ADMIN);
|
||||||
|
@ -6,16 +6,26 @@ import lombok.*;
|
|||||||
import net.hostsharing.hsadminng.errors.DisplayName;
|
import net.hostsharing.hsadminng.errors.DisplayName;
|
||||||
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.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;
|
||||||
import org.hibernate.annotations.Type;
|
import org.hibernate.annotations.Type;
|
||||||
|
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
|
import java.io.IOException;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import static net.hostsharing.hsadminng.mapper.PostgresDateRange.*;
|
import static net.hostsharing.hsadminng.mapper.PostgresDateRange.*;
|
||||||
|
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.Permission.*;
|
||||||
|
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.SQL.*;
|
||||||
|
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
|
||||||
@ -84,4 +94,33 @@ public class HsOfficeSepaMandateEntity implements Stringifyable, HasUuid {
|
|||||||
return reference;
|
return reference;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static RbacView rbac() {
|
||||||
|
return rbacViewFor("sepaMandate", HsOfficeSepaMandateEntity.class)
|
||||||
|
.withIdentityView(projection("concat(tradeName, familyName, givenName)"))
|
||||||
|
.withUpdatableColumns("reference", "agreement", "validity")
|
||||||
|
|
||||||
|
.importEntityAlias("debitorRel", HsOfficeRelationshipEntity.class, dependsOnColumn("debitorRelUuid"))
|
||||||
|
.importEntityAlias("bankAccount", HsOfficeBankAccountEntity.class, dependsOnColumn("bankAccountUuid"))
|
||||||
|
|
||||||
|
.createRole(OWNER, (with) -> {
|
||||||
|
with.owningUser(CREATOR);
|
||||||
|
with.incomingSuperRole(GLOBAL, ADMIN);
|
||||||
|
with.outgoingSubRole("bankAccount", REFERRER);
|
||||||
|
with.permission(ALL);
|
||||||
|
})
|
||||||
|
.createSubRole(ADMIN, (with) -> {
|
||||||
|
with.permission(EDIT);
|
||||||
|
})
|
||||||
|
.createSubRole(AGENT, (with) -> {
|
||||||
|
with.outgoingSubRole("debitorRel", AGENT);
|
||||||
|
})
|
||||||
|
.createSubRole(REFERRER, (with) -> {
|
||||||
|
with.incomingSuperRole("debitorRel", AGENT);
|
||||||
|
with.permission(VIEW);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws IOException {
|
||||||
|
HsOfficeSepaMandateEntity.rbac().generateWithBaseFileName("253-hs-office-sepamandate-rbac");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package net.hostsharing.hsadminng.rbac.rbacdef;
|
package net.hostsharing.hsadminng.rbac.rbacdef;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
@ -7,17 +8,21 @@ import lombok.Getter;
|
|||||||
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;
|
||||||
|
|
||||||
|
import jakarta.persistence.Table;
|
||||||
import jakarta.validation.constraints.NotNull;
|
import jakarta.validation.constraints.NotNull;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
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.SQL.autoFetched;
|
||||||
import static org.apache.commons.lang3.StringUtils.uncapitalize;
|
import static org.apache.commons.lang3.StringUtils.uncapitalize;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
public class RbacView {
|
public class RbacView {
|
||||||
|
|
||||||
public static final String GLOBAL = "global";
|
public static final String GLOBAL = "global";
|
||||||
|
public static final String OUTPUT_BASEDIR = "src/main/resources/db/changelog";
|
||||||
|
|
||||||
|
|
||||||
private final EntityAlias rootEntityAlias;
|
private final EntityAlias rootEntityAlias;
|
||||||
|
|
||||||
@ -123,11 +128,18 @@ public class RbacView {
|
|||||||
|
|
||||||
public RbacView importEntityAlias(
|
public RbacView importEntityAlias(
|
||||||
final String aliasName, final Class<? extends HasUuid> entityClass,
|
final String aliasName, final Class<? extends HasUuid> entityClass,
|
||||||
final SQL fetchSql, final Column dependsOnColum) {
|
final Column dependsOnColum, final SQL fetchSql) {
|
||||||
importEntityAliasImpl(aliasName, entityClass, fetchSql, dependsOnColum, false);
|
importEntityAliasImpl(aliasName, entityClass, fetchSql, dependsOnColum, false);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public RbacView importEntityAlias(
|
||||||
|
final String aliasName, final Class<? extends HasUuid> entityClass,
|
||||||
|
final Column dependsOnColum) {
|
||||||
|
importEntityAliasImpl(aliasName, entityClass, autoFetched(), dependsOnColum, false);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
private EntityAlias importEntityAliasImpl(
|
private EntityAlias importEntityAliasImpl(
|
||||||
final String aliasName, final Class<? extends HasUuid> entityClass,
|
final String aliasName, final Class<? extends HasUuid> entityClass,
|
||||||
final SQL fetchSql, final Column dependsOnColum, boolean asSubEntity) {
|
final SQL fetchSql, final Column dependsOnColum, boolean asSubEntity) {
|
||||||
@ -200,6 +212,11 @@ public class RbacView {
|
|||||||
return entityAlias == rootEntityAliasProxy;
|
return entityAlias == rootEntityAliasProxy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void generateWithBaseFileName(final String baseFileName) {
|
||||||
|
new RbacViewMermaidFlowchart(this).generateToMarkdownFile(Path.of(OUTPUT_BASEDIR, baseFileName + "-generated.md"));
|
||||||
|
new RbacViewPostgresGenerator(this).generateToChangeLog(Path.of(OUTPUT_BASEDIR, baseFileName + "-generated.sql"));
|
||||||
|
}
|
||||||
|
|
||||||
public class RbacGrantBuilder {
|
public class RbacGrantBuilder {
|
||||||
|
|
||||||
private final RbacRoleDefinition superRoleDef;
|
private final RbacRoleDefinition superRoleDef;
|
||||||
@ -463,6 +480,18 @@ public class RbacView {
|
|||||||
return entityClass == null;
|
return entityClass == null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SQL fetchSql() {
|
||||||
|
if ( fetchSql == null ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return switch (fetchSql.part) {
|
||||||
|
case SQL_QUERY -> fetchSql;
|
||||||
|
case AUTO_FETCH -> SQL.query("SELECT * FROM " + getRawTableName(entityClass) + " WHERE uuid = ${ref}." + dependsOnColum.column);
|
||||||
|
default -> throw new IllegalStateException("unexpected SQL definition: " + fetchSql);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
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());
|
||||||
}
|
}
|
||||||
@ -474,6 +503,13 @@ public class RbacView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getRawTableName(final Class<?> entityClass) {
|
||||||
|
return withoutRvSuffix(entityClass.getAnnotation(Table.class).name());
|
||||||
|
}
|
||||||
|
public static String withoutRvSuffix(final String tableName) {
|
||||||
|
return tableName.substring(0, tableName.length()-"_rv".length());
|
||||||
|
}
|
||||||
|
|
||||||
public record Role(String roleName) {
|
public record Role(String roleName) {
|
||||||
public static final Role OWNER = new Role("owner");
|
public static final Role OWNER = new Role("owner");
|
||||||
public static final Role ADMIN = new Role("admin");
|
public static final Role ADMIN = new Role("admin");
|
||||||
@ -510,7 +546,7 @@ public class RbacView {
|
|||||||
public static class SQL {
|
public static class SQL {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DSL methid to specify an SQL SELECT expression which fetches the related entity,
|
* DSL method to specify an SQL SELECT expression which fetches the related entity,
|
||||||
* using the reference `${ref}` of the root entity.
|
* using the reference `${ref}` of the root entity.
|
||||||
* `${ref}` is going to be replaced by either `NEW` or `OLD` of the trigger function.
|
* `${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`.
|
* `into ...` will be added with a variable name prefixed with either `new` or `old`.
|
||||||
@ -520,7 +556,18 @@ public class RbacView {
|
|||||||
*/
|
*/
|
||||||
public static SQL fetchedBySql(final String sql) {
|
public static SQL fetchedBySql(final String sql) {
|
||||||
validateExpression(sql);
|
validateExpression(sql);
|
||||||
return new SQL(sql);
|
return new SQL(sql, Part.SQL_QUERY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DSL method to specify that a related entity is to be fetched by a simple SELECT statement
|
||||||
|
* using the raw table from the @Table statement of the entity to fetch
|
||||||
|
* and the dependent column of the root entity.
|
||||||
|
*
|
||||||
|
* @return the wrapped SQL definition object
|
||||||
|
*/
|
||||||
|
public static SQL autoFetched() {
|
||||||
|
return new SQL(null, Part.AUTO_FETCH);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Generic DSL method to specify an SQL SELECT expression.
|
/** Generic DSL method to specify an SQL SELECT expression.
|
||||||
@ -530,13 +577,39 @@ public class RbacView {
|
|||||||
*/
|
*/
|
||||||
public static SQL query(final String sql) {
|
public static SQL query(final String sql) {
|
||||||
validateExpression(sql);
|
validateExpression(sql);
|
||||||
return new SQL(sql);
|
return new SQL(sql, Part.SQL_QUERY);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final String sql;
|
/** Generic DSL method to specify an SQL SELECT expression by just the projection part.
|
||||||
|
*
|
||||||
|
* @param projection an SQL SELECT expression, the list of columns after 'SELECT'
|
||||||
|
* @return the wrapped SQL projection
|
||||||
|
*/
|
||||||
|
public static SQL projection(final String projection) {
|
||||||
|
validateProjection(projection);
|
||||||
|
return new SQL(projection, Part.SQL_PROJECTION);
|
||||||
|
}
|
||||||
|
|
||||||
private SQL(final String sql) {
|
enum Part {
|
||||||
|
SQL_QUERY,
|
||||||
|
AUTO_FETCH, SQL_PROJECTION
|
||||||
|
}
|
||||||
|
|
||||||
|
final String sql;
|
||||||
|
final Part part;
|
||||||
|
|
||||||
|
private SQL(final String sql, final Part part) {
|
||||||
this.sql = sql;
|
this.sql = sql;
|
||||||
|
this.part = part;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void validateProjection(final String projection) {
|
||||||
|
if (projection.toUpperCase().matches("[ \t]*$SELECT[ \t]")) {
|
||||||
|
throw new IllegalArgumentException("SQL projection must not start with 'SELECT': " + projection);
|
||||||
|
}
|
||||||
|
if (projection.matches(";[ \t]*$")) {
|
||||||
|
throw new IllegalArgumentException("SQL projection must not end with ';': " + projection);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void validateExpression(final String sql) {
|
private static void validateExpression(final String sql) {
|
||||||
|
@ -1,13 +1,8 @@
|
|||||||
package net.hostsharing.hsadminng.rbac.rbacdef;
|
package net.hostsharing.hsadminng.rbac.rbacdef;
|
||||||
|
|
||||||
import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity;
|
import lombok.SneakyThrows;
|
||||||
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.relationship.HsOfficeRelationshipEntity;
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.*;
|
import java.nio.file.*;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
@ -46,10 +41,11 @@ public class RbacViewMermaidFlowchart {
|
|||||||
flowchart.writeLn("""
|
flowchart.writeLn("""
|
||||||
subgraph %{aliasName}["`**%{aliasName}**`"]
|
subgraph %{aliasName}["`**%{aliasName}**`"]
|
||||||
direction TB
|
direction TB
|
||||||
style %{aliasName} fill:%{color},stroke:darkblue,stroke-width:8px
|
style %{aliasName} fill:%{fillColor},stroke:%{strokeColor},stroke-width:8px
|
||||||
"""
|
"""
|
||||||
.replace("%{aliasName}", entity.aliasName())
|
.replace("%{aliasName}", entity.aliasName())
|
||||||
.replace("%{color}", color ));
|
.replace("%{fillColor}", color )
|
||||||
|
.replace("%{strokeColor}", HOSTSHARING_DARK_BLUE ));
|
||||||
|
|
||||||
flowchart.indented( () -> {
|
flowchart.indented( () -> {
|
||||||
rbacDef.getEntityAliases().values().stream()
|
rbacDef.getEntityAliases().values().stream()
|
||||||
@ -83,9 +79,9 @@ public class RbacViewMermaidFlowchart {
|
|||||||
flowchart.ensureEmptyLine();
|
flowchart.ensureEmptyLine();
|
||||||
flowchart.writeLn("subgraph " + name + "[ ]\n");
|
flowchart.writeLn("subgraph " + name + "[ ]\n");
|
||||||
flowchart.indented(() -> {
|
flowchart.indented(() -> {
|
||||||
flowchart.writeLn("style %{aliasName} fill: %{color}"
|
flowchart.writeLn("style %{aliasName} fill:%{fillColor},stroke:white"
|
||||||
.replace("%{aliasName}", name)
|
.replace("%{aliasName}", name)
|
||||||
.replace("%{color}", color));
|
.replace("%{fillColor}", color));
|
||||||
flowchart.writeLn();
|
flowchart.writeLn();
|
||||||
flowchart.writeLn(content);
|
flowchart.writeLn(content);
|
||||||
});
|
});
|
||||||
@ -147,8 +143,8 @@ public class RbacViewMermaidFlowchart {
|
|||||||
return flowchart.toString();
|
return flowchart.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void generateToMarkdownFile() throws IOException {
|
@SneakyThrows
|
||||||
final Path path = Paths.get("doc", rbacDef.getRootEntityAlias().simpleName() + ".md");
|
public void generateToMarkdownFile(final Path path) {
|
||||||
Files.writeString(
|
Files.writeString(
|
||||||
path,
|
path,
|
||||||
"""
|
"""
|
||||||
@ -164,12 +160,4 @@ public class RbacViewMermaidFlowchart {
|
|||||||
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
|
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
|
||||||
System.out.println("Markdown-File: " + path.toAbsolutePath());
|
System.out.println("Markdown-File: " + path.toAbsolutePath());
|
||||||
}
|
}
|
||||||
|
|
||||||
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(HsOfficePartnerDetailsEntity.rbac()).generateToMarkdownFile();
|
|
||||||
new RbacViewMermaidFlowchart(HsOfficeDebitorEntity.rbac()).generateToMarkdownFile();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
package net.hostsharing.hsadminng.rbac.rbacdef;
|
package net.hostsharing.hsadminng.rbac.rbacdef;
|
||||||
|
|
||||||
import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity;
|
import lombok.SneakyThrows;
|
||||||
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity;
|
|
||||||
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
@ -37,7 +34,8 @@ public class RbacViewPostgresGenerator {
|
|||||||
return plPgSql.toString();
|
return plPgSql.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void generatePostgres(final RbacView rbac) throws IOException {
|
@SneakyThrows
|
||||||
|
private static void generatePostgres(final RbacView 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,8 +45,8 @@ public class RbacViewPostgresGenerator {
|
|||||||
System.out.println(outputPath.toAbsolutePath());
|
System.out.println(outputPath.toAbsolutePath());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void generateToChangeLog(final String fileName) throws IOException {
|
@SneakyThrows
|
||||||
final Path outputPath = Path.of("src/main/resources/db/changelog", fileName);
|
public void generateToChangeLog(final Path outputPath) {
|
||||||
Files.writeString(
|
Files.writeString(
|
||||||
outputPath,
|
outputPath,
|
||||||
toString(),
|
toString(),
|
||||||
@ -56,10 +54,4 @@ public class RbacViewPostgresGenerator {
|
|||||||
StandardOpenOption.TRUNCATE_EXISTING);
|
StandardOpenOption.TRUNCATE_EXISTING);
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -2,14 +2,15 @@ package net.hostsharing.hsadminng.rbac.rbacdef;
|
|||||||
|
|
||||||
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacPermissionDefinition;
|
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacPermissionDefinition;
|
||||||
|
|
||||||
import jakarta.persistence.Table;
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import static java.util.stream.Collectors.*;
|
import static java.util.stream.Collectors.*;
|
||||||
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.RbacView.RbacGrantDefinition.GrantType.*;
|
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacGrantDefinition.GrantType.*;
|
||||||
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.getRawTableName;
|
||||||
import static org.apache.commons.lang3.StringUtils.capitalize;
|
import static org.apache.commons.lang3.StringUtils.capitalize;
|
||||||
import static org.apache.commons.lang3.StringUtils.uncapitalize;
|
import static org.apache.commons.lang3.StringUtils.uncapitalize;
|
||||||
|
|
||||||
@ -141,10 +142,6 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
return ref.name().toLowerCase() + capitalize(entityAlias.aliasName());
|
return ref.name().toLowerCase() + capitalize(entityAlias.aliasName());
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getRawTableName(final Class<?> entityClass) {
|
|
||||||
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 ) {
|
if ( roleDef == null ) {
|
||||||
System.out.println("null");
|
System.out.println("null");
|
||||||
@ -179,51 +176,13 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
.replace("${simpleVarName)", simpleEntityVarName)
|
.replace("${simpleVarName)", simpleEntityVarName)
|
||||||
.replace("${roleSuffix}", capitalize(role.roleName())));
|
.replace("${roleSuffix}", capitalize(role.roleName())));
|
||||||
|
|
||||||
final var permissionGrantsForRole = findPermissionsGrantsForRole(rbacDef.getRootEntityAlias(), role);
|
generatePermissionsForRole(plPgSql, role);
|
||||||
if (!permissionGrantsForRole.isEmpty()) {
|
|
||||||
final var permissionsForRoleInPlPgSql = permissionGrantsForRole.stream()
|
|
||||||
.map(RbacView.RbacGrantDefinition::getPermDef)
|
|
||||||
.map(RbacPermissionDefinition::getPermission)
|
|
||||||
.map(RbacView.Permission::permission)
|
|
||||||
.map(p -> "'" + p + "'")
|
|
||||||
.collect(joining(", "));
|
|
||||||
plPgSql.indented( () ->
|
|
||||||
plPgSql.writeLn("permissions => array[" + permissionsForRoleInPlPgSql + "],\n"));
|
|
||||||
rbacGrants.removeAll(permissionGrantsForRole);
|
|
||||||
}
|
|
||||||
|
|
||||||
final var grantsToUsers = findGrantsToUserForRole(rbacDef.getRootEntityAlias(), role);
|
generateUserGrantsForRole(plPgSql, role);
|
||||||
if (!grantsToUsers.isEmpty()) {
|
|
||||||
final var grantsToUsersPlPgSql = grantsToUsers.stream()
|
|
||||||
.map(RbacView.RbacGrantDefinition::getUserDef)
|
|
||||||
.map(this::toPlPgSqlReference)
|
|
||||||
.collect(joining(", "));
|
|
||||||
plPgSql.indented(() ->
|
|
||||||
plPgSql.writeLn("userUuids => array[" + grantsToUsersPlPgSql + "],\n"));
|
|
||||||
rbacGrants.removeAll(grantsToUsers);
|
|
||||||
}
|
|
||||||
|
|
||||||
final var incomingGrants = findIncomingSuperRolesForRole(rbacDef.getRootEntityAlias(), role);
|
generateIncomingSuperRolesForRole(plPgSql, role);
|
||||||
if (!incomingGrants.isEmpty()) {
|
|
||||||
final var incomingGrantsInPlPgSql = incomingGrants.stream()
|
|
||||||
.map(RbacView.RbacGrantDefinition::getSuperRoleDef)
|
|
||||||
.map(r -> toPlPgSqlReference(NEW, r))
|
|
||||||
.collect(joining(", "));
|
|
||||||
plPgSql.indented(() ->
|
|
||||||
plPgSql.writeLn("incomingSuperRoles => array[" + incomingGrantsInPlPgSql + "],\n"));
|
|
||||||
rbacGrants.removeAll(incomingGrants);
|
|
||||||
}
|
|
||||||
|
|
||||||
final var outgoingGrants = findOutgoingSuperRolesForRole(rbacDef.getRootEntityAlias(), role);
|
generateOutgoingSubRolesForRole(plPgSql, role);
|
||||||
if (!outgoingGrants.isEmpty()) {
|
|
||||||
final var outgoingGrantsInPlPgSql = outgoingGrants.stream()
|
|
||||||
.map(RbacView.RbacGrantDefinition::getSuperRoleDef)
|
|
||||||
.map(r -> toPlPgSqlReference(NEW, r))
|
|
||||||
.collect(joining(", "));
|
|
||||||
plPgSql.indented(() ->
|
|
||||||
plPgSql.writeLn("outgoingSubRoles => array[" + outgoingGrantsInPlPgSql + "],\n"));
|
|
||||||
rbacGrants.removeAll(outgoingGrants);
|
|
||||||
}
|
|
||||||
|
|
||||||
plPgSql.chopTail(",\n");
|
plPgSql.chopTail(",\n");
|
||||||
plPgSql.writeLn();
|
plPgSql.writeLn();
|
||||||
@ -232,6 +191,66 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
plPgSql.writeLn(");");
|
plPgSql.writeLn(");");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void generateUserGrantsForRole(final StringWriter plPgSql, final RbacView.Role role) {
|
||||||
|
final var grantsToUsers = findGrantsToUserForRole(rbacDef.getRootEntityAlias(), role);
|
||||||
|
if (!grantsToUsers.isEmpty()) {
|
||||||
|
final var arrayElements = grantsToUsers.stream()
|
||||||
|
.map(RbacView.RbacGrantDefinition::getUserDef)
|
||||||
|
.map(this::toPlPgSqlReference)
|
||||||
|
.toList();
|
||||||
|
plPgSql.indented(() ->
|
||||||
|
plPgSql.writeLn("userUuids => array[" + joinArrayElements(arrayElements, 2) + "],\n"));
|
||||||
|
rbacGrants.removeAll(grantsToUsers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void generatePermissionsForRole(final StringWriter plPgSql, final RbacView.Role role) {
|
||||||
|
final var permissionGrantsForRole = findPermissionsGrantsForRole(rbacDef.getRootEntityAlias(), role);
|
||||||
|
if (!permissionGrantsForRole.isEmpty()) {
|
||||||
|
final var arrayElements = permissionGrantsForRole.stream()
|
||||||
|
.map(RbacView.RbacGrantDefinition::getPermDef)
|
||||||
|
.map(RbacPermissionDefinition::getPermission)
|
||||||
|
.map(RbacView.Permission::permission)
|
||||||
|
.map(p -> "'" + p + "'")
|
||||||
|
.toList();
|
||||||
|
plPgSql.indented( () ->
|
||||||
|
plPgSql.writeLn("permissions => array[" + joinArrayElements(arrayElements, 3) + "],\n"));
|
||||||
|
rbacGrants.removeAll(permissionGrantsForRole);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void generateIncomingSuperRolesForRole(final StringWriter plPgSql, final RbacView.Role role) {
|
||||||
|
final var incomingGrants = findIncomingSuperRolesForRole(rbacDef.getRootEntityAlias(), role);
|
||||||
|
if (!incomingGrants.isEmpty()) {
|
||||||
|
final var arraElements = incomingGrants.stream()
|
||||||
|
.map(RbacView.RbacGrantDefinition::getSuperRoleDef)
|
||||||
|
.map(r -> toPlPgSqlReference(NEW, r))
|
||||||
|
.toList();
|
||||||
|
plPgSql.indented(() ->
|
||||||
|
plPgSql.writeLn("incomingSuperRoles => array[" + joinArrayElements(arraElements, 1) + "],\n"));
|
||||||
|
rbacGrants.removeAll(incomingGrants);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void generateOutgoingSubRolesForRole(final StringWriter plPgSql, final RbacView.Role role) {
|
||||||
|
final var outgoingGrants = findOutgoingSuperRolesForRole(rbacDef.getRootEntityAlias(), role);
|
||||||
|
if (!outgoingGrants.isEmpty()) {
|
||||||
|
final var arrayElements = outgoingGrants.stream()
|
||||||
|
.map(RbacView.RbacGrantDefinition::getSubRoleDef)
|
||||||
|
.map(r -> toPlPgSqlReference(NEW, r))
|
||||||
|
.toList();
|
||||||
|
plPgSql.indented(() ->
|
||||||
|
plPgSql.writeLn("outgoingSubRoles => array[" + joinArrayElements(arrayElements, 1) + "],\n"));
|
||||||
|
rbacGrants.removeAll(outgoingGrants);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String joinArrayElements(final List<String> arrayElements, final int singleLineLimit) {
|
||||||
|
return arrayElements.size() <= singleLineLimit
|
||||||
|
? String.join(", ", arrayElements)
|
||||||
|
: arrayElements.stream().collect(joining(",\n\t", "\n\t", ""));
|
||||||
|
}
|
||||||
|
|
||||||
private Set<RbacView.RbacGrantDefinition> findPermissionsGrantsForRole(final RbacView.EntityAlias entityAlias, final RbacView.Role role) {
|
private Set<RbacView.RbacGrantDefinition> findPermissionsGrantsForRole(final RbacView.EntityAlias entityAlias, final RbacView.Role role) {
|
||||||
final var roleDef = rbacDef.findRbacRole(entityAlias, role);
|
final var roleDef = rbacDef.findRbacRole(entityAlias, role);
|
||||||
return rbacGrants.stream()
|
return rbacGrants.stream()
|
||||||
@ -284,10 +303,6 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
plPgSql.writeLn();
|
plPgSql.writeLn();
|
||||||
}
|
}
|
||||||
|
|
||||||
private String withoutRvSuffix(final String tableName) {
|
|
||||||
return tableName.substring(0, tableName.length()-"_rv".length());
|
|
||||||
}
|
|
||||||
|
|
||||||
private String toPlPgSqlReference(final RbacView.RbacUserReference userRef) {
|
private String toPlPgSqlReference(final RbacView.RbacUserReference userRef) {
|
||||||
return switch (userRef.role) {
|
return switch (userRef.role) {
|
||||||
case CREATOR -> "currentUserUuid()";
|
case CREATOR -> "currentUserUuid()";
|
||||||
|
@ -5,6 +5,7 @@ import lombok.Getter;
|
|||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
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.rbac.rbacobject.RbacObject;
|
import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
|
||||||
|
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
@ -39,7 +40,7 @@ public class TestCustomerEntity implements RbacObject {
|
|||||||
|
|
||||||
public static RbacView rbac() {
|
public static RbacView rbac() {
|
||||||
return rbacViewFor("customer", TestCustomerEntity.class)
|
return rbacViewFor("customer", TestCustomerEntity.class)
|
||||||
.withIdentityView(RbacView.SQL.query("target.prefix"))
|
.withIdentityView(SQL.projection("prefix"))
|
||||||
.withUpdatableColumns("reference", "prefix", "adminUserName")
|
.withUpdatableColumns("reference", "prefix", "adminUserName")
|
||||||
.createRole(OWNER, (with) -> {
|
.createRole(OWNER, (with) -> {
|
||||||
with.owningUser(CREATOR);
|
with.owningUser(CREATOR);
|
||||||
|
Loading…
Reference in New Issue
Block a user