merging master aftermath, ImportOfficeData not fully working yet
This commit is contained in:
parent
6e663cf525
commit
37f00a19f0
@ -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).
|
||||
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
""")
|
||||
)
|
||||
|
@ -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);
|
||||
|
@ -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), '%')
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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());
|
||||
|
@ -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), '%')
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
""")
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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$);
|
||||
--//
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
||||
```
|
||||
|
@ -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; $$;
|
||||