merging master aftermath, ImportOfficeData not fully working yet

This commit is contained in:
Michael Hoennig 2024-03-22 17:23:17 +01:00
parent 6e663cf525
commit 37f00a19f0
46 changed files with 1029 additions and 996 deletions

View File

@ -8,7 +8,7 @@ Vor einigen Wochen hatten wir schon einmal darüber geredet, ob wir dieses Gefle
Und nun gehe ich noch einen Schritt weiter: Könnte es nicht auch andersherum sein? Also wenn jemand z.B. SELECT-Recht am Partner hat, dass wir davon ausgehen können, dass derjenige auch die Partner-Personen- und Kontaktdaten sehen darf, und zwar implizit durch seine Partner-SELECT-Permission und ohne dass er explizit Rollen für diese Partner-Personen oder Kontaktdaten inne hat?
Im Halbschlaf kam mir nur die Idee, warum wir nicht einfach die komplexen JPA-Entitäten zwar auf die restricted View setzen, wie bisher, aber für die verknüpften Entitäten auf die direkten (bisher "Raw..." genannt) Entitäten gehen. Dann könnte jemand mit einer Rolle, welche die SELECT-Permission auf die komplexe JPA-Entität (z.B.) Partner inne hat, auch die dazugehörige Relation(ship) ["Relationship" wurde vor kurzem auf kurz "Relation" umbenannt] und die wiederum dazu gehörigen Personen- und Kontaktdaten lesen, ohne dass in einem INSERT- und UPDATE-Trigger der Partner-Entität die ganzen Grants mit den verknüpften Entäten aufgebaut und aktualisiert werden müssen.
Im Halbschlaf kam mir nur die Idee, warum wir nicht einfach die komplexen JPA-Entitäten zwar auf die restricted View setzen, wie bisher, aber für die verknüpften Entitäten auf die direkten (bisher "Raw..." genannt) Entitäten gehen. Dann könnte jemand mit einer Rolle, welche die SELECT-Permission auf die komplexe JPA-Entität (z.B.) Partner inne hat, auch die dazugehörige Relation(ship) ["Relation" wurde vor kurzem auf kurz "Relation" umbenannt] und die wiederum dazu gehörigen Personen- und Kontaktdaten lesen, ohne dass in einem INSERT- und UPDATE-Trigger der Partner-Entität die ganzen Grants mit den verknüpften Entäten aufgebaut und aktualisiert werden müssen.
Beim Debitor ist das nämlich selbst mit Generator die Hölle, zumal eben auch Querverbindungen gegranted werden müssen, z.B. von der Debitor-Person zum Sema-Mandat - jedenfalls wenn man nicht Gefahr laufen wollte, dass jemand mit Admin-Rechten an der Partner-Person (also z.B. ein Repräsentant des Partners) die Sepa-Mandate der Debitoren gar nicht mehr sehen kann. Natürlich bräuchte man immer noch die Agent-Rolle am Partner und Debitor (evtl. repräsentiert durch die jeweils zugehörigen Relation - falls dieser Trick überhaupt noch nötig wäre), sowie ein Grant vom Partner-Agent auf den Debitor-Agent und vom Debitor-Agent auf die Sepa-Mandate-Admins, aber eben ohne filigran die ganzen Neben-Entäten (Personen- und Kontaktdaten von Partner und Debitor sowie Bank-Account) in jedem Trigger berücksichtigen zu müssen. Beim Refund-Bank-Account sogar besonders ätzend, weil der optional ist und dadurch zig "if ...refundBankAccountUuid is not null then ..." im Code enstehen (wenn der auch generiert ist).

View File

@ -8,11 +8,11 @@ Das folgende Schema soll dabei unterstützen, die richtigen Permissions, Rollen
An einigen Stellen ist vom *Initiator* die Rede. Als *Initiator* gilt derjenige User, der die Operation (INSERT oder UPDATE) durchführt bzw. dessen primary assumed Rol. (TODO: bisher gibt es nur assumed roles, das Konzept einer primary assumed Role müsste noch eingeführt werden, derzeit nehmen wir dafür immer den `globalAdmin()`. Bevor Kunden aber selbst Objekte anlegen können, muss das geklärt sein.)
#### Typ Root: Objekte, welche nur eine Spezialisierung bzw. Zusatzdaten für andere Objekte bereitstellen (z.B. Partner für Relationships vom Typ Partner oder Partner Details für Partner)
#### Typ Root: Objekte, welche nur eine Spezialisierung bzw. Zusatzdaten für andere Objekte bereitstellen (z.B. Partner für Relations vom Typ Partner oder Partner Details für Partner)
Objektorientiert gedacht, enthalten solche Objekte die Zusatzdaten einer Subklasse; die Daten im Partner erweitern also eine Relationship vom Typ `partner`.
Objektorientiert gedacht, enthalten solche Objekte die Zusatzdaten einer Subklasse; die Daten im Partner erweitern also eine Relation vom Typ `partner`.
- Dann muss dieses Objekt zeitlich nach dem Objekt erzeugt werden, auf dass es sich bezieht, also z.B. zeitlich nach der Relationship.
- Dann muss dieses Objekt zeitlich nach dem Objekt erzeugt werden, auf dass es sich bezieht, also z.B. zeitlich nach der Relation.
- Es werden Delete (\*), Edit und View Permissions für dieses Objekt erzeugt.
- Es werden **keine** Rollen für dieses Objekt erzeugt.
- Statt eigener Rollen werden die o.g. Permissions passenden Rollen des Hauptobjekts zugewiesen (granted) bzw. aus denen entfernt (revoked).
@ -33,7 +33,7 @@ Objektorientiert gedacht, enthalten solche Objekte die Zusatzdaten einer Subklas
Anmerkung: Der Typ-Begriff *Root* bezieht sich auf die Rolle im fachlichen Datenmodell. Im Bezug auf den Teilgraphen eines fachlichen Kontexts ist dies auch eine Wurzel im Sinne der Graphentheorie. Aber in anderen fachlichen Kontexten können auch diese Objekte von anderen Teilgraphen referenziert werden und werden dann zum inneren Knoten.
#### Typ Aggregator: Objekte, welche weitere Objekte zusammenfassen (z.B. Relationship fasst zwei Persons und einen Contact zusammen)
#### Typ Aggregator: Objekte, welche weitere Objekte zusammenfassen (z.B. Relation fasst zwei Persons und einen Contact zusammen)
Solche Objekte verweisen üblicherweise auf Objekte vom Typ Leaf und werden oft von Objekten des Typs Root referenziert.

View File

@ -5,8 +5,8 @@ import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeDebitors
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeDebitorInsertResource;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeDebitorPatchResource;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeDebitorResource;
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity;
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipRepository;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRepository;
import net.hostsharing.hsadminng.mapper.Mapper;
import org.apache.commons.lang3.Validate;
import org.springframework.beans.factory.annotation.Autowired;
@ -21,7 +21,7 @@ import jakarta.persistence.PersistenceContext;
import java.util.List;
import java.util.UUID;
import static net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipType.ACCOUNTING;
import static net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType.DEBITOR;
@RestController
@ -37,7 +37,7 @@ public class HsOfficeDebitorController implements HsOfficeDebitorsApi {
private HsOfficeDebitorRepository debitorRepo;
@Autowired
private HsOfficeRelationshipRepository relRepo;
private HsOfficeRelationRepository relRepo;
@PersistenceContext
private EntityManager em;
@ -73,15 +73,15 @@ public class HsOfficeDebitorController implements HsOfficeDebitorsApi {
Validate.isTrue(body.getDebitorRel() != null || body.getDebitorRelUuid() != null,
"ERROR: [400] exactly one of debitorRel and debitorRelUuid must be supplied, but found none");
Validate.isTrue(body.getDebitorRel() == null ||
body.getDebitorRel().getRelType() == null || ACCOUNTING.name().equals(body.getDebitorRel().getRelType()),
"ERROR: [400] debitorRel.relType must be '"+ACCOUNTING.name()+"' or null for default");
Validate.isTrue(body.getDebitorRel() == null || body.getDebitorRel().getRelMark() == null,
"ERROR: [400] debitorRel.relMark must be null");
body.getDebitorRel().getType() == null || DEBITOR.name().equals(body.getDebitorRel().getType()),
"ERROR: [400] debitorRel.type must be '"+DEBITOR.name()+"' or null for default");
Validate.isTrue(body.getDebitorRel() == null || body.getDebitorRel().getMark() == null,
"ERROR: [400] debitorRel.mark must be null");
final var entityToSave = mapper.map(body, HsOfficeDebitorEntity.class);
if ( body.getDebitorRel() != null ) {
body.getDebitorRel().setRelType(ACCOUNTING.name());
final var debitorRel = mapper.map(body.getDebitorRel(), HsOfficeRelationshipEntity.class);
body.getDebitorRel().setType(DEBITOR.name());
final var debitorRel = mapper.map(body.getDebitorRel(), HsOfficeRelationEntity.class);
entityToSave.setDebitorRel(relRepo.save(debitorRel));
// FIXME em.flush();
} else {

View File

@ -3,8 +3,8 @@ package net.hostsharing.hsadminng.hs.office.debitor;
import lombok.*;
import net.hostsharing.hsadminng.errors.DisplayName;
import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity;
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity;
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
import net.hostsharing.hsadminng.persistence.HasUuid;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
@ -43,7 +43,7 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
private static Stringify<HsOfficeDebitorEntity> stringify =
stringify(HsOfficeDebitorEntity.class, "debitor")
.withIdProp(HsOfficeDebitorEntity::toShortString)
.withProp(e -> ofNullable(e.getDebitorRel()).map(HsOfficeRelationshipEntity::toShortString).orElse(null))
.withProp(e -> ofNullable(e.getDebitorRel()).map(HsOfficeRelationEntity::toShortString).orElse(null))
.withProp(HsOfficeDebitorEntity::getDefaultPrefix)
.quotedValues(false);
@ -60,11 +60,11 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
(
SELECT DISTINCT partner.uuid
FROM hs_office_partner partner
JOIN hs_office_relationship dRel
ON dRel.uuid = debitorreluuid AND dRel.relType = 'ACCOUNTING'
JOIN hs_office_relationship pRel
ON pRel.uuid = partner.partnerRoleUuid AND pRel.relType = 'PARTNER'
WHERE pRel.relHolderUuid = dRel.relAnchorUuid
JOIN hs_office_relation dRel
ON dRel.uuid = debitorreluuid AND dRel.type = 'DEBITOR'
JOIN hs_office_relation pRel
ON pRel.uuid = partner.partnerRelUuid AND pRel.type = 'PARTNER'
WHERE pRel.holderUuid = dRel.anchorUuid
)
""")
@NotFound(action = NotFoundAction.IGNORE)
@ -75,7 +75,7 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "debitorreluuid", nullable = false)
private HsOfficeRelationshipEntity debitorRel;
private HsOfficeRelationEntity debitorRel;
@Column(name = "billable", nullable = false)
private Boolean billable; // not a primitive because otherwise the default would be false
@ -128,10 +128,10 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
SELECT debitor.uuid AS uuid,
'D-' || (SELECT partner.partnerNumber
FROM hs_office_partner partner
JOIN hs_office_relationship partnerRel
ON partnerRel.uuid = partner.partnerRoleUUid AND partnerRel.relType = 'PARTNER'
JOIN hs_office_relationship debitorRel
ON debitorRel.relAnchorUuid = partnerRel.relHolderUuid AND debitorRel.relType = 'ACCOUNTING'
JOIN hs_office_relation partnerRel
ON partnerRel.uuid = partner.partnerRelUUid AND partnerRel.type = 'PARTNER'
JOIN hs_office_relation debitorRel
ON debitorRel.anchorUuid = partnerRel.holderUuid AND debitorRel.type = 'DEBITOR'
WHERE debitorRel.uuid = debitor.debitorRelUuid)
|| to_char(debitorNumberSuffix, 'fm00') as idName
FROM hs_office_debitor AS debitor
@ -148,11 +148,11 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
"defaultPrefix" /* TODO: do we want that updatable? */)
.toRole("global", ADMIN).grantPermission(INSERT)
.importRootEntityAliasProxy("debitorRel", HsOfficeRelationshipEntity.class,
.importRootEntityAliasProxy("debitorRel", HsOfficeRelationEntity.class,
fetchedBySql("""
SELECT *
FROM hs_office_relationship AS r
WHERE r.relType = 'ACCOUNTING' AND r.uuid = ${REF}.debitorRelUuid
FROM hs_office_relation AS r
WHERE r.type = 'DEBITOR' AND r.uuid = ${REF}.debitorRelUuid
"""),
dependsOnColumn("debitorRelUuid"))
.createPermission(DELETE).grantedTo("debitorRel", OWNER)
@ -171,14 +171,14 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
.toRole("refundBankAccount", ADMIN).grantRole("debitorRel", AGENT)
.toRole("debitorRel", AGENT).grantRole("refundBankAccount", REFERRER)
.importEntityAlias("partnerRel", HsOfficeRelationshipEntity.class,
.importEntityAlias("partnerRel", HsOfficeRelationEntity.class,
dependsOnColumn("debitorRelUuid"),
fetchedBySql("""
SELECT partnerRel.*
FROM hs_office_relationship AS partnerRel
JOIN hs_office_relationship AS debitorRel
ON debitorRel.relType = 'ACCOUNTING' AND debitorRel.relAnchorUuid = partnerRel.relHolderUuid
WHERE partnerRel.relType = 'PARTNER'
FROM hs_office_relation AS partnerRel
JOIN hs_office_relation AS debitorRel
ON debitorRel.type = 'DEBITOR' AND debitorRel.anchorUuid = partnerRel.holderUuid
WHERE partnerRel.type = 'PARTNER'
AND ${REF}.debitorRelUuid = debitorRel.uuid
""")
)

View File

@ -2,7 +2,7 @@ package net.hostsharing.hsadminng.hs.office.debitor;
import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeDebitorPatchResource;
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
import net.hostsharing.hsadminng.mapper.EntityPatcher;
import net.hostsharing.hsadminng.mapper.OptionalFromJson;
@ -25,7 +25,7 @@ class HsOfficeDebitorEntityPatcher implements EntityPatcher<HsOfficeDebitorPatch
public void apply(final HsOfficeDebitorPatchResource resource) {
OptionalFromJson.of(resource.getDebitorRelUuid()).ifPresent(newValue -> {
verifyNotNull(newValue, "debitorRel");
entity.setDebitorRel(em.getReference(HsOfficeRelationshipEntity.class, newValue));
entity.setDebitorRel(em.getReference(HsOfficeRelationEntity.class, newValue));
});
Optional.ofNullable(resource.getBillable()).ifPresent(entity::setBillable);
OptionalFromJson.of(resource.getVatId()).ifPresent(entity::setVatId);

View File

@ -14,8 +14,8 @@ public interface HsOfficeDebitorRepository extends Repository<HsOfficeDebitorEnt
@Query("""
SELECT debitor FROM HsOfficeDebitorEntity debitor
JOIN HsOfficePartnerEntity partner
ON partner.partnerRole.relHolder = debitor.debitorRel.relAnchor
AND partner.partnerRole.relType = 'PARTNER' AND debitor.debitorRel.relType = 'ACCOUNTING'
ON partner.partnerRel.holder = debitor.debitorRel.anchor
AND partner.partnerRel.type = 'PARTNER' AND debitor.debitorRel.type = 'DEBITOR'
WHERE cast(partner.partnerNumber as integer) = :partnerNumber
AND cast(debitor.debitorNumberSuffix as integer) = :debitorNumberSuffix
""")
@ -28,14 +28,14 @@ public interface HsOfficeDebitorRepository extends Repository<HsOfficeDebitorEnt
@Query("""
SELECT debitor FROM HsOfficeDebitorEntity debitor
JOIN HsOfficePartnerEntity partner
ON partner.partnerRole.relHolder = debitor.debitorRel.relAnchor
AND partner.partnerRole.relType = 'PARTNER' AND debitor.debitorRel.relType = 'ACCOUNTING'
ON partner.partnerRel.holder = debitor.debitorRel.anchor
AND partner.partnerRel.type = 'PARTNER' AND debitor.debitorRel.type = 'DEBITOR'
JOIN HsOfficePersonEntity person
ON person.uuid = partner.partnerRole.relHolder.uuid
OR person.uuid = debitor.debitorRel.relHolder.uuid
ON person.uuid = partner.partnerRel.holder.uuid
OR person.uuid = debitor.debitorRel.holder.uuid
JOIN HsOfficeContactEntity contact
ON contact.uuid = debitor.debitorRel.contact.uuid
OR contact.uuid = partner.partnerRole.contact.uuid
OR contact.uuid = partner.partnerRel.contact.uuid
WHERE :name is null
OR partner.details.birthName like concat(cast(:name as text), '%')
OR person.tradeName like concat(cast(:name as text), '%')

View File

@ -4,7 +4,7 @@ import com.vladmihalcea.hibernate.type.range.PostgreSQLRangeType;
import com.vladmihalcea.hibernate.type.range.Range;
import lombok.*;
import net.hostsharing.hsadminng.errors.DisplayName;
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
import net.hostsharing.hsadminng.persistence.HasUuid;
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
@ -128,12 +128,12 @@ public class HsOfficeMembershipEntity implements HasUuid, Stringifyable {
.withRestrictedViewOrderBy(SQL.projection("validity"))
.withUpdatableColumns("validity", "membershipFeeBillable", "reasonForTermination")
.importEntityAlias("partnerRel", HsOfficeRelationshipEntity.class,
.importEntityAlias("partnerRel", HsOfficeRelationEntity.class,
dependsOnColumn("partnerUuid"),
fetchedBySql("""
SELECT r.*
FROM hs_office_partner AS p
JOIN hs_office_relationship AS r ON r.uuid = p.partnerRoleUuid
JOIN hs_office_relation AS r ON r.uuid = p.partnerRelUuid
WHERE p.uuid = ${REF}.partnerUuid
"""))
.toRole("partnerRel", ADMIN).grantPermission(INSERT)

View File

@ -7,11 +7,11 @@ import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficePartners
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficePartnerInsertResource;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficePartnerPatchResource;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficePartnerResource;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficePartnerRoleInsertResource;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficePartnerRelInsertResource;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity;
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipRepository;
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipType;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRepository;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType;
import net.hostsharing.hsadminng.mapper.Mapper;
import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
import org.springframework.beans.factory.annotation.Autowired;
@ -40,7 +40,7 @@ public class HsOfficePartnerController implements HsOfficePartnersApi {
private HsOfficePartnerRepository partnerRepo;
@Autowired
private HsOfficeRelationshipRepository relationshipRepo;
private HsOfficeRelationRepository relationRepo;
@PersistenceContext
private EntityManager em;
@ -139,16 +139,16 @@ public class HsOfficePartnerController implements HsOfficePartnersApi {
private HsOfficePartnerEntity createPartnerEntity(final HsOfficePartnerInsertResource body) {
final var entityToSave = new HsOfficePartnerEntity();
entityToSave.setPartnerNumber(body.getPartnerNumber());
entityToSave.setPartnerRole(persistPartnerRole(body.getPartnerRole()));
entityToSave.setPartnerRel(persistPartnerRel(body.getPartnerRel()));
entityToSave.setDetails(mapper.map(body.getDetails(), HsOfficePartnerDetailsEntity.class));
return entityToSave;
}
private HsOfficeRelationshipEntity persistPartnerRole(final HsOfficePartnerRoleInsertResource resource) {
final var entity = new HsOfficeRelationshipEntity();
entity.setRelType(HsOfficeRelationshipType.PARTNER);
entity.setRelAnchor(ref(HsOfficePersonEntity.class, resource.getRelAnchorUuid()));
entity.setRelHolder(ref(HsOfficePersonEntity.class, resource.getRelHolderUuid()));
private HsOfficeRelationEntity persistPartnerRel(final HsOfficePartnerRelInsertResource resource) {
final var entity = new HsOfficeRelationEntity();
entity.setType(HsOfficeRelationType.PARTNER);
entity.setAnchor(ref(HsOfficePersonEntity.class, resource.getAnchorUuid()));
entity.setHolder(ref(HsOfficePersonEntity.class, resource.getHolderUuid()));
entity.setContact(ref(HsOfficeContactEntity.class, resource.getContactUuid()));
em.persist(entity);
return entity;

View File

@ -9,7 +9,7 @@ import net.hostsharing.hsadminng.errors.DisplayName;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
import net.hostsharing.hsadminng.persistence.HasUuid;
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
import net.hostsharing.hsadminng.stringify.Stringify;
@ -51,12 +51,12 @@ public class HsOfficePartnerEntity implements Stringifyable, HasUuid {
private static Stringify<HsOfficePartnerEntity> stringify = stringify(HsOfficePartnerEntity.class, "partner")
.withIdProp(HsOfficePartnerEntity::toShortString)
.withProp(p -> ofNullable(p.getPartnerRole())
.map(HsOfficeRelationshipEntity::getRelHolder)
.withProp(p -> ofNullable(p.getPartnerRel())
.map(HsOfficeRelationEntity::getHolder)
.map(HsOfficePersonEntity::toShortString)
.orElse(null))
.withProp(p -> ofNullable(p.getPartnerRole())
.map(HsOfficeRelationshipEntity::getContact)
.withProp(p -> ofNullable(p.getPartnerRel())
.map(HsOfficeRelationEntity::getContact)
.map(HsOfficeContactEntity::toShortString)
.orElse(null))
.quotedValues(false);
@ -69,8 +69,8 @@ public class HsOfficePartnerEntity implements Stringifyable, HasUuid {
private Integer partnerNumber;
@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "partnerroleuuid", nullable = false)
private HsOfficeRelationshipEntity partnerRole;
@JoinColumn(name = "partnerReluuid", nullable = false)
private HsOfficeRelationEntity partnerRel;
@ManyToOne(cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.DETACH }, optional = true)
@JoinColumn(name = "detailsuuid")
@ -94,12 +94,12 @@ public class HsOfficePartnerEntity implements Stringifyable, HasUuid {
public static RbacView rbac() {
return rbacViewFor("partner", HsOfficePartnerEntity.class)
.withIdentityView(SQL.projection("'P-' || partnerNumber"))
.withUpdatableColumns("partnerRoleUuid")
.toRole("global", ADMIN).grantPermission(INSERT) // FIXME: global -> partnerRel.relAnchor?
.withUpdatableColumns("partnerRelUuid")
.toRole("global", ADMIN).grantPermission(INSERT) // FIXME: global -> partnerRel.anchor?
.importRootEntityAliasProxy("partnerRel", HsOfficeRelationshipEntity.class,
fetchedBySql("SELECT * FROM hs_office_relationship AS r WHERE r.uuid = ${ref}.partnerRoleUuid"),
dependsOnColumn("partnerRoleUuid"))
.importRootEntityAliasProxy("partnerRel", HsOfficeRelationEntity.class,
fetchedBySql("SELECT * FROM hs_office_relation AS r WHERE r.uuid = ${ref}.partnerRelUuid"),
dependsOnColumn("partnerRelUuid"))
.createPermission(DELETE).grantedTo("partnerRel", ADMIN)
.createPermission(UPDATE).grantedTo("partnerRel", AGENT)
.createPermission(SELECT).grantedTo("partnerRel", TENANT)

View File

@ -1,7 +1,7 @@
package net.hostsharing.hsadminng.hs.office.partner;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficePartnerPatchResource;
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
import net.hostsharing.hsadminng.mapper.EntityPatcher;
import net.hostsharing.hsadminng.mapper.OptionalFromJson;
@ -19,9 +19,9 @@ class HsOfficePartnerEntityPatcher implements EntityPatcher<HsOfficePartnerPatch
@Override
public void apply(final HsOfficePartnerPatchResource resource) {
OptionalFromJson.of(resource.getPartnerRoleUuid()).ifPresent(newValue -> {
verifyNotNull(newValue, "partnerRole");
entity.setPartnerRole(em.getReference(HsOfficeRelationshipEntity.class, newValue));
OptionalFromJson.of(resource.getPartnerRelUuid()).ifPresent(newValue -> {
verifyNotNull(newValue, "partnerRel");
entity.setPartnerRel(em.getReference(HsOfficeRelationEntity.class, newValue));
});
new HsOfficePartnerDetailsEntityPatcher(em, entity.getDetails()).apply(resource.getDetails());

View File

@ -15,9 +15,9 @@ public interface HsOfficePartnerRepository extends Repository<HsOfficePartnerEnt
@Query("""
SELECT partner FROM HsOfficePartnerEntity partner
JOIN HsOfficeRelationshipEntity rel ON rel.uuid = partner.partnerRole.uuid
JOIN HsOfficeRelationEntity rel ON rel.uuid = partner.partnerRel.uuid
JOIN HsOfficeContactEntity contact ON contact.uuid = rel.contact.uuid
JOIN HsOfficePersonEntity person ON person.uuid = rel.relHolder.uuid
JOIN HsOfficePersonEntity person ON person.uuid = rel.holder.uuid
WHERE :name is null
OR partner.details.birthName like concat(cast(:name as text), '%')
OR contact.label like concat(cast(:name as text), '%')

View File

@ -1,4 +1,4 @@
package net.hostsharing.hsadminng.hs.office.relationship;
package net.hostsharing.hsadminng.hs.office.relation;
import lombok.*;
import lombok.experimental.FieldNameConstants;
@ -24,49 +24,49 @@ import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@Entity
@Table(name = "hs_office_relationship_rv")
@Table(name = "hs_office_relation_rv")
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@FieldNameConstants
public class HsOfficeRelationshipEntity implements HasUuid, Stringifyable {
public class HsOfficeRelationEntity implements HasUuid, Stringifyable {
private static Stringify<HsOfficeRelationshipEntity> toString = stringify(HsOfficeRelationshipEntity.class, "rel")
.withProp(Fields.relAnchor, HsOfficeRelationshipEntity::getRelAnchor)
.withProp(Fields.relType, HsOfficeRelationshipEntity::getRelType)
.withProp(Fields.relMark, HsOfficeRelationshipEntity::getRelMark)
.withProp(Fields.relHolder, HsOfficeRelationshipEntity::getRelHolder)
.withProp(Fields.contact, HsOfficeRelationshipEntity::getContact);
private static Stringify<HsOfficeRelationEntity> toString = stringify(HsOfficeRelationEntity.class, "rel")
.withProp(Fields.anchor, HsOfficeRelationEntity::getAnchor)
.withProp(Fields.type, HsOfficeRelationEntity::getType)
.withProp(Fields.mark, HsOfficeRelationEntity::getMark)
.withProp(Fields.holder, HsOfficeRelationEntity::getHolder)
.withProp(Fields.contact, HsOfficeRelationEntity::getContact);
private static Stringify<HsOfficeRelationshipEntity> toShortString = stringify(HsOfficeRelationshipEntity.class, "rel")
.withProp(Fields.relAnchor, HsOfficeRelationshipEntity::getRelAnchor)
.withProp(Fields.relType, HsOfficeRelationshipEntity::getRelType)
.withProp(Fields.relHolder, HsOfficeRelationshipEntity::getRelHolder);
private static Stringify<HsOfficeRelationEntity> toShortString = stringify(HsOfficeRelationEntity.class, "rel")
.withProp(Fields.anchor, HsOfficeRelationEntity::getAnchor)
.withProp(Fields.type, HsOfficeRelationEntity::getType)
.withProp(Fields.holder, HsOfficeRelationEntity::getHolder);
@Id
@GeneratedValue
private UUID uuid;
@ManyToOne
@JoinColumn(name = "relanchoruuid")
private HsOfficePersonEntity relAnchor;
@JoinColumn(name = "anchoruuid")
private HsOfficePersonEntity anchor;
@ManyToOne
@JoinColumn(name = "relholderuuid")
private HsOfficePersonEntity relHolder;
@JoinColumn(name = "holderuuid")
private HsOfficePersonEntity holder;
@ManyToOne
@JoinColumn(name = "contactuuid")
private HsOfficeContactEntity contact;
@Column(name = "reltype")
@Column(name = "type")
@Enumerated(EnumType.STRING)
private HsOfficeRelationshipType relType;
private HsOfficeRelationType type;
@Column(name = "relmark")
private String relMark;
@Column(name = "mark")
private String mark;
@Override
public String toString() {
@ -79,22 +79,22 @@ public class HsOfficeRelationshipEntity implements HasUuid, Stringifyable {
}
public static RbacView rbac() {
return rbacViewFor("relationship", HsOfficeRelationshipEntity.class)
return rbacViewFor("relation", HsOfficeRelationEntity.class)
.withIdentityView(SQL.projection("""
(select idName from hs_office_person_iv p where p.uuid = relAnchorUuid)
|| '-with-' || target.relType || '-'
|| (select idName from hs_office_person_iv p where p.uuid = relHolderUuid)
(select idName from hs_office_person_iv p where p.uuid = anchorUuid)
|| '-with-' || target.type || '-'
|| (select idName from hs_office_person_iv p where p.uuid = holderUuid)
"""))
.withRestrictedViewOrderBy(SQL.expression(
"(select idName from hs_office_person_iv p where p.uuid = target.relHolderUuid)"))
"(select idName from hs_office_person_iv p where p.uuid = target.holderUuid)"))
.withUpdatableColumns("contactUuid")
.importEntityAlias("anchorPerson", HsOfficePersonEntity.class,
dependsOnColumn("relAnchorUuid"),
fetchedBySql("select * from hs_office_person as p where p.uuid = ${REF}.relAnchorUuid")
dependsOnColumn("anchorUuid"),
fetchedBySql("select * from hs_office_person as p where p.uuid = ${REF}.anchorUuid")
)
.importEntityAlias("holderPerson", HsOfficePersonEntity.class,
dependsOnColumn("relHolderUuid"),
fetchedBySql("select * from hs_office_person as p where p.uuid = ${REF}.relHolderUuid")
dependsOnColumn("holderUuid"),
fetchedBySql("select * from hs_office_person as p where p.uuid = ${REF}.holderUuid")
)
.importEntityAlias("contact", HsOfficeContactEntity.class,
dependsOnColumn("contactUuid"),
@ -129,6 +129,6 @@ public class HsOfficeRelationshipEntity implements HasUuid, Stringifyable {
}
public static void main(String[] args) throws IOException {
rbac().generateWithBaseFileName("223-hs-office-relationship-rbac");
rbac().generateWithBaseFileName("223-hs-office-relation-rbac");
}
}

View File

@ -6,7 +6,7 @@ import lombok.*;
import net.hostsharing.hsadminng.errors.DisplayName;
import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity;
import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity;
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
import net.hostsharing.hsadminng.persistence.HasUuid;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.stringify.Stringify;
@ -103,11 +103,11 @@ public class HsOfficeSepaMandateEntity implements Stringifyable, HasUuid {
.withRestrictedViewOrderBy(expression("validity"))
.withUpdatableColumns("reference", "agreement", "validity")
.importEntityAlias("debitorRel", HsOfficeRelationshipEntity.class,
.importEntityAlias("debitorRel", HsOfficeRelationEntity.class,
dependsOnColumn("debitorUuid"),
fetchedBySql("""
SELECT debitorRel.*
FROM hs_office_relationship debitorRel
FROM hs_office_relation debitorRel
JOIN hs_office_debitor debitor ON debitor.debitorRelUuid = debitorRel.uuid
WHERE debitor.uuid = ${REF}.debitorUuid
""")

View File

@ -10,7 +10,7 @@ components:
type: string
format: uuid
debitorRel:
$ref: './hs-office-relationship-schemas.yaml#/components/schemas/HsOfficeRelationship'
$ref: './hs-office-relation-schemas.yaml#/components/schemas/HsOfficeRelation'
debitorNumber:
type: integer
format: int32
@ -76,7 +76,7 @@ components:
type: object
properties:
debitorRel:
$ref: './hs-office-relationship-schemas.yaml#/components/schemas/HsOfficeRelationshipInsert'
$ref: './hs-office-relation-schemas.yaml#/components/schemas/HsOfficeRelationInsert'
debitorRelUuid:
type: string
format: uuid

View File

@ -14,8 +14,8 @@ components:
format: int8
minimum: 10000
maximum: 99999
partnerRole:
$ref: './hs-office-relationship-schemas.yaml#/components/schemas/HsOfficeRelationship'
partnerRel:
$ref: './hs-office-relation-schemas.yaml#/components/schemas/HsOfficeRelation'
details:
$ref: '#/components/schemas/HsOfficePartnerDetails'
@ -50,7 +50,7 @@ components:
HsOfficePartnerPatch:
type: object
properties:
partnerRoleUuid:
partnerRelUuid:
type: string
format: uuid
nullable: true
@ -90,31 +90,31 @@ components:
format: int8
minimum: 10000
maximum: 99999
partnerRole:
$ref: '#/components/schemas/HsOfficePartnerRoleInsert'
partnerRel:
$ref: '#/components/schemas/HsOfficePartnerRelInsert'
details:
$ref: '#/components/schemas/HsOfficePartnerDetailsInsert'
required:
- partnerNumber
- partnerRole
- partnerRel
- details
HsOfficePartnerRoleInsert:
HsOfficePartnerRelInsert:
type: object
nullable: false
properties:
relAnchorUuid:
anchorUuid:
type: string
format: uuid
relHolderUuid:
holderUuid:
type: string
format: uuid
contactUuid:
type: string
format: uuid
required:
- relAnchorUuid
- relHolderUuid
- anchorUuid
- holderUuid
- relContactUuid
HsOfficePartnerDetailsInsert:

View File

@ -1,5 +1,6 @@
--liquibase formatted sql
-- This code generated was by RbacViewPostgresGenerator at 2024-03-11T11:29:11.625353859.
-- This code generated was by RbacViewPostgresGenerator at 2024-03-22T12:01:44.554331877.
-- ============================================================================
--changeset test-package-rbac-OBJECT:1 endDelimiter:--//
@ -33,9 +34,12 @@ declare
begin
call enterTriggerForObjectUuid(NEW.uuid);
SELECT * FROM test_customer c
WHERE c.uuid= NEW.customerUuid
into newCustomer;
INTO newCustomer;
assert newCustomer.uuid is not null, format('newCustomer must not be null for NEW.customerUuid = %s', NEW.customerUuid);
perform createRoleWithGrants(
testPackageOwner(NEW),
@ -75,9 +79,9 @@ create trigger insertTriggerForTestPackage_tg
after insert on test_package
for each row
execute procedure insertTriggerForTestPackage_tf();
--//
-- ============================================================================
--changeset test-package-rbac-update-trigger:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
@ -101,14 +105,18 @@ begin
SELECT * FROM test_customer c
WHERE c.uuid= OLD.customerUuid
into oldCustomer;
INTO oldCustomer;
assert oldCustomer.uuid is not null, format('oldCustomer must not be null for OLD.customerUuid = %s', OLD.customerUuid);
SELECT * FROM test_customer c
WHERE c.uuid= NEW.customerUuid
into newCustomer;
INTO newCustomer;
assert newCustomer.uuid is not null, format('newCustomer must not be null for NEW.customerUuid = %s', NEW.customerUuid);
if NEW.customerUuid <> OLD.customerUuid then
call revokePermissionFromRole(findPermissionId(OLD.uuid, 'INSERT'), testCustomerAdmin(oldCustomer));
call revokePermissionFromRole(getPermissionId(OLD.uuid, 'INSERT'), testCustomerAdmin(oldCustomer));
call revokeRoleFromRole(testPackageOwner(OLD), testCustomerAdmin(oldCustomer));
call grantRoleToRole(testPackageOwner(NEW), testCustomerAdmin(newCustomer));
@ -138,9 +146,9 @@ create trigger updateTriggerForTestPackage_tg
after update on test_package
for each row
execute procedure updateTriggerForTestPackage_tf();
--//
-- ============================================================================
--changeset test-package-rbac-INSERT:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
@ -179,29 +187,43 @@ begin
return NEW;
end; $$;
-- z_... is to put it at the end of after insert triggers, to make sure the roles exist
create trigger z_test_package_test_customer_insert_tg
after insert on test_customer
for each row
execute procedure test_package_test_customer_insert_tf();
/**
Checks if the user or assumed roles are allowed to insert a row to test_package.
Checks if the user or assumed roles are allowed to insert a row to test_package,
where the check is performed by an indirect role.
An indirect role is a role FIXME.
*/
create or replace function test_package_insert_permission_missing_tf()
returns trigger
language plpgsql as $$
begin
raise exception '[403] insert into test_package not allowed for current subjects % (%)',
currentSubjects(), currentSubjectsUuids();
if ( not hasInsertPermission(
( SELECT customer.uuid FROM
(SELECT * FROM test_customer c
WHERE c.uuid= NEW.customerUuid
) AS customer
), 'INSERT', 'test_package') ) then
raise exception
'[403] insert into test_package not allowed for current subjects % (%)',
currentSubjects(), currentSubjectsUuids();
end if;
return NEW;
end; $$;
create trigger test_package_insert_permission_check_tg
before insert on test_package
for each row
when ( not hasInsertPermission(NEW.customerUuid, 'INSERT', 'test_package') )
execute procedure test_package_insert_permission_missing_tf();
--//
-- ============================================================================
--changeset test-package-rbac-IDENTITY-VIEW:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
@ -209,13 +231,15 @@ create trigger test_package_insert_permission_check_tg
call generateRbacIdentityViewFromProjection('test_package', $idName$
name
$idName$);
--//
-- ============================================================================
--changeset test-package-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
call generateRbacRestrictedView('test_package',
'name',
$orderBy$
name
$orderBy$,
$updates$
version = new.version,
customerUuid = new.customerUuid,
@ -223,4 +247,3 @@ call generateRbacRestrictedView('test_package',
$updates$);
--//

View File

@ -1,4 +1,4 @@
### rbac relationship
### rbac relation
This code generated was by RbacViewMermaidFlowchartGenerator at 2024-03-15T17:17:00.854621634.
@ -45,31 +45,31 @@ subgraph contact["`**contact**`"]
end
end
subgraph relationship["`**relationship**`"]
subgraph relation["`**relation**`"]
direction TB
style relationship fill:#dd4901,stroke:#274d6e,stroke-width:8px
style relation fill:#dd4901,stroke:#274d6e,stroke-width:8px
subgraph relationship:roles[ ]
style relationship:roles fill:#dd4901,stroke:white
subgraph relation:roles[ ]
style relation:roles fill:#dd4901,stroke:white
role:relationship:owner[[relationship:owner]]
role:relationship:admin[[relationship:admin]]
role:relationship:agent[[relationship:agent]]
role:relationship:tenant[[relationship:tenant]]
role:relation:owner[[relation:owner]]
role:relation:admin[[relation:admin]]
role:relation:agent[[relation:agent]]
role:relation:tenant[[relation:tenant]]
end
subgraph relationship:permissions[ ]
style relationship:permissions fill:#dd4901,stroke:white
subgraph relation:permissions[ ]
style relation:permissions fill:#dd4901,stroke:white
perm:relationship:DELETE{{relationship:DELETE}}
perm:relationship:UPDATE{{relationship:UPDATE}}
perm:relationship:SELECT{{relationship:SELECT}}
perm:relationship:INSERT{{relationship:INSERT}}
perm:relation:DELETE{{relation:DELETE}}
perm:relation:UPDATE{{relation:UPDATE}}
perm:relation:SELECT{{relation:SELECT}}
perm:relation:INSERT{{relation:INSERT}}
end
end
%% granting roles to users
user:creator ==> role:relationship:owner
user:creator ==> role:relation:owner
%% granting roles to roles
role:global:admin -.-> role:anchorPerson:owner
@ -81,22 +81,22 @@ role:holderPerson:admin -.-> role:holderPerson:referrer
role:global:admin -.-> role:contact:owner
role:contact:owner -.-> role:contact:admin
role:contact:admin -.-> role:contact:referrer
role:global:admin ==> role:relationship:owner
role:relationship:owner ==> role:relationship:admin
role:anchorPerson:admin ==> role:relationship:admin
role:relationship:admin ==> role:relationship:agent
role:holderPerson:admin ==> role:relationship:agent
role:relationship:agent ==> role:relationship:tenant
role:holderPerson:admin ==> role:relationship:tenant
role:contact:admin ==> role:relationship:tenant
role:relationship:tenant ==> role:anchorPerson:referrer
role:relationship:tenant ==> role:holderPerson:referrer
role:relationship:tenant ==> role:contact:referrer
role:global:admin ==> role:relation:owner
role:relation:owner ==> role:relation:admin
role:anchorPerson:admin ==> role:relation:admin
role:relation:admin ==> role:relation:agent
role:holderPerson:admin ==> role:relation:agent
role:relation:agent ==> role:relation:tenant
role:holderPerson:admin ==> role:relation:tenant
role:contact:admin ==> role:relation:tenant
role:relation:tenant ==> role:anchorPerson:referrer
role:relation:tenant ==> role:holderPerson:referrer
role:relation:tenant ==> role:contact:referrer
%% granting permissions to roles
role:relationship:owner ==> perm:relationship:DELETE
role:relationship:admin ==> perm:relationship:UPDATE
role:relationship:tenant ==> perm:relationship:SELECT
role:anchorPerson:admin ==> perm:relationship:INSERT
role:relation:owner ==> perm:relation:DELETE
role:relation:admin ==> perm:relation:UPDATE
role:relation:tenant ==> perm:relation:SELECT
role:anchorPerson:admin ==> perm:relation:INSERT
```

View File

@ -3,29 +3,29 @@
-- ============================================================================
--changeset hs-office-relationship-rbac-OBJECT:1 endDelimiter:--//
--changeset hs-office-relation-rbac-OBJECT:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
call generateRelatedRbacObject('hs_office_relationship');
call generateRelatedRbacObject('hs_office_relation');
--//
-- ============================================================================
--changeset hs-office-relationship-rbac-ROLE-DESCRIPTORS:1 endDelimiter:--//
--changeset hs-office-relation-rbac-ROLE-DESCRIPTORS:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
call generateRbacRoleDescriptors('hsOfficeRelationship', 'hs_office_relationship');
call generateRbacRoleDescriptors('hsOfficeRelation', 'hs_office_relation');
--//
-- ============================================================================
--changeset hs-office-relationship-rbac-insert-trigger:1 endDelimiter:--//
--changeset hs-office-relation-rbac-insert-trigger:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
/*
Creates the roles, grants and permission for the AFTER INSERT TRIGGER.
*/
create or replace procedure buildRbacSystemForHsOfficeRelationship(
NEW hs_office_relationship
create or replace procedure buildRbacSystemForHsOfficeRelation(
NEW hs_office_relation
)
language plpgsql as $$
@ -37,43 +37,43 @@ declare
begin
call enterTriggerForObjectUuid(NEW.uuid);
select * from hs_office_person as p where p.uuid = NEW.relHolderUuid INTO newHolderPerson;
assert newHolderPerson.uuid is not null, format('newHolderPerson must not be null for NEW.relHolderUuid = %s', NEW.relHolderUuid);
select * from hs_office_person as p where p.uuid = NEW.holderUuid INTO newHolderPerson;
assert newHolderPerson.uuid is not null, format('newHolderPerson must not be null for NEW.holderUuid = %s', NEW.holderUuid);
select * from hs_office_person as p where p.uuid = NEW.relAnchorUuid INTO newAnchorPerson;
assert newAnchorPerson.uuid is not null, format('newAnchorPerson must not be null for NEW.relAnchorUuid = %s', NEW.relAnchorUuid);
select * from hs_office_person as p where p.uuid = NEW.anchorUuid INTO newAnchorPerson;
assert newAnchorPerson.uuid is not null, format('newAnchorPerson must not be null for NEW.anchorUuid = %s', NEW.anchorUuid);
select * from hs_office_contact as c where c.uuid = NEW.contactUuid INTO newContact;
assert newContact.uuid is not null, format('newContact must not be null for NEW.contactUuid = %s', NEW.contactUuid);
perform createRoleWithGrants(
hsOfficeRelationshipOwner(NEW),
hsOfficeRelationOwner(NEW),
permissions => array['DELETE'],
incomingSuperRoles => array[globalAdmin()],
userUuids => array[currentUserUuid()]
);
perform createRoleWithGrants(
hsOfficeRelationshipAdmin(NEW),
hsOfficeRelationAdmin(NEW),
permissions => array['UPDATE'],
incomingSuperRoles => array[
hsOfficeRelationshipOwner(NEW),
hsOfficeRelationOwner(NEW),
hsOfficePersonAdmin(newAnchorPerson)]
);
perform createRoleWithGrants(
hsOfficeRelationshipAgent(NEW),
hsOfficeRelationAgent(NEW),
incomingSuperRoles => array[
hsOfficePersonAdmin(newHolderPerson),
hsOfficeRelationshipAdmin(NEW)]
hsOfficeRelationAdmin(NEW)]
);
perform createRoleWithGrants(
hsOfficeRelationshipTenant(NEW),
hsOfficeRelationTenant(NEW),
permissions => array['SELECT'],
incomingSuperRoles => array[
hsOfficeRelationshipAgent(NEW),
hsOfficeRelationAgent(NEW),
hsOfficeContactAdmin(newContact),
hsOfficePersonAdmin(newHolderPerson)],
outgoingSubRoles => array[
@ -86,36 +86,36 @@ begin
end; $$;
/*
AFTER INSERT TRIGGER to create the role+grant structure for a new hs_office_relationship row.
AFTER INSERT TRIGGER to create the role+grant structure for a new hs_office_relation row.
*/
create or replace function insertTriggerForHsOfficeRelationship_tf()
create or replace function insertTriggerForHsOfficeRelation_tf()
returns trigger
language plpgsql
strict as $$
begin
call buildRbacSystemForHsOfficeRelationship(NEW);
call buildRbacSystemForHsOfficeRelation(NEW);
return NEW;
end; $$;
create trigger insertTriggerForHsOfficeRelationship_tg
after insert on hs_office_relationship
create trigger insertTriggerForHsOfficeRelation_tg
after insert on hs_office_relation
for each row
execute procedure insertTriggerForHsOfficeRelationship_tf();
execute procedure insertTriggerForHsOfficeRelation_tf();
--//
-- ============================================================================
--changeset hs-office-relationship-rbac-update-trigger:1 endDelimiter:--//
--changeset hs-office-relation-rbac-update-trigger:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
/*
Called from the AFTER UPDATE TRIGGER to re-wire the grants.
*/
create or replace procedure updateRbacRulesForHsOfficeRelationship(
OLD hs_office_relationship,
NEW hs_office_relationship
create or replace procedure updateRbacRulesForHsOfficeRelation(
OLD hs_office_relation,
NEW hs_office_relation
)
language plpgsql as $$
@ -130,17 +130,17 @@ declare
begin
call enterTriggerForObjectUuid(NEW.uuid);
select * from hs_office_person as p where p.uuid = OLD.relHolderUuid INTO oldHolderPerson;
assert oldHolderPerson.uuid is not null, format('oldHolderPerson must not be null for OLD.relHolderUuid = %s', OLD.relHolderUuid);
select * from hs_office_person as p where p.uuid = OLD.holderUuid INTO oldHolderPerson;
assert oldHolderPerson.uuid is not null, format('oldHolderPerson must not be null for OLD.holderUuid = %s', OLD.holderUuid);
select * from hs_office_person as p where p.uuid = NEW.relHolderUuid INTO newHolderPerson;
assert newHolderPerson.uuid is not null, format('newHolderPerson must not be null for NEW.relHolderUuid = %s', NEW.relHolderUuid);
select * from hs_office_person as p where p.uuid = NEW.holderUuid INTO newHolderPerson;
assert newHolderPerson.uuid is not null, format('newHolderPerson must not be null for NEW.holderUuid = %s', NEW.holderUuid);
select * from hs_office_person as p where p.uuid = OLD.relAnchorUuid INTO oldAnchorPerson;
assert oldAnchorPerson.uuid is not null, format('oldAnchorPerson must not be null for OLD.relAnchorUuid = %s', OLD.relAnchorUuid);
select * from hs_office_person as p where p.uuid = OLD.anchorUuid INTO oldAnchorPerson;
assert oldAnchorPerson.uuid is not null, format('oldAnchorPerson must not be null for OLD.anchorUuid = %s', OLD.anchorUuid);
select * from hs_office_person as p where p.uuid = NEW.relAnchorUuid INTO newAnchorPerson;
assert newAnchorPerson.uuid is not null, format('newAnchorPerson must not be null for NEW.relAnchorUuid = %s', NEW.relAnchorUuid);
select * from hs_office_person as p where p.uuid = NEW.anchorUuid INTO newAnchorPerson;
assert newAnchorPerson.uuid is not null, format('newAnchorPerson must not be null for NEW.anchorUuid = %s', NEW.anchorUuid);
select * from hs_office_contact as c where c.uuid = OLD.contactUuid INTO oldContact;
assert oldContact.uuid is not null, format('oldContact must not be null for OLD.contactUuid = %s', OLD.contactUuid);
@ -151,11 +151,11 @@ begin
if NEW.contactUuid <> OLD.contactUuid then
call revokeRoleFromRole(hsOfficeRelationshipTenant(OLD), hsOfficeContactAdmin(oldContact));
call grantRoleToRole(hsOfficeRelationshipTenant(NEW), hsOfficeContactAdmin(newContact));
call revokeRoleFromRole(hsOfficeRelationTenant(OLD), hsOfficeContactAdmin(oldContact));
call grantRoleToRole(hsOfficeRelationTenant(NEW), hsOfficeContactAdmin(newContact));
call revokeRoleFromRole(hsOfficeContactReferrer(oldContact), hsOfficeRelationshipTenant(OLD));
call grantRoleToRole(hsOfficeContactReferrer(newContact), hsOfficeRelationshipTenant(NEW));
call revokeRoleFromRole(hsOfficeContactReferrer(oldContact), hsOfficeRelationTenant(OLD));
call grantRoleToRole(hsOfficeContactReferrer(newContact), hsOfficeRelationTenant(NEW));
end if;
@ -163,31 +163,31 @@ begin
end; $$;
/*
AFTER INSERT TRIGGER to re-wire the grant structure for a new hs_office_relationship row.
AFTER INSERT TRIGGER to re-wire the grant structure for a new hs_office_relation row.
*/
create or replace function updateTriggerForHsOfficeRelationship_tf()
create or replace function updateTriggerForHsOfficeRelation_tf()
returns trigger
language plpgsql
strict as $$
begin
call updateRbacRulesForHsOfficeRelationship(OLD, NEW);
call updateRbacRulesForHsOfficeRelation(OLD, NEW);
return NEW;
end; $$;
create trigger updateTriggerForHsOfficeRelationship_tg
after update on hs_office_relationship
create trigger updateTriggerForHsOfficeRelation_tg
after update on hs_office_relation
for each row
execute procedure updateTriggerForHsOfficeRelationship_tf();
execute procedure updateTriggerForHsOfficeRelation_tf();
--//
-- ============================================================================
--changeset hs-office-relationship-rbac-INSERT:1 endDelimiter:--//
--changeset hs-office-relation-rbac-INSERT:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
/*
Creates INSERT INTO hs_office_relationship permissions for the related hs_office_person rows.
Creates INSERT INTO hs_office_relation permissions for the related hs_office_person rows.
*/
do language plpgsql $$
declare
@ -195,80 +195,80 @@ do language plpgsql $$
permissionUuid uuid;
roleUuid uuid;
begin
call defineContext('create INSERT INTO hs_office_relationship permissions for the related hs_office_person rows');
call defineContext('create INSERT INTO hs_office_relation permissions for the related hs_office_person rows');
FOR row IN SELECT * FROM hs_office_person
LOOP
roleUuid := findRoleId(hsOfficePersonAdmin(row));
permissionUuid := createPermission(row.uuid, 'INSERT', 'hs_office_relationship');
permissionUuid := createPermission(row.uuid, 'INSERT', 'hs_office_relation');
call grantPermissionToRole(permissionUuid, roleUuid);
END LOOP;
END;
$$;
/**
Adds hs_office_relationship INSERT permission to specified role of new hs_office_person rows.
Adds hs_office_relation INSERT permission to specified role of new hs_office_person rows.
*/
create or replace function hs_office_relationship_hs_office_person_insert_tf()
create or replace function hs_office_relation_hs_office_person_insert_tf()
returns trigger
language plpgsql
strict as $$
begin
call grantPermissionToRole(
createPermission(NEW.uuid, 'INSERT', 'hs_office_relationship'),
createPermission(NEW.uuid, 'INSERT', 'hs_office_relation'),
hsOfficePersonAdmin(NEW));
return NEW;
end; $$;