move Parter+Debitor person+contact to related Relationsship #20
82
doc/ideas/rbac-schema-f.md
Normal file
82
doc/ideas/rbac-schema-f.md
Normal file
@ -0,0 +1,82 @@
|
||||
*(this is just a scribbled draft, that's why it's still in German)*
|
||||
|
||||
### *Schema-F* für Permissions, Rollen und Grants
|
||||
|
||||
Permissions, Rollen und Grants werden in den INSERT/UPDATE/DELETE-Triggern von Geschäftsobjekten erzeugt und gelöscht. Das Löschen erfolgt meistens automatisch über das zugehörige RbacObject, die INSERT- und UPDATE-Trigger müssen jedoch in *pl/pgsql* ausprogrammiert werden.
|
||||
|
||||
Das folgende Schema soll dabei unterstützen, die richtigen Permissions, Rollen und Grants festzulegen.
|
||||
|
||||
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 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 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 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).
|
||||
- Handelt es sich um Zusatzdaten zum Zwecke der Spezialisierung, dann z.B. so:
|
||||
- Delete (\*) <-- Owner des Hauptobjektes
|
||||
- Edit <-- **Admin** des Hauptobjektes
|
||||
- View <-- Agent des Hauptobjektes
|
||||
- Handelt es sich um Zusatzdaten, für die sich Edit-Rechte delegieren lassen sollen (wie im Falle der Partner-Details eines Partners), dann z.B. so:
|
||||
- Delete (\*) <-- Owner des Hauptobjektes
|
||||
- Edit <-- **Agent** des Hauptobjektes
|
||||
- View <-- Agent des Hauptobjektes
|
||||
- Für die Rollenzuordnung zwischen referenzierten Objekten gilt:
|
||||
- Für Objekte vom Typ Root werden die Rollen des zugehörigen Aggregator-Objektes verwendet.
|
||||
- Gibt es Referenzen auf hierarchisch verbundene Objekte (z.B. Debitor.refundBankAccount) gilt folgende Faustregel:
|
||||
***Nach oben absteigen, nach unten halten oder aufsteigen.*** An einem fachlich übergeordneten Objekt wird also eine niedrigere Rolle (z.B. Debitor-admin -> Partner.agent), einem fachlich untergeordneten Objekt eine gleichwertige Rolle (z.B. Partner.admin -> Debitor.admin) zugewiesen oder sogar aufgestiegen (Debitor.admin -> Package.tenant).
|
||||
- Für Referenzen zwischen Objekten, die nicht hierarchisch zueinander stehen (z.B. Debitor und Bankverbindung), wird auf beiden seiten abgestiegen (also Debitor.admin -> BankAccount.referrer und BankAccount.admin -> Debitor.tenant).
|
||||
|
||||
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. 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.
|
||||
|
||||
- Es werden i.d.R. folgende Rollen für diese Objekte erzeugt:
|
||||
- Owner, Admin, Agent, Tenent(, Guest?)
|
||||
- Es werden Delete (\*), Edit und View Permissions für dieses Objekt erzeugt.
|
||||
- Die Permissions werden den Rollen sinnvoll zugewiesen, z.B.:
|
||||
- Owner -> Delete (\*)
|
||||
- Admin --> Edit
|
||||
- Tenant (oder ggf. Guest) --> View
|
||||
- Außerdem werden folgende Grants erstellt bzw. entzogen:
|
||||
- Initiator --> Owner
|
||||
- Owner --> Admin
|
||||
- Admin --> Referrer
|
||||
- Admins der referenzierten Objekte werden Agent des Aggregators
|
||||
- Tenants des Aggregators werden Referrer der referenzierten Objekte
|
||||
|
||||
### Typ Leaf: Handelt es sich um ein Objekt, welches (außer zur Modellierung separater Permissions) keine Unterobjekte enthält (z.B. Person, Customer)?
|
||||
|
||||
Solche Objekte werden üblicherweise von Objekten des Typs Aggregator, manchmal auch von Objekten des Typs Root, referenziert.
|
||||
|
||||
- Es werden i.d.R. folgende Rollen für diese Objekte erzeugt:
|
||||
- Owner, Admin, Referrer
|
||||
- Es werden Delete (\*), Edit und View Permissions für dieses Objekt erzeugt.
|
||||
- Die Permissions werden den Rollen sinnvoll zugewiesen, z.B.:
|
||||
- Delete (\*) <-- Owner
|
||||
- Edit <-- Admin
|
||||
- View <-- Referrer
|
||||
- Außerdem werden folgende Grants erstellt bzw. entzogen:
|
||||
- Owner --> Admin
|
||||
- Admin --> Referrer
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
|
||||
subgraph partnerDetails
|
||||
direction TB
|
||||
style partnerDetails fill:#eee
|
||||
|
||||
perm:partnerDetails.*{{partnerDetails.*}}
|
||||
role:partnerDetails.edit{{partnerDetails.edit}}
|
||||
role:partnerDetails.view{{partnerDetails.view}}
|
||||
|
||||
|
||||
end
|
||||
```
|
29
doc/ideas/simplified-grant-structure.md
Normal file
29
doc/ideas/simplified-grant-structure.md
Normal file
@ -0,0 +1,29 @@
|
||||
(this is just a scribbled idea, that's why it's still in German)
|
||||
|
||||
Ich habe mal wieder vom RBAC-System geträumt 🙈 Ok, im Halbschlaf darüber nachgedacht trifft es wohl besser. Und jetzt frage ich mich, ob wir viel zu kompliziert gedacht haben.
|
||||
|
||||
Bislang gingen wir ja davon aus, dass, wenn komplexe Entitäten (z.B. Partner) erzeugt werden, wir wir über den INSERT-Trigger den Rollen der verknüpften Entitäten (z.B. den Rollen der Personendaten des Partners) auch Rechte an den komplexeren Entitäten und umgekehrt geben müssen.
|
||||
|
||||
Da die komplexen Entitäten nur mit gewissen verbundenen Entitäten überhaupt sinnvoll nutzbar sind und diese daher über INNSER JOINs mitladen, könnte sonst auch nur jemand diese Entitäten, der auch die SELECT-Permission an den verküpften Entitäten hat.
|
||||
|
||||
Vor einigen Wochen hatten wir schon einmal darüber geredet, ob wir dieses Geflecht wirklich komplett durchplanen müssen, also über mehrere Stufen hinweg, oder ob sehr warscheinlich eh dieselben Leuten an den weiter entfernten Entitäten die nötien Rechte haben, weil dahinter dieselben User stehen. Also z.B. dass gewährleistet ist, dass jemand mit ADMIN-Recht an den Personendaten des Partners auch bis in die SEPA-Mandate eines Debitors hineinsehen kann.
|
||||
|
||||
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) ["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).
|
||||
|
||||
Mit anderen Worten, um als Repräsentant eines Geschäftspartners auf den Bank-Account der Sepa-Mandate sehen zu dürfen, wird derzeut folgende Grant-Kette durchlaufen (bzw. eben noch nicht, weil es noch nicht funktioniert):
|
||||
|
||||
User -> Partner-Holder-Person:Admin -> Partner-Relation:Agent -> Debitor-Relation:Agent -> Sepa-Mandat:Admin -> BankAccount:Admin -> BankAccount:SELECT
|
||||
|
||||
Daraus würde:
|
||||
|
||||
User -> Partner-Relation:Agent -> Debitor-Relation:Agent -> Sepa-Mandat:Admin -> Sepa-Mandat:SELECT*
|
||||
|
||||
(*mit JOIN auf RawBankAccount, also implizitem Leserecht)
|
||||
|
||||
Das klingt zunächst nach nur einer marginalen Vereinfachung, die eigentlich Vereinfachung liegt aber im Erzeugen der Grants in den Triggern, denn da sind zudem noch Partner-Anchor-Person, Debitor-Holder- und Anchor-Person, Partner- und Debitor-Contact sowie der RefundBankAccount zu berücksichtigen. Und genau diese Grants würden großteils wegfallen, und durch implizite Persmissions über die JOINs auf die Raw-Tables ersetzt werden. Den refundBankAccound müssten wir dann, analog zu den Sepa-Mandataten, umgedreht modellieren, da den sonst
|
||||
|
||||
Man könnte das Ganze auch als "Entwicklung der Rechtestruktur für Hosting-Entitäten auf der obersten Ebene" (Manged Webspace, Managed Server, Cloud Server etc.) sehen, denn die hängen alle unter dem Mega-komplexen Debitor.
|
@ -1,6 +1,6 @@
|
||||
## *hsadmin-ng*'s Role-Based-Access-Management (RBAC)
|
||||
|
||||
The requirements of *hsadmin-ng* include table-m row- and column-level-security for read and write access to business-objects.
|
||||
The requirements of *hsadmin-ng* include table-, row- and column-level-security for read and write access to business-objects.
|
||||
More precisely, any access has to be controlled according to given rules depending on the accessing users, their roles and the accessed business-object.
|
||||
Further, roles and business-objects are hierarchical.
|
||||
|
||||
|
@ -33,8 +33,8 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
||||
public class HsOfficeBankAccountEntity implements HasUuid, Stringifyable {
|
||||
|
||||
private static Stringify<HsOfficeBankAccountEntity> toString = stringify(HsOfficeBankAccountEntity.class, "bankAccount")
|
||||
.withIdProp(HsOfficeBankAccountEntity::getIban)
|
||||
.withProp(Fields.holder, HsOfficeBankAccountEntity::getHolder)
|
||||
.withProp(Fields.iban, HsOfficeBankAccountEntity::getIban)
|
||||
.withProp(Fields.bic, HsOfficeBankAccountEntity::getBic);
|
||||
|
||||
@Id
|
||||
@ -59,8 +59,11 @@ public class HsOfficeBankAccountEntity implements HasUuid, Stringifyable {
|
||||
|
||||
public static RbacView rbac() {
|
||||
return rbacViewFor("bankAccount", HsOfficeBankAccountEntity.class)
|
||||
.withIdentityView(SQL.projection("iban || ':' || holder"))
|
||||
.withIdentityView(SQL.projection("iban"))
|
||||
.withUpdatableColumns("holder", "iban", "bic")
|
||||
|
||||
.toRole("global", GUEST).grantPermission(INSERT)
|
||||
hsh-michaelhoennig marked this conversation as resolved
|
||||
|
||||
.createRole(OWNER, (with) -> {
|
||||
with.owningUser(CREATOR);
|
||||
with.incomingSuperRole(GLOBAL, ADMIN);
|
||||
@ -75,6 +78,6 @@ public class HsOfficeBankAccountEntity implements HasUuid, Stringifyable {
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
rbac().generateWithBaseFileName("243-hs-office-bankaccount-rbac-generated");
|
||||
rbac().generateWithBaseFileName("243-hs-office-bankaccount-rbac");
|
||||
}
|
||||
}
|
||||
|
@ -75,10 +75,11 @@ public class HsOfficeContactEntity implements Stringifyable, HasUuid {
|
||||
})
|
||||
.createSubRole(REFERRER, (with) -> {
|
||||
with.permission(SELECT);
|
||||
});
|
||||
})
|
||||
.toRole(GLOBAL, GUEST).grantPermission(INSERT);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
rbac().generateWithBaseFileName("203-hs-office-contact-rbac-generated");
|
||||
rbac().generateWithBaseFileName("203-hs-office-contact-rbac");
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ import org.hibernate.annotations.GenericGenerator;
|
||||
import jakarta.persistence.*;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import static java.util.Optional.ofNullable;
|
||||
@ -28,13 +29,12 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
||||
public class HsOfficeCoopAssetsTransactionEntity implements Stringifyable, HasUuid {
|
||||
|
||||
private static Stringify<HsOfficeCoopAssetsTransactionEntity> stringify = stringify(HsOfficeCoopAssetsTransactionEntity.class)
|
||||
.withProp(HsOfficeCoopAssetsTransactionEntity::getMemberNumber)
|
||||
.withIdProp(HsOfficeCoopAssetsTransactionEntity::getTaggedMemberNumber)
|
||||
.withProp(HsOfficeCoopAssetsTransactionEntity::getValueDate)
|
||||
.withProp(HsOfficeCoopAssetsTransactionEntity::getTransactionType)
|
||||
.withProp(HsOfficeCoopAssetsTransactionEntity::getAssetValue)
|
||||
.withProp(HsOfficeCoopAssetsTransactionEntity::getReference)
|
||||
.withProp(HsOfficeCoopAssetsTransactionEntity::getComment)
|
||||
.withSeparator(", ")
|
||||
.quotedValues(false);
|
||||
|
||||
@Id
|
||||
@ -76,8 +76,8 @@ public class HsOfficeCoopAssetsTransactionEntity implements Stringifyable, HasUu
|
||||
private String comment;
|
||||
|
||||
|
||||
public Integer getMemberNumber() {
|
||||
return ofNullable(membership).map(HsOfficeMembershipEntity::getMemberNumber).orElse(null);
|
||||
public String getTaggedMemberNumber() {
|
||||
return ofNullable(membership).map(HsOfficeMembershipEntity::toShortString).orElse("M-?????");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -87,6 +87,6 @@ public class HsOfficeCoopAssetsTransactionEntity implements Stringifyable, HasUu
|
||||
|
||||
@Override
|
||||
public String toShortString() {
|
||||
return "%s%+1.2f".formatted(getMemberNumber(), assetValue);
|
||||
return "%s:%+1.2f".formatted(getTaggedMemberNumber(), Optional.ofNullable(assetValue).orElse(BigDecimal.ZERO));
|
||||
}
|
||||
}
|
||||
|
@ -31,7 +31,6 @@ public class HsOfficeCoopSharesTransactionEntity implements Stringifyable, HasUu
|
||||
.withProp(HsOfficeCoopSharesTransactionEntity::getShareCount)
|
||||
.withProp(HsOfficeCoopSharesTransactionEntity::getReference)
|
||||
.withProp(HsOfficeCoopSharesTransactionEntity::getComment)
|
||||
.withSeparator(", ")
|
||||
.quotedValues(false);
|
||||
|
||||
@Id
|
||||
|
@ -5,7 +5,11 @@ 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.relation.HsOfficeRelationEntity;
|
||||
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRepository;
|
||||
import net.hostsharing.hsadminng.mapper.Mapper;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.hibernate.Hibernate;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@ -13,10 +17,13 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.persistence.EntityNotFoundException;
|
||||
import jakarta.persistence.PersistenceContext;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import static net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType.DEBITOR;
|
||||
|
||||
@RestController
|
||||
|
||||
public class HsOfficeDebitorController implements HsOfficeDebitorsApi {
|
||||
@ -30,6 +37,9 @@ public class HsOfficeDebitorController implements HsOfficeDebitorsApi {
|
||||
@Autowired
|
||||
private HsOfficeDebitorRepository debitorRepo;
|
||||
|
||||
@Autowired
|
||||
private HsOfficeRelationRepository relRepo;
|
||||
|
||||
@PersistenceContext
|
||||
private EntityManager em;
|
||||
|
||||
@ -53,22 +63,44 @@ public class HsOfficeDebitorController implements HsOfficeDebitorsApi {
|
||||
@Override
|
||||
@Transactional
|
||||
public ResponseEntity<HsOfficeDebitorResource> addDebitor(
|
||||
final String currentUser,
|
||||
final String assumedRoles,
|
||||
final HsOfficeDebitorInsertResource body) {
|
||||
String currentUser,
|
||||
String assumedRoles,
|
||||
HsOfficeDebitorInsertResource body) {
|
||||
|
||||
context.define(currentUser, assumedRoles);
|
||||
|
||||
final var entityToSave = mapper.map(body, HsOfficeDebitorEntity.class);
|
||||
Validate.isTrue(body.getDebitorRel() == null || body.getDebitorRelUuid() == null,
|
||||
"ERROR: [400] exactly one of debitorRel and debitorRelUuid must be supplied, but found both");
|
||||
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().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 saved = debitorRepo.save(entityToSave);
|
||||
final var entityToSave = mapper.map(body, HsOfficeDebitorEntity.class);
|
||||
if ( body.getDebitorRel() != null ) {
|
||||
body.getDebitorRel().setType(DEBITOR.name());
|
||||
final var debitorRel = mapper.map(body.getDebitorRel(), HsOfficeRelationEntity.class);
|
||||
entityToSave.setDebitorRel(relRepo.save(debitorRel));
|
||||
} else {
|
||||
final var debitorRelOptional = relRepo.findByUuid(body.getDebitorRelUuid());
|
||||
debitorRelOptional.ifPresentOrElse(
|
||||
debitorRel -> {entityToSave.setDebitorRel(relRepo.save(debitorRel));},
|
||||
() -> { throw new EntityNotFoundException("ERROR: [400] debitorRelUuid not found: " + body.getDebitorRelUuid());});
|
||||
}
|
||||
|
||||
final var savedEntity = debitorRepo.save(entityToSave);
|
||||
em.flush();
|
||||
em.refresh(savedEntity);
|
||||
|
||||
final var uri =
|
||||
MvcUriComponentsBuilder.fromController(getClass())
|
||||
.path("/api/hs/office/debitors/{id}")
|
||||
.buildAndExpand(saved.getUuid())
|
||||
.buildAndExpand(savedEntity.getUuid())
|
||||
.toUri();
|
||||
final var mapped = mapper.map(saved, HsOfficeDebitorResource.class);
|
||||
final var mapped = mapper.map(savedEntity, HsOfficeDebitorResource.class);
|
||||
return ResponseEntity.created(uri).body(mapped);
|
||||
}
|
||||
|
||||
@ -119,6 +151,7 @@ public class HsOfficeDebitorController implements HsOfficeDebitorsApi {
|
||||
new HsOfficeDebitorEntityPatcher(em, current).apply(body);
|
||||
|
||||
final var saved = debitorRepo.save(current);
|
||||
Hibernate.initialize(saved);
|
||||
final var mapped = mapper.map(saved, HsOfficeDebitorResource.class);
|
||||
return ResponseEntity.ok(mapped);
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ 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.contact.HsOfficeContactEntity;
|
||||
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity;
|
||||
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
|
||||
import net.hostsharing.hsadminng.persistence.HasUuid;
|
||||
@ -12,17 +11,26 @@ import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
|
||||
import net.hostsharing.hsadminng.stringify.Stringify;
|
||||
import net.hostsharing.hsadminng.stringify.Stringifyable;
|
||||
import org.hibernate.annotations.GenericGenerator;
|
||||
import org.hibernate.annotations.JoinFormula;
|
||||
import org.hibernate.annotations.NotFound;
|
||||
import org.hibernate.annotations.NotFoundAction;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import static jakarta.persistence.CascadeType.DETACH;
|
||||
import static jakarta.persistence.CascadeType.MERGE;
|
||||
import static jakarta.persistence.CascadeType.PERSIST;
|
||||
import static jakarta.persistence.CascadeType.REFRESH;
|
||||
import static java.util.Optional.ofNullable;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NULLABLE;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.directlyFetchedByDependsOnColumn;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.fetchedBySql;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
|
||||
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
||||
|
||||
@ -30,7 +38,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
||||
@Table(name = "hs_office_debitor_rv")
|
||||
@Getter
|
||||
@Setter
|
||||
@Builder
|
||||
@Builder(toBuilder = true)
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@DisplayName("Debitor")
|
||||
@ -38,15 +46,11 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
|
||||
|
||||
public static final String DEBITOR_NUMBER_TAG = "D-";
|
||||
|
||||
// TODO: I would rather like to generate something matching this example:
|
||||
// debitor(1234500: Test AG, tes)
|
||||
// maybe remove withSepararator (always use ', ') and add withBusinessIdProp (with ': ' afterwards)?
|
||||
private static Stringify<HsOfficeDebitorEntity> stringify =
|
||||
stringify(HsOfficeDebitorEntity.class, "debitor")
|
||||
.withProp(e -> DEBITOR_NUMBER_TAG + e.getDebitorNumber())
|
||||
.withProp(HsOfficeDebitorEntity::getPartner)
|
||||
.withIdProp(HsOfficeDebitorEntity::toShortString)
|
||||
.withProp(e -> ofNullable(e.getDebitorRel()).map(HsOfficeRelationEntity::toShortString).orElse(null))
|
||||
.withProp(HsOfficeDebitorEntity::getDefaultPrefix)
|
||||
.withSeparator(": ")
|
||||
.quotedValues(false);
|
||||
|
||||
@Id
|
||||
@ -55,15 +59,28 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
|
||||
private UUID uuid;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "partneruuid")
|
||||
@JoinFormula(
|
||||
referencedColumnName = "uuid",
|
||||
value = """
|
||||
(
|
||||
SELECT DISTINCT partner.uuid
|
||||
FROM hs_office_partner_rv partner
|
||||
JOIN hs_office_relation_rv dRel
|
||||
ON dRel.uuid = debitorreluuid AND dRel.type = 'DEBITOR'
|
||||
JOIN hs_office_relation_rv pRel
|
||||
ON pRel.uuid = partner.partnerRelUuid AND pRel.type = 'PARTNER'
|
||||
WHERE pRel.holderUuid = dRel.anchorUuid
|
||||
)
|
||||
""")
|
||||
@NotFound(action = NotFoundAction.IGNORE)
|
||||
private HsOfficePartnerEntity partner;
|
||||
|
||||
@Column(name = "debitornumbersuffix", columnDefinition = "numeric(2)")
|
||||
private Byte debitorNumberSuffix; // TODO maybe rather as a formatted String?
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "billingcontactuuid")
|
||||
private HsOfficeContactEntity billingContact; // TODO: migrate to billingPerson
|
||||
@ManyToOne(cascade = { PERSIST, MERGE, REFRESH, DETACH }, optional = false)
|
||||
@JoinColumn(name = "debitorreluuid", nullable = false)
|
||||
private HsOfficeRelationEntity debitorRel;
|
||||
|
||||
@Column(name = "billable", nullable = false)
|
||||
private Boolean billable; // not a primitive because otherwise the default would be false
|
||||
@ -88,14 +105,16 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
|
||||
private String defaultPrefix;
|
||||
|
||||
private String getDebitorNumberString() {
|
||||
if (partner == null || partner.getPartnerNumber() == null || debitorNumberSuffix == null) {
|
||||
return null;
|
||||
}
|
||||
return partner.getPartnerNumber() + String.format("%02d", debitorNumberSuffix);
|
||||
return ofNullable(partner)
|
||||
.filter(partner -> debitorNumberSuffix != null)
|
||||
.map(HsOfficePartnerEntity::getPartnerNumber)
|
||||
.map(Object::toString)
|
||||
.map(partnerNumber -> partnerNumber + String.format("%02d", debitorNumberSuffix))
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
public Integer getDebitorNumber() {
|
||||
return Optional.ofNullable(getDebitorNumberString()).map(Integer::parseInt).orElse(null);
|
||||
return ofNullable(getDebitorNumberString()).map(Integer::parseInt).orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -111,28 +130,28 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
|
||||
public static RbacView rbac() {
|
||||
return rbacViewFor("debitor", HsOfficeDebitorEntity.class)
|
||||
.withIdentityView(SQL.query("""
|
||||
SELECT debitor.uuid,
|
||||
'D-' || (SELECT partner.partnerNumber
|
||||
FROM hs_office_partner partner
|
||||
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 partnerRel.type = 'DEBITOR'
|
||||
WHERE debitorRel.uuid = debitor.debitorRelUuid)
|
||||
|| to_char(debitorNumberSuffix, 'fm00')
|
||||
from hs_office_debitor as debitor
|
||||
SELECT debitor.uuid AS uuid,
|
||||
'D-' || (SELECT partner.partnerNumber
|
||||
FROM hs_office_partner partner
|
||||
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
|
||||
"""))
|
||||
.withRestrictedViewOrderBy(SQL.projection("defaultPrefix"))
|
||||
.withUpdatableColumns(
|
||||
"debitorRel",
|
||||
"debitorRelUuid",
|
||||
"billable",
|
||||
"debitorUuid",
|
||||
"refundBankAccountUuid",
|
||||
"vatId",
|
||||
"vatCountryCode",
|
||||
"vatBusiness",
|
||||
"vatReverseCharge",
|
||||
"defaultPrefix" /* TODO: do we want that updatable? */)
|
||||
.createPermission(INSERT).grantedTo("global", ADMIN)
|
||||
.toRole("global", ADMIN).grantPermission(INSERT)
|
||||
|
||||
.importRootEntityAliasProxy("debitorRel", HsOfficeRelationEntity.class,
|
||||
directlyFetchedByDependsOnColumn(),
|
||||
@ -149,9 +168,16 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
|
||||
.toRole("debitorRel", AGENT).grantRole("refundBankAccount", REFERRER)
|
||||
|
||||
.importEntityAlias("partnerRel", HsOfficeRelationEntity.class,
|
||||
dependsOnColumn("partnerRelUuid"),
|
||||
directlyFetchedByDependsOnColumn(),
|
||||
NULLABLE)
|
||||
dependsOnColumn("debitorRelUuid"),
|
||||
fetchedBySql("""
|
||||
SELECT ${columns}
|
||||
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
|
||||
"""),
|
||||
NOT_NULL)
|
||||
.toRole("partnerRel", ADMIN).grantRole("debitorRel", ADMIN)
|
||||
.toRole("partnerRel", AGENT).grantRole("debitorRel", AGENT)
|
||||
.toRole("debitorRel", AGENT).grantRole("partnerRel", TENANT)
|
||||
@ -162,6 +188,6 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
rbac().generateWithBaseFileName("273-hs-office-debitor-rbac-generated");
|
||||
rbac().generateWithBaseFileName("273-hs-office-debitor-rbac");
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
package net.hostsharing.hsadminng.hs.office.debitor;
|
||||
|
||||
import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity;
|
||||
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
|
||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeDebitorPatchResource;
|
||||
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
|
||||
import net.hostsharing.hsadminng.mapper.EntityPatcher;
|
||||
import net.hostsharing.hsadminng.mapper.OptionalFromJson;
|
||||
|
||||
@ -23,9 +23,9 @@ class HsOfficeDebitorEntityPatcher implements EntityPatcher<HsOfficeDebitorPatch
|
||||
|
||||
@Override
|
||||
public void apply(final HsOfficeDebitorPatchResource resource) {
|
||||
OptionalFromJson.of(resource.getBillingContactUuid()).ifPresent(newValue -> {
|
||||
verifyNotNull(newValue, "billingContact");
|
||||
entity.setBillingContact(em.getReference(HsOfficeContactEntity.class, newValue));
|
||||
OptionalFromJson.of(resource.getDebitorRelUuid()).ifPresent(newValue -> {
|
||||
verifyNotNull(newValue, "debitorRel");
|
||||
entity.setDebitorRel(em.getReference(HsOfficeRelationEntity.class, newValue));
|
||||
});
|
||||
Optional.ofNullable(resource.getBillable()).ifPresent(entity::setBillable);
|
||||
OptionalFromJson.of(resource.getVatId()).ifPresent(entity::setVatId);
|
||||
|
@ -13,7 +13,10 @@ public interface HsOfficeDebitorRepository extends Repository<HsOfficeDebitorEnt
|
||||
|
||||
@Query("""
|
||||
SELECT debitor FROM HsOfficeDebitorEntity debitor
|
||||
WHERE cast(debitor.partner.partnerNumber as integer) = :partnerNumber
|
||||
JOIN HsOfficePartnerEntity partner
|
||||
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
|
||||
""")
|
||||
List<HsOfficeDebitorEntity> findDebitorByDebitorNumber(int partnerNumber, byte debitorNumberSuffix);
|
||||
@ -24,9 +27,15 @@ public interface HsOfficeDebitorRepository extends Repository<HsOfficeDebitorEnt
|
||||
|
||||
@Query("""
|
||||
SELECT debitor FROM HsOfficeDebitorEntity debitor
|
||||
JOIN HsOfficePartnerEntity partner ON partner.uuid = debitor.partner.uuid
|
||||
JOIN HsOfficePersonEntity person ON person.uuid = partner.person.uuid
|
||||
JOIN HsOfficeContactEntity contact ON contact.uuid = debitor.billingContact.uuid
|
||||
JOIN HsOfficePartnerEntity partner
|
||||
ON partner.partnerRel.holder = debitor.debitorRel.anchor
|
||||
AND partner.partnerRel.type = 'PARTNER' AND debitor.debitorRel.type = 'DEBITOR'
|
||||
JOIN HsOfficePersonEntity person
|
||||
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.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), '%')
|
||||
|
@ -12,8 +12,6 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.persistence.PersistenceContext;
|
||||
import jakarta.validation.Valid;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
@ -32,9 +30,6 @@ public class HsOfficeMembershipController implements HsOfficeMembershipsApi {
|
||||
@Autowired
|
||||
private HsOfficeMembershipRepository membershipRepo;
|
||||
|
||||
@PersistenceContext
|
||||
private EntityManager em;
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public ResponseEntity<List<HsOfficeMembershipResource>> listMemberships(
|
||||
@ -121,7 +116,7 @@ public class HsOfficeMembershipController implements HsOfficeMembershipsApi {
|
||||
|
||||
final var current = membershipRepo.findByUuid(membershipUuid).orElseThrow();
|
||||
|
||||
new HsOfficeMembershipEntityPatcher(em, mapper, current).apply(body);
|
||||
new HsOfficeMembershipEntityPatcher(mapper, current).apply(body);
|
||||
|
||||
final var saved = membershipRepo.save(current);
|
||||
final var mapped = mapper.map(saved, HsOfficeMembershipResource.class, SEPA_MANDATE_ENTITY_TO_RESOURCE_POSTMAPPER);
|
||||
|
@ -4,20 +4,30 @@ 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.debitor.HsOfficeDebitorEntity;
|
||||
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;
|
||||
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
|
||||
import net.hostsharing.hsadminng.stringify.Stringify;
|
||||
import net.hostsharing.hsadminng.stringify.Stringifyable;
|
||||
import org.hibernate.annotations.Fetch;
|
||||
import org.hibernate.annotations.FetchMode;
|
||||
import org.hibernate.annotations.Type;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDate;
|
||||
import java.util.UUID;
|
||||
|
||||
import static net.hostsharing.hsadminng.mapper.PostgresDateRange.*;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.SELECT;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.REFERRER;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.fetchedBySql;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
|
||||
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
||||
|
||||
@Entity
|
||||
@ -35,10 +45,8 @@ public class HsOfficeMembershipEntity implements HasUuid, Stringifyable {
|
||||
private static Stringify<HsOfficeMembershipEntity> stringify = stringify(HsOfficeMembershipEntity.class)
|
||||
.withProp(e -> MEMBER_NUMBER_TAG + e.getMemberNumber())
|
||||
.withProp(e -> e.getPartner().toShortString())
|
||||
.withProp(e -> e.getMainDebitor().toShortString())
|
||||
.withProp(e -> e.getValidity().asString())
|
||||
.withProp(HsOfficeMembershipEntity::getReasonForTermination)
|
||||
.withSeparator(", ")
|
||||
.quotedValues(false);
|
||||
|
||||
@Id
|
||||
@ -49,11 +57,6 @@ public class HsOfficeMembershipEntity implements HasUuid, Stringifyable {
|
||||
@JoinColumn(name = "partneruuid")
|
||||
private HsOfficePartnerEntity partner;
|
||||
|
||||
@ManyToOne
|
||||
@Fetch(FetchMode.JOIN)
|
||||
@JoinColumn(name = "maindebitoruuid")
|
||||
private HsOfficeDebitorEntity mainDebitor;
|
||||
|
||||
@Column(name = "membernumbersuffix", length = 2)
|
||||
private String memberNumberSuffix;
|
||||
|
||||
@ -114,4 +117,45 @@ public class HsOfficeMembershipEntity implements HasUuid, Stringifyable {
|
||||
setReasonForTermination(HsOfficeReasonForTermination.NONE);
|
||||
}
|
||||
}
|
||||
|
||||
public static RbacView rbac() {
|
||||
return rbacViewFor("membership", HsOfficeMembershipEntity.class)
|
||||
.withIdentityView(SQL.query("""
|
||||
SELECT m.uuid AS uuid,
|
||||
'M-' || p.partnerNumber || m.memberNumberSuffix as idName
|
||||
FROM hs_office_membership AS m
|
||||
JOIN hs_office_partner AS p ON p.uuid = m.partnerUuid
|
||||
"""))
|
||||
.withRestrictedViewOrderBy(SQL.projection("validity"))
|
||||
.withUpdatableColumns("validity", "membershipFeeBillable", "reasonForTermination")
|
||||
|
||||
.importEntityAlias("partnerRel", HsOfficeRelationEntity.class,
|
||||
dependsOnColumn("partnerUuid"),
|
||||
fetchedBySql("""
|
||||
SELECT ${columns}
|
||||
FROM hs_office_partner AS partner
|
||||
JOIN hs_office_relation AS partnerRel ON partnerRel.uuid = partner.partnerRelUuid
|
||||
WHERE partner.uuid = ${REF}.partnerUuid
|
||||
"""),
|
||||
NOT_NULL)
|
||||
.toRole("global", ADMIN).grantPermission(INSERT)
|
||||
|
||||
.createRole(OWNER, (with) -> {
|
||||
with.owningUser(CREATOR);
|
||||
with.incomingSuperRole("partnerRel", ADMIN);
|
||||
with.permission(DELETE);
|
||||
})
|
||||
.createSubRole(ADMIN, (with) -> {
|
||||
with.incomingSuperRole("partnerRel", AGENT);
|
||||
with.permission(UPDATE);
|
||||
})
|
||||
.createSubRole(REFERRER, (with) -> {
|
||||
with.outgoingSubRole("partnerRel", TENANT);
|
||||
with.permission(SELECT);
|
||||
});
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
rbac().generateWithBaseFileName("303-hs-office-membership-rbac");
|
||||
}
|
||||
}
|
||||
|
@ -1,37 +1,26 @@
|
||||
package net.hostsharing.hsadminng.hs.office.membership;
|
||||
|
||||
import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity;
|
||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeMembershipPatchResource;
|
||||
import net.hostsharing.hsadminng.mapper.EntityPatcher;
|
||||
import net.hostsharing.hsadminng.mapper.Mapper;
|
||||
import net.hostsharing.hsadminng.mapper.OptionalFromJson;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public class HsOfficeMembershipEntityPatcher implements EntityPatcher<HsOfficeMembershipPatchResource> {
|
||||
|
||||
private final EntityManager em;
|
||||
private final Mapper mapper;
|
||||
private final HsOfficeMembershipEntity entity;
|
||||
|
||||
public HsOfficeMembershipEntityPatcher(
|
||||
final EntityManager em,
|
||||
final Mapper mapper,
|
||||
final HsOfficeMembershipEntity entity) {
|
||||
this.em = em;
|
||||
this.mapper = mapper;
|
||||
this.entity = entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(final HsOfficeMembershipPatchResource resource) {
|
||||
OptionalFromJson.of(resource.getMainDebitorUuid())
|
||||
.ifPresent(newValue -> {
|
||||
verifyNotNull(newValue, "debitor");
|
||||
entity.setMainDebitor(em.getReference(HsOfficeDebitorEntity.class, newValue));
|
||||
});
|
||||
OptionalFromJson.of(resource.getValidTo()).ifPresent(
|
||||
entity::setValidTo);
|
||||
Optional.ofNullable(resource.getReasonForTermination())
|
||||
@ -40,10 +29,4 @@ public class HsOfficeMembershipEntityPatcher implements EntityPatcher<HsOfficeMe
|
||||
OptionalFromJson.of(resource.getMembershipFeeBillable()).ifPresent(
|
||||
entity::setMembershipFeeBillable);
|
||||
}
|
||||
|
||||
private void verifyNotNull(final UUID newValue, final String propertyName) {
|
||||
if (newValue == null) {
|
||||
throw new IllegalArgumentException("property '" + propertyName + "' must not be null");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -110,9 +110,7 @@ public class HsOfficePartnerController implements HsOfficePartnersApi {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
if (partnerRepo.deleteByUuid(partnerUuid) != 1 ||
|
||||
// TODO: move to after delete trigger in partner
|
||||
relationRepo.deleteByUuid(partnerToDelete.get().getPartnerRel().getUuid()) != 1 ) {
|
||||
if (partnerRepo.deleteByUuid(partnerUuid) != 1) {
|
||||
return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
|
||||
}
|
||||
|
||||
@ -142,8 +140,6 @@ public class HsOfficePartnerController implements HsOfficePartnersApi {
|
||||
final var entityToSave = new HsOfficePartnerEntity();
|
||||
entityToSave.setPartnerNumber(body.getPartnerNumber());
|
||||
entityToSave.setPartnerRel(persistPartnerRel(body.getPartnerRel()));
|
||||
entityToSave.setContact(ref(HsOfficeContactEntity.class, body.getContactUuid()));
|
||||
entityToSave.setPerson(ref(HsOfficePersonEntity.class, body.getPersonUuid()));
|
||||
entityToSave.setDetails(mapper.map(body.getDetails(), HsOfficePartnerDetailsEntity.class));
|
||||
return entityToSave;
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ package net.hostsharing.hsadminng.hs.office.partner;
|
||||
|
||||
import lombok.*;
|
||||
import net.hostsharing.hsadminng.errors.DisplayName;
|
||||
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;
|
||||
@ -14,10 +13,8 @@ import java.io.IOException;
|
||||
import java.time.LocalDate;
|
||||
import java.util.UUID;
|
||||
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.fetchedBySql;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
|
||||
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
||||
|
||||
@ -40,7 +37,6 @@ public class HsOfficePartnerDetailsEntity implements HasUuid, Stringifyable {
|
||||
.withProp(HsOfficePartnerDetailsEntity::getBirthday)
|
||||
.withProp(HsOfficePartnerDetailsEntity::getBirthName)
|
||||
.withProp(HsOfficePartnerDetailsEntity::getDateOfDeath)
|
||||
.withSeparator(", ")
|
||||
.quotedValues(false);
|
||||
|
||||
@Id
|
||||
@ -72,11 +68,12 @@ public class HsOfficePartnerDetailsEntity implements HasUuid, Stringifyable {
|
||||
public static RbacView rbac() {
|
||||
return rbacViewFor("partnerDetails", HsOfficePartnerDetailsEntity.class)
|
||||
.withIdentityView(SQL.query("""
|
||||
SELECT partner_iv.idName || '-details'
|
||||
SELECT partnerDetails.uuid as uuid, partner_iv.idName || '-details' as idName
|
||||
FROM hs_office_partner_details AS partnerDetails
|
||||
JOIN hs_office_partner partner ON partner.detailsUuid = partnerDetails.uuid
|
||||
JOIN hs_office_partner_iv partner_iv ON partner_iv.uuid = partner.uuid
|
||||
"""))
|
||||
.withRestrictedViewOrderBy(SQL.expression("uuid"))
|
||||
.withUpdatableColumns(
|
||||
"registrationOffice",
|
||||
"registrationNumber",
|
||||
@ -84,17 +81,7 @@ public class HsOfficePartnerDetailsEntity implements HasUuid, Stringifyable {
|
||||
"birthName",
|
||||
"birthday",
|
||||
"dateOfDeath")
|
||||
.createPermission(INSERT).grantedTo("global", ADMIN)
|
||||
|
||||
.importRootEntityAliasProxy("partnerRel", HsOfficeRelationEntity.class,
|
||||
fetchedBySql("""
|
||||
SELECT ${columns}
|
||||
FROM hs_office_relation AS partnerRel
|
||||
JOIN hs_office_partner AS partner
|
||||
ON partner.detailsUuid = ${ref}.uuid
|
||||
WHERE partnerRel.uuid = partner.partnerRelUuid
|
||||
"""),
|
||||
dependsOnColumn("partnerRelUuid"))
|
||||
.toRole("global", ADMIN).grantPermission(INSERT)
|
||||
|
||||
// The grants are defined in HsOfficePartnerEntity.rbac()
|
||||
// because they have to be changed when its partnerRel changes,
|
||||
@ -103,6 +90,6 @@ public class HsOfficePartnerDetailsEntity implements HasUuid, Stringifyable {
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
rbac().generateWithBaseFileName("234-hs-office-partner-details-rbac-generated");
|
||||
rbac().generateWithBaseFileName("234-hs-office-partner-details-rbac");
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,14 @@
|
||||
package net.hostsharing.hsadminng.hs.office.partner;
|
||||
|
||||
import lombok.*;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import net.hostsharing.hsadminng.errors.DisplayName;
|
||||
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
|
||||
import net.hostsharing.hsadminng.persistence.HasUuid;
|
||||
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
|
||||
import net.hostsharing.hsadminng.persistence.HasUuid;
|
||||
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
|
||||
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
|
||||
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
|
||||
@ -13,17 +17,24 @@ import net.hostsharing.hsadminng.stringify.Stringifyable;
|
||||
import org.hibernate.annotations.NotFound;
|
||||
import org.hibernate.annotations.NotFoundAction;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.Table;
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import static jakarta.persistence.CascadeType.*;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.SELECT;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.directlyFetchedByDependsOnColumn;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
|
||||
import static java.util.Optional.ofNullable;
|
||||
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
||||
|
||||
@Entity
|
||||
@ -36,10 +47,18 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
||||
@DisplayName("Partner")
|
||||
public class HsOfficePartnerEntity implements Stringifyable, HasUuid {
|
||||
|
||||
public static final String PARTNER_NUMBER_TAG = "P-";
|
||||
|
||||
private static Stringify<HsOfficePartnerEntity> stringify = stringify(HsOfficePartnerEntity.class, "partner")
|
||||
.withProp(HsOfficePartnerEntity::getPerson)
|
||||
.withProp(HsOfficePartnerEntity::getContact)
|
||||
.withSeparator(": ")
|
||||
.withIdProp(HsOfficePartnerEntity::toShortString)
|
||||
.withProp(p -> ofNullable(p.getPartnerRel())
|
||||
.map(HsOfficeRelationEntity::getHolder)
|
||||
.map(HsOfficePersonEntity::toShortString)
|
||||
.orElse(null))
|
||||
.withProp(p -> ofNullable(p.getPartnerRel())
|
||||
.map(HsOfficeRelationEntity::getContact)
|
||||
.map(HsOfficeContactEntity::toShortString)
|
||||
.orElse(null))
|
||||
.quotedValues(false);
|
||||
|
||||
@Id
|
||||
@ -49,25 +68,19 @@ public class HsOfficePartnerEntity implements Stringifyable, HasUuid {
|
||||
@Column(name = "partnernumber", columnDefinition = "numeric(5) not null")
|
||||
private Integer partnerNumber;
|
||||
|
||||
@ManyToOne
|
||||
@ManyToOne(cascade = { PERSIST, MERGE, REFRESH, DETACH }, optional = false)
|
||||
@JoinColumn(name = "partnerreluuid", nullable = false)
|
||||
private HsOfficeRelationEntity partnerRel;
|
||||
|
||||
// TODO: remove, is replaced by partnerRel
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "personuuid", nullable = false)
|
||||
private HsOfficePersonEntity person;
|
||||
|
||||
// TODO: remove, is replaced by partnerRel
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "contactuuid", nullable = false)
|
||||
private HsOfficeContactEntity contact;
|
||||
|
||||
@ManyToOne(cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.DETACH }, optional = true)
|
||||
@ManyToOne(cascade = { PERSIST, MERGE, REFRESH, DETACH }, optional = true)
|
||||
@JoinColumn(name = "detailsuuid")
|
||||
@NotFound(action = NotFoundAction.IGNORE)
|
||||
private HsOfficePartnerDetailsEntity details;
|
||||
|
||||
public String getTaggedPartnerNumber() {
|
||||
return PARTNER_NUMBER_TAG + partnerNumber;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return stringify.apply(this);
|
||||
@ -75,22 +88,14 @@ public class HsOfficePartnerEntity implements Stringifyable, HasUuid {
|
||||
|
||||
@Override
|
||||
public String toShortString() {
|
||||
return Optional.ofNullable(person).map(HsOfficePersonEntity::toShortString).orElse("<person=null>");
|
||||
return getTaggedPartnerNumber();
|
||||
}
|
||||
|
||||
public static RbacView rbac() {
|
||||
return rbacViewFor("partner", HsOfficePartnerEntity.class)
|
||||
.withIdentityView(SQL.query("""
|
||||
SELECT partner.partnerNumber
|
||||
|| ':' || (SELECT idName FROM hs_office_person_iv p WHERE p.uuid = partner.personUuid)
|
||||
|| '-' || (SELECT idName FROM hs_office_contact_iv c WHERE c.uuid = partner.contactUuid)
|
||||
FROM hs_office_partner AS partner
|
||||
"""))
|
||||
.withUpdatableColumns(
|
||||
"partnerRelUuid",
|
||||
"personUuid",
|
||||
"contactUuid")
|
||||
.createPermission(INSERT).grantedTo("global", ADMIN)
|
||||
.withIdentityView(SQL.projection("'P-' || partnerNumber"))
|
||||
.withUpdatableColumns("partnerRelUuid")
|
||||
.toRole("global", ADMIN).grantPermission(INSERT)
|
||||
|
||||
.importRootEntityAliasProxy("partnerRel", HsOfficeRelationEntity.class,
|
||||
directlyFetchedByDependsOnColumn(),
|
||||
@ -108,6 +113,6 @@ public class HsOfficePartnerEntity implements Stringifyable, HasUuid {
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
rbac().generateWithBaseFileName("233-hs-office-partner-rbac-generated");
|
||||
rbac().generateWithBaseFileName("233-hs-office-partner-rbac");
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,11 @@
|
||||
package net.hostsharing.hsadminng.hs.office.partner;
|
||||
|
||||
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
|
||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficePartnerPatchResource;
|
||||
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
|
||||
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
|
||||
import net.hostsharing.hsadminng.mapper.EntityPatcher;
|
||||
import net.hostsharing.hsadminng.mapper.OptionalFromJson;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
import java.util.UUID;
|
||||
|
||||
class HsOfficePartnerEntityPatcher implements EntityPatcher<HsOfficePartnerPatchResource> {
|
||||
private final EntityManager em;
|
||||
@ -21,19 +19,15 @@ class HsOfficePartnerEntityPatcher implements EntityPatcher<HsOfficePartnerPatch
|
||||
|
||||
@Override
|
||||
public void apply(final HsOfficePartnerPatchResource resource) {
|
||||
OptionalFromJson.of(resource.getContactUuid()).ifPresent(newValue -> {
|
||||
verifyNotNull(newValue, "contact");
|
||||
entity.setContact(em.getReference(HsOfficeContactEntity.class, newValue));
|
||||
});
|
||||
OptionalFromJson.of(resource.getPersonUuid()).ifPresent(newValue -> {
|
||||
verifyNotNull(newValue, "person");
|
||||
entity.setPerson(em.getReference(HsOfficePersonEntity.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());
|
||||
}
|
||||
|
||||
private void verifyNotNull(final UUID newValue, final String propertyName) {
|
||||
private void verifyNotNull(final Object newValue, final String propertyName) {
|
||||
if (newValue == null) {
|
||||
throw new IllegalArgumentException("property '" + propertyName + "' must not be null");
|
||||
}
|
||||
|
@ -11,10 +11,13 @@ public interface HsOfficePartnerRepository extends Repository<HsOfficePartnerEnt
|
||||
|
||||
Optional<HsOfficePartnerEntity> findByUuid(UUID id);
|
||||
|
||||
List<HsOfficePartnerEntity> findAll(); // TODO: move to a repo in test sources
|
||||
|
||||
@Query("""
|
||||
SELECT partner FROM HsOfficePartnerEntity partner
|
||||
JOIN HsOfficeContactEntity contact ON contact.uuid = partner.contact.uuid
|
||||
JOIN HsOfficePersonEntity person ON person.uuid = partner.person.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.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), '%')
|
||||
|
@ -69,6 +69,8 @@ public class HsOfficePersonEntity implements HasUuid, Stringifyable {
|
||||
return rbacViewFor("person", HsOfficePersonEntity.class)
|
||||
.withIdentityView(SQL.projection("concat(tradeName, familyName, givenName)"))
|
||||
.withUpdatableColumns("personType", "tradeName", "givenName", "familyName")
|
||||
.toRole("global", GUEST).grantPermission(INSERT)
|
||||
|
||||
.createRole(OWNER, (with) -> {
|
||||
with.permission(DELETE);
|
||||
with.owningUser(CREATOR);
|
||||
@ -84,6 +86,6 @@ public class HsOfficePersonEntity implements HasUuid, Stringifyable {
|
||||
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
rbac().generateWithBaseFileName("213-hs-office-person-rbac-generated");
|
||||
rbac().generateWithBaseFileName("213-hs-office-person-rbac");
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ import java.util.UUID;
|
||||
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.GLOBAL;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NULLABLE;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
|
||||
@ -92,22 +92,26 @@ public class HsOfficeRelationEntity implements HasUuid, Stringifyable {
|
||||
.importEntityAlias("anchorPerson", HsOfficePersonEntity.class,
|
||||
dependsOnColumn("anchorUuid"),
|
||||
directlyFetchedByDependsOnColumn(),
|
||||
NULLABLE)
|
||||
NOT_NULL)
|
||||
.importEntityAlias("holderPerson", HsOfficePersonEntity.class,
|
||||
dependsOnColumn("holderUuid"),
|
||||
directlyFetchedByDependsOnColumn(),
|
||||
NULLABLE)
|
||||
NOT_NULL)
|
||||
.importEntityAlias("contact", HsOfficeContactEntity.class,
|
||||
dependsOnColumn("contactUuid"),
|
||||
directlyFetchedByDependsOnColumn(),
|
||||
NULLABLE)
|
||||
NOT_NULL)
|
||||
.createRole(OWNER, (with) -> {
|
||||
with.owningUser(CREATOR);
|
||||
with.incomingSuperRole(GLOBAL, ADMIN);
|
||||
// TODO: if type=REPRESENTATIIVE
|
||||
// with.incomingSuperRole("holderPerson", ADMIN);
|
||||
with.permission(DELETE);
|
||||
})
|
||||
.createSubRole(ADMIN, (with) -> {
|
||||
with.incomingSuperRole("anchorPerson", ADMIN);
|
||||
// TODO: if type=REPRESENTATIIVE
|
||||
// with.outgoingSuperRole("anchorPerson", OWNER);
|
||||
with.permission(UPDATE);
|
||||
})
|
||||
.createSubRole(AGENT, (with) -> {
|
||||
@ -120,10 +124,12 @@ public class HsOfficeRelationEntity implements HasUuid, Stringifyable {
|
||||
with.outgoingSubRole("holderPerson", REFERRER);
|
||||
with.outgoingSubRole("contact", REFERRER);
|
||||
with.permission(SELECT);
|
||||
});
|
||||
})
|
||||
|
||||
.toRole("anchorPerson", ADMIN).grantPermission(INSERT);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
rbac().generateWithBaseFileName("223-hs-office-relation-rbac-generated");
|
||||
rbac().generateWithBaseFileName("223-hs-office-relation-rbac");
|
||||
}
|
||||
}
|
||||
|
@ -132,6 +132,7 @@ public class HsOfficeSepaMandateController implements HsOfficeSepaMandatesApi {
|
||||
if (entity.getValidity().hasUpperBound()) {
|
||||
resource.setValidTo(entity.getValidity().upper().minusDays(1));
|
||||
}
|
||||
resource.getDebitor().setDebitorNumber(entity.getDebitor().getDebitorNumber());
|
||||
};
|
||||
|
||||
final BiConsumer<HsOfficeSepaMandateInsertResource, HsOfficeSepaMandateEntity> SEPA_MANDATE_RESOURCE_TO_ENTITY_POSTMAPPER = (resource, entity) -> {
|
||||
|
@ -21,6 +21,7 @@ import java.util.UUID;
|
||||
import static net.hostsharing.hsadminng.mapper.PostgresDateRange.*;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.GLOBAL;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
|
||||
@ -43,7 +44,6 @@ public class HsOfficeSepaMandateEntity implements Stringifyable, HasUuid {
|
||||
.withProp(HsOfficeSepaMandateEntity::getReference)
|
||||
.withProp(HsOfficeSepaMandateEntity::getAgreement)
|
||||
.withProp(e -> e.getValidity().asString())
|
||||
.withSeparator(", ")
|
||||
.quotedValues(false);
|
||||
|
||||
@Id
|
||||
@ -96,11 +96,27 @@ public class HsOfficeSepaMandateEntity implements Stringifyable, HasUuid {
|
||||
|
||||
public static RbacView rbac() {
|
||||
return rbacViewFor("sepaMandate", HsOfficeSepaMandateEntity.class)
|
||||
.withIdentityView(projection("concat(tradeName, familyName, givenName)"))
|
||||
.withIdentityView(query("""
|
||||
select sm.uuid as uuid, ba.iban || '-' || sm.validity as idName
|
||||
from hs_office_sepamandate sm
|
||||
join hs_office_bankaccount ba on ba.uuid = sm.bankAccountUuid
|
||||
"""))
|
||||
.withRestrictedViewOrderBy(expression("validity"))
|
||||
.withUpdatableColumns("reference", "agreement", "validity")
|
||||
|
||||
.importEntityAlias("debitorRel", HsOfficeRelationEntity.class, dependsOnColumn("debitorRelUuid"))
|
||||
.importEntityAlias("bankAccount", HsOfficeBankAccountEntity.class, dependsOnColumn("bankAccountUuid"))
|
||||
.importEntityAlias("debitorRel", HsOfficeRelationEntity.class,
|
||||
dependsOnColumn("debitorUuid"),
|
||||
fetchedBySql("""
|
||||
SELECT ${columns}
|
||||
FROM hs_office_relation debitorRel
|
||||
JOIN hs_office_debitor debitor ON debitor.debitorRelUuid = debitorRel.uuid
|
||||
WHERE debitor.uuid = ${REF}.debitorUuid
|
||||
"""),
|
||||
NOT_NULL)
|
||||
.importEntityAlias("bankAccount", HsOfficeBankAccountEntity.class,
|
||||
dependsOnColumn("bankAccountUuid"),
|
||||
directlyFetchedByDependsOnColumn(),
|
||||
NOT_NULL)
|
||||
|
||||
.createRole(OWNER, (with) -> {
|
||||
with.owningUser(CREATOR);
|
||||
@ -119,10 +135,12 @@ public class HsOfficeSepaMandateEntity implements Stringifyable, HasUuid {
|
||||
with.incomingSuperRole("debitorRel", AGENT);
|
||||
with.outgoingSubRole("debitorRel", TENANT);
|
||||
with.permission(SELECT);
|
||||
});
|
||||
})
|
||||
|
||||
.toRole("debitorRel", ADMIN).grantPermission(INSERT);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
rbac().generateWithBaseFileName("253-hs-office-sepamandate-rbac-generated");
|
||||
rbac().generateWithBaseFileName("253-hs-office-sepamandate-rbac");
|
||||
}
|
||||
}
|
||||
|
@ -47,16 +47,14 @@ public class InsertTriggerGenerator {
|
||||
do language plpgsql $$
|
||||
declare
|
||||
row ${rawSuperTableName};
|
||||
permissionUuid uuid;
|
||||
roleUuid uuid;
|
||||
begin
|
||||
call defineContext('create INSERT INTO ${rawSubTableName} permissions for the related ${rawSuperTableName} rows');
|
||||
|
||||
FOR row IN SELECT * FROM ${rawSuperTableName}
|
||||
LOOP
|
||||
roleUuid := findRoleId(${rawSuperRoleDescriptor});
|
||||
permissionUuid := createPermission(row.uuid, 'INSERT', '${rawSubTableName}');
|
||||
call grantPermissionToRole(permissionUuid, roleUuid);
|
||||
call grantPermissionToRole(
|
||||
createPermission(row.uuid, 'INSERT', '${rawSubTableName}'),
|
||||
${rawSuperRoleDescriptor});
|
||||
END LOOP;
|
||||
END;
|
||||
$$;
|
||||
|
@ -16,6 +16,7 @@ public final class Stringify<B> {
|
||||
|
||||
private final Class<B> clazz;
|
||||
private final String name;
|
||||
private Function<B, ?> idProp;
|
||||
private final List<Property<B>> props = new ArrayList<>();
|
||||
private String separator = ", ";
|
||||
private Boolean quotedValues = null;
|
||||
@ -42,6 +43,11 @@ public final class Stringify<B> {
|
||||
}
|
||||
}
|
||||
|
||||
public Stringify<B> withIdProp(final Function<B, ?> getter) {
|
||||
idProp = getter;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Stringify<B> withProp(final String propName, final Function<B, ?> getter) {
|
||||
props.add(new Property<>(propName, getter));
|
||||
return this;
|
||||
@ -64,7 +70,9 @@ public final class Stringify<B> {
|
||||
})
|
||||
.map(propVal -> propName(propVal, "=") + optionallyQuoted(propVal))
|
||||
.collect(Collectors.joining(separator));
|
||||
return name + "(" + propValues + ")";
|
||||
return idProp != null
|
||||
? name + "(" + idProp.apply(object) + ": " + propValues + ")"
|
||||
: name + "(" + propValues + ")";
|
||||
}
|
||||
|
||||
public Stringify<B> withSeparator(final String separator) {
|
||||
|
@ -9,6 +9,8 @@ components:
|
||||
uuid:
|
||||
type: string
|
||||
format: uuid
|
||||
debitorRel:
|
||||
$ref: './hs-office-relation-schemas.yaml#/components/schemas/HsOfficeRelation'
|
||||
debitorNumber:
|
||||
type: integer
|
||||
format: int32
|
||||
@ -21,8 +23,6 @@ components:
|
||||
maximum: 99
|
||||
partner:
|
||||
$ref: './hs-office-partner-schemas.yaml#/components/schemas/HsOfficePartner'
|
||||
billingContact:
|
||||
$ref: './hs-office-contact-schemas.yaml#/components/schemas/HsOfficeContact'
|
||||
billable:
|
||||
type: boolean
|
||||
vatId:
|
||||
@ -43,7 +43,7 @@ components:
|
||||
HsOfficeDebitorPatch:
|
||||
type: object
|
||||
properties:
|
||||
billingContactUuid:
|
||||
debitorRelUuid:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
@ -75,14 +75,11 @@ components:
|
||||
HsOfficeDebitorInsert:
|
||||
type: object
|
||||
properties:
|
||||
partnerUuid:
|
||||
debitorRel:
|
||||
$ref: './hs-office-relation-schemas.yaml#/components/schemas/HsOfficeRelationInsert'
|
||||
debitorRelUuid:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: false
|
||||
billingContactUuid:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: false
|
||||
debitorNumberSuffix:
|
||||
type: integer
|
||||
format: int8
|
||||
@ -105,9 +102,7 @@ components:
|
||||
defaultPrefix:
|
||||
type: string
|
||||
pattern: '^[a-z]{3}$'
|
||||
|
||||
required:
|
||||
- partnerUuid
|
||||
- billingContactUuid
|
||||
- debitorNumberSuffix
|
||||
- defaultPrefix
|
||||
- billable
|
||||
|
@ -46,10 +46,6 @@ components:
|
||||
HsOfficeMembershipPatch:
|
||||
type: object
|
||||
properties:
|
||||
mainDebitorUuid:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
validTo:
|
||||
type: string
|
||||
format: date
|
||||
@ -69,10 +65,6 @@ components:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: false
|
||||
mainDebitorUuid:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: false
|
||||
memberNumberSuffix:
|
||||
type: string
|
||||
minLength: 2
|
||||
@ -95,7 +87,6 @@ components:
|
||||
required:
|
||||
- partnerUuid
|
||||
- memberNumberSuffix
|
||||
- mainDebitorUuid
|
||||
- validFrom
|
||||
- membershipFeeBillable
|
||||
additionalProperties: false
|
||||
|
@ -14,10 +14,8 @@ components:
|
||||
format: int8
|
||||
minimum: 10000
|
||||
maximum: 99999
|
||||
person:
|
||||
$ref: './hs-office-person-schemas.yaml#/components/schemas/HsOfficePerson'
|
||||
contact:
|
||||
$ref: './hs-office-contact-schemas.yaml#/components/schemas/HsOfficeContact'
|
||||
partnerRel:
|
||||
$ref: './hs-office-relation-schemas.yaml#/components/schemas/HsOfficeRelation'
|
||||
details:
|
||||
$ref: '#/components/schemas/HsOfficePartnerDetails'
|
||||
|
||||
@ -52,11 +50,7 @@ components:
|
||||
HsOfficePartnerPatch:
|
||||
type: object
|
||||
properties:
|
||||
personUuid:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
contactUuid:
|
||||
partnerRelUuid:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
@ -98,18 +92,11 @@ components:
|
||||
maximum: 99999
|
||||
partnerRel:
|
||||
$ref: '#/components/schemas/HsOfficePartnerRelInsert'
|
||||
personUuid:
|
||||
type: string
|
||||
format: uuid
|
||||
contactUuid:
|
||||
type: string
|
||||
format: uuid
|
||||
details:
|
||||
$ref: '#/components/schemas/HsOfficePartnerDetailsInsert'
|
||||
required:
|
||||
- partnerNumber
|
||||
- personUuid
|
||||
- contactUuid
|
||||
- partnerRel
|
||||
- details
|
||||
|
||||
HsOfficePartnerRelInsert:
|
||||
|
@ -19,7 +19,7 @@ get:
|
||||
content:
|
||||
'application/json':
|
||||
schema:
|
||||
$ref: './hs-office-relations-schemas.yaml#/components/schemas/HsOfficeRelation'
|
||||
$ref: './hs-office-relation-schemas.yaml#/components/schemas/HsOfficeRelation'
|
||||
|
||||
"401":
|
||||
$ref: './error-responses.yaml#/components/responses/Unauthorized'
|
||||
@ -44,14 +44,14 @@ patch:
|
||||
content:
|
||||
'application/json':
|
||||
schema:
|
||||
$ref: './hs-office-relations-schemas.yaml#/components/schemas/HsOfficeRelationPatch'
|
||||
$ref: './hs-office-relation-schemas.yaml#/components/schemas/HsOfficeRelationPatch'
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
content:
|
||||
'application/json':
|
||||
schema:
|
||||
$ref: './hs-office-relations-schemas.yaml#/components/schemas/HsOfficeRelation'
|
||||
$ref: './hs-office-relation-schemas.yaml#/components/schemas/HsOfficeRelation'
|
||||
"401":
|
||||
$ref: './error-responses.yaml#/components/responses/Unauthorized'
|
||||
"403":
|
||||
|
@ -18,7 +18,7 @@ get:
|
||||
in: query
|
||||
required: false
|
||||
schema:
|
||||
$ref: './hs-office-relations-schemas.yaml#/components/schemas/HsOfficeRelationType'
|
||||
$ref: './hs-office-relation-schemas.yaml#/components/schemas/HsOfficeRelationType'
|
||||
description: Prefix of name properties from holder or contact to filter the results.
|
||||
responses:
|
||||
"200":
|
||||
@ -28,7 +28,7 @@ get:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: './hs-office-relations-schemas.yaml#/components/schemas/HsOfficeRelation'
|
||||
$ref: './hs-office-relation-schemas.yaml#/components/schemas/HsOfficeRelation'
|
||||
"401":
|
||||
$ref: './error-responses.yaml#/components/responses/Unauthorized'
|
||||
"403":
|
||||
@ -46,7 +46,7 @@ post:
|
||||
content:
|
||||
'application/json':
|
||||
schema:
|
||||
$ref: './hs-office-relations-schemas.yaml#/components/schemas/HsOfficeRelationInsert'
|
||||
$ref: './hs-office-relation-schemas.yaml#/components/schemas/HsOfficeRelationInsert'
|
||||
required: true
|
||||
responses:
|
||||
"201":
|
||||
@ -54,7 +54,7 @@ post:
|
||||
content:
|
||||
'application/json':
|
||||
schema:
|
||||
$ref: './hs-office-relations-schemas.yaml#/components/schemas/HsOfficeRelation'
|
||||
$ref: './hs-office-relation-schemas.yaml#/components/schemas/HsOfficeRelation'
|
||||
"401":
|
||||
$ref: './error-responses.yaml#/components/responses/Unauthorized'
|
||||
"403":
|
||||
|
@ -22,5 +22,6 @@ components:
|
||||
- owner
|
||||
- admin
|
||||
- tenant
|
||||
- referrer
|
||||
roleName:
|
||||
type: string
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
-- ============================================================================
|
||||
-- NUMERIC-HASH-FUNCTIONS
|
||||
--changeset hash:1 endDelimiter:--//
|
||||
--changeset numeric-hash-functions:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
create function bigIntHash(text) returns bigint as $$
|
||||
|
@ -26,7 +26,7 @@ create or replace procedure defineContext(
|
||||
currentTask varchar(96),
|
||||
currentRequest text = null,
|
||||
currentUser varchar(63) = null,
|
||||
assumedRoles varchar(256) = null
|
||||
assumedRoles varchar(1023) = null
|
||||
)
|
||||
language plpgsql as $$
|
||||
begin
|
||||
@ -43,7 +43,7 @@ begin
|
||||
execute format('set local hsadminng.currentUser to %L', currentUser);
|
||||
|
||||
assumedRoles := coalesce(assumedRoles, '');
|
||||
assert length(assumedRoles) <= 256, FORMAT('assumedRoles must not be longer than 256 characters: "%s"', assumedRoles);
|
||||
assert length(assumedRoles) <= 1023, FORMAT('assumedRoles must not be longer than 1023 characters: "%s"', assumedRoles);
|
||||
execute format('set local hsadminng.assumedRoles to %L', assumedRoles);
|
||||
|
||||
call contextDefined(currentTask, currentRequest, currentUser, assumedRoles);
|
||||
@ -87,11 +87,11 @@ end; $$;
|
||||
Raises exception if not set.
|
||||
*/
|
||||
create or replace function currentRequest()
|
||||
returns varchar(512)
|
||||
returns text
|
||||
stable -- leakproof
|
||||
language plpgsql as $$
|
||||
declare
|
||||
currentRequest varchar(512);
|
||||
currentRequest text;
|
||||
begin
|
||||
begin
|
||||
currentRequest := current_setting('hsadminng.currentRequest');
|
||||
@ -135,22 +135,11 @@ end; $$;
|
||||
or empty array, if not set.
|
||||
*/
|
||||
create or replace function assumedRoles()
|
||||
returns varchar(63)[]
|
||||
returns varchar(1023)[]
|
||||
stable -- leakproof
|
||||
language plpgsql as $$
|
||||
declare
|
||||
currentSubject varchar(63);
|
||||
begin
|
||||
begin
|
||||
currentSubject := current_setting('hsadminng.assumedRoles');
|
||||
exception
|
||||
when others then
|
||||
return array []::varchar[];
|
||||
end;
|
||||
if (currentSubject = '') then
|
||||
return array []::varchar[];
|
||||
end if;
|
||||
return string_to_array(currentSubject, ';');
|
||||
return string_to_array(current_setting('hsadminng.assumedRoles', true), ';');
|
||||
end; $$;
|
||||
|
||||
create or replace function cleanIdentifier(rawIdentifier varchar)
|
||||
@ -219,17 +208,17 @@ begin
|
||||
end ; $$;
|
||||
|
||||
create or replace function currentSubjects()
|
||||
returns varchar(63)[]
|
||||
returns varchar(1023)[]
|
||||
stable -- leakproof
|
||||
language plpgsql as $$
|
||||
declare
|
||||
assumedRoles varchar(63)[];
|
||||
assumedRoles varchar(1023)[];
|
||||
begin
|
||||
assumedRoles := assumedRoles();
|
||||
if array_length(assumedRoles, 1) > 0 then
|
||||
return assumedRoles();
|
||||
return assumedRoles;
|
||||
else
|
||||
return array [currentUser()]::varchar(63)[];
|
||||
return array [currentUser()]::varchar(1023)[];
|
||||
end if;
|
||||
end; $$;
|
||||
|
||||
|
@ -27,9 +27,9 @@ create table tx_context
|
||||
txId bigint not null,
|
||||
txTimestamp timestamp not null,
|
||||
currentUser varchar(63) not null, -- not the uuid, because users can be deleted
|
||||
assumedRoles varchar(256) not null, -- not the uuids, because roles can be deleted
|
||||
assumedRoles varchar(1023) not null, -- not the uuids, because roles can be deleted
|
||||
currentTask varchar(96) not null,
|
||||
currentRequest text not null
|
||||
currentRequest text not null
|
||||
);
|
||||
|
||||
create index on tx_context using brin (txTimestamp);
|
||||
|
@ -63,6 +63,7 @@ create or replace view rbacgrants_ev as
|
||||
x.grantedByRoleUuid,
|
||||
x.ascendantUuid as ascendantUuid,
|
||||
x.descendantUuid as descendantUuid,
|
||||
x.op as permOp, x.optablename as permOpTableName,
|
||||
x.assumed
|
||||
from (
|
||||
select g.uuid as grantUuid,
|
||||
@ -74,13 +75,17 @@ create or replace view rbacgrants_ev as
|
||||
'role ' || aro.objectTable || '#' || findIdNameByObjectUuid(aro.objectTable, aro.uuid) || '.' || ar.roletype
|
||||
) as ascendingIdName,
|
||||
aro.objectTable, aro.uuid,
|
||||
|
||||
coalesce(
|
||||
'role ' || dro.objectTable || '#' || findIdNameByObjectUuid(dro.objectTable, dro.uuid) || '.' || dr.roletype,
|
||||
'perm ' || dp.op || ' on ' || dpo.objecttable || '#' || findIdNameByObjectUuid(dpo.objectTable, dpo.uuid)
|
||||
( case
|
||||
when dro is not null
|
||||
then ('role ' || dro.objectTable || '#' || findIdNameByObjectUuid(dro.objectTable, dro.uuid) || '.' || dr.roletype)
|
||||
when dp.op = 'INSERT'
|
||||
then 'perm ' || dp.op || ' into ' || dp.opTableName || ' with ' || dpo.objecttable || '#' || findIdNameByObjectUuid(dpo.objectTable, dpo.uuid)
|
||||
else 'perm ' || dp.op || ' on ' || dpo.objecttable || '#' || findIdNameByObjectUuid(dpo.objectTable, dpo.uuid)
|
||||
end
|
||||
) as descendingIdName,
|
||||
dro.objectTable, dro.uuid
|
||||
from rbacgrants as g
|
||||
dro.objectTable, dro.uuid,
|
||||
dp.op, dp.optablename
|
||||
from rbacgrants as g
|
||||
|
||||
left outer join rbacrole as ar on ar.uuid = g.ascendantUuid
|
||||
left outer join rbacobject as aro on aro.uuid = ar.objectuuid
|
||||
|
@ -86,16 +86,14 @@ execute procedure insertTriggerForTestCustomer_tf();
|
||||
do language plpgsql $$
|
||||
declare
|
||||
row global;
|
||||
permissionUuid uuid;
|
||||
roleUuid uuid;
|
||||
begin
|
||||
call defineContext('create INSERT INTO test_customer permissions for the related global rows');
|
||||
|
||||
FOR row IN SELECT * FROM global
|
||||
LOOP
|
||||
roleUuid := findRoleId(globalAdmin());
|
||||
permissionUuid := createPermission(row.uuid, 'INSERT', 'test_customer');
|
||||
call grantPermissionToRole(permissionUuid, roleUuid);
|
||||
call grantPermissionToRole(
|
||||
createPermission(row.uuid, 'INSERT', 'test_customer'),
|
||||
globalAdmin());
|
||||
END LOOP;
|
||||
END;
|
||||
$$;
|
||||
|
@ -151,16 +151,14 @@ execute procedure updateTriggerForTestPackage_tf();
|
||||
do language plpgsql $$
|
||||
declare
|
||||
row test_customer;
|
||||
permissionUuid uuid;
|
||||
roleUuid uuid;
|
||||
begin
|
||||
call defineContext('create INSERT INTO test_package permissions for the related test_customer rows');
|
||||
|
||||
FOR row IN SELECT * FROM test_customer
|
||||
LOOP
|
||||
roleUuid := findRoleId(testCustomerAdmin(row));
|
||||
permissionUuid := createPermission(row.uuid, 'INSERT', 'test_package');
|
||||
call grantPermissionToRole(permissionUuid, roleUuid);
|
||||
call grantPermissionToRole(
|
||||
createPermission(row.uuid, 'INSERT', 'test_package'),
|
||||
testCustomerAdmin(row));
|
||||
END LOOP;
|
||||
END;
|
||||
$$;
|
||||
|
@ -150,16 +150,14 @@ execute procedure updateTriggerForTestDomain_tf();
|
||||
do language plpgsql $$
|
||||
declare
|
||||
row test_package;
|
||||
permissionUuid uuid;
|
||||
roleUuid uuid;
|
||||
begin
|
||||
call defineContext('create INSERT INTO test_domain permissions for the related test_package rows');
|
||||
|
||||
FOR row IN SELECT * FROM test_package
|
||||
LOOP
|
||||
roleUuid := findRoleId(testPackageAdmin(row));
|
||||
permissionUuid := createPermission(row.uuid, 'INSERT', 'test_domain');
|
||||
call grantPermissionToRole(permissionUuid, roleUuid);
|
||||
call grantPermissionToRole(
|
||||
createPermission(row.uuid, 'INSERT', 'test_domain'),
|
||||
testPackageAdmin(row));
|
||||
END LOOP;
|
||||
END;
|
||||
$$;
|
||||
|
@ -1,126 +0,0 @@
|
||||
--liquibase formatted sql
|
||||
-- This code generated was by RbacViewPostgresGenerator, do not amend manually.
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-contact-rbac-OBJECT:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRelatedRbacObject('hs_office_contact');
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-contact-rbac-ROLE-DESCRIPTORS:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacRoleDescriptors('hsOfficeContact', 'hs_office_contact');
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-contact-rbac-insert-trigger:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
Creates the roles, grants and permission for the AFTER INSERT TRIGGER.
|
||||
*/
|
||||
|
||||
create or replace procedure buildRbacSystemForHsOfficeContact(
|
||||
NEW hs_office_contact
|
||||
)
|
||||
language plpgsql as $$
|
||||
|
||||
declare
|
||||
|
||||
begin
|
||||
call enterTriggerForObjectUuid(NEW.uuid);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeContactOwner(NEW),
|
||||
permissions => array['DELETE'],
|
||||
incomingSuperRoles => array[globalAdmin()],
|
||||
userUuids => array[currentUserUuid()]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeContactAdmin(NEW),
|
||||
permissions => array['UPDATE'],
|
||||
incomingSuperRoles => array[hsOfficeContactOwner(NEW)]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeContactReferrer(NEW),
|
||||
permissions => array['SELECT'],
|
||||
incomingSuperRoles => array[hsOfficeContactAdmin(NEW)]
|
||||
);
|
||||
|
||||
call leaveTriggerForObjectUuid(NEW.uuid);
|
||||
end; $$;
|
||||
|
||||
/*
|
||||
AFTER INSERT TRIGGER to create the role+grant structure for a new hs_office_contact row.
|
||||
*/
|
||||
|
||||
create or replace function insertTriggerForHsOfficeContact_tf()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
strict as $$
|
||||
begin
|
||||
call buildRbacSystemForHsOfficeContact(NEW);
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
create trigger insertTriggerForHsOfficeContact_tg
|
||||
after insert on hs_office_contact
|
||||
for each row
|
||||
execute procedure insertTriggerForHsOfficeContact_tf();
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-contact-rbac-INSERT:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
Checks if the user or assumed roles are allowed to insert a row to hs_office_contact,
|
||||
where only global-admin has that permission.
|
||||
*/
|
||||
create or replace function hs_office_contact_insert_permission_missing_tf()
|
||||
returns trigger
|
||||
language plpgsql as $$
|
||||
begin
|
||||
raise exception '[403] insert into hs_office_contact not allowed for current subjects % (%)',
|
||||
currentSubjects(), currentSubjectsUuids();
|
||||
end; $$;
|
||||
|
||||
create trigger hs_office_contact_insert_permission_check_tg
|
||||
before insert on hs_office_contact
|
||||
for each row
|
||||
when ( not isGlobalAdmin() )
|
||||
execute procedure hs_office_contact_insert_permission_missing_tf();
|
||||
--//
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-contact-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
call generateRbacIdentityViewFromProjection('hs_office_contact',
|
||||
$idName$
|
||||
label
|
||||
$idName$);
|
||||
--//
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-contact-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacRestrictedView('hs_office_contact',
|
||||
$orderBy$
|
||||
label
|
||||
$orderBy$,
|
||||
$updates$
|
||||
label = new.label,
|
||||
postalAddress = new.postalAddress,
|
||||
emailAddresses = new.emailAddresses,
|
||||
phoneNumbers = new.phoneNumbers
|
||||
$updates$);
|
||||
--//
|
||||
|
@ -24,6 +24,7 @@ subgraph contact["`**contact**`"]
|
||||
perm:contact:DELETE{{contact:DELETE}}
|
||||
perm:contact:UPDATE{{contact:UPDATE}}
|
||||
perm:contact:SELECT{{contact:SELECT}}
|
||||
perm:contact:INSERT{{contact:INSERT}}
|
||||
end
|
||||
end
|
||||
|
||||
@ -39,5 +40,6 @@ role:contact:admin ==> role:contact:referrer
|
||||
role:contact:owner ==> perm:contact:DELETE
|
||||
role:contact:admin ==> perm:contact:UPDATE
|
||||
role:contact:referrer ==> perm:contact:SELECT
|
||||
role:global:guest ==> perm:contact:INSERT
|
||||
|
||||
```
|
@ -1,4 +1,6 @@
|
||||
--liquibase formatted sql
|
||||
-- This code generated was by RbacViewPostgresGenerator, do not amend manually.
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-contact-rbac-OBJECT:1 endDelimiter:--//
|
||||
@ -15,127 +17,130 @@ call generateRbacRoleDescriptors('hsOfficeContact', 'hs_office_contact');
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-contact-rbac-ROLES-CREATION:1 endDelimiter:--//
|
||||
--changeset hs-office-contact-rbac-insert-trigger:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
Creates the roles and their assignments for a new contact for the AFTER INSERT TRIGGER.
|
||||
Creates the roles, grants and permission for the AFTER INSERT TRIGGER.
|
||||
*/
|
||||
|
||||
create or replace function createRbacRolesForHsOfficeContact()
|
||||
create or replace procedure buildRbacSystemForHsOfficeContact(
|
||||
NEW hs_office_contact
|
||||
)
|
||||
language plpgsql as $$
|
||||
|
||||
declare
|
||||
|
||||
begin
|
||||
call enterTriggerForObjectUuid(NEW.uuid);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeContactOwner(NEW),
|
||||
permissions => array['DELETE'],
|
||||
incomingSuperRoles => array[globalAdmin()],
|
||||
userUuids => array[currentUserUuid()]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeContactAdmin(NEW),
|
||||
permissions => array['UPDATE'],
|
||||
incomingSuperRoles => array[hsOfficeContactOwner(NEW)]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeContactReferrer(NEW),
|
||||
permissions => array['SELECT'],
|
||||
incomingSuperRoles => array[hsOfficeContactAdmin(NEW)]
|
||||
);
|
||||
|
||||
call leaveTriggerForObjectUuid(NEW.uuid);
|
||||
end; $$;
|
||||
|
||||
/*
|
||||
AFTER INSERT TRIGGER to create the role+grant structure for a new hs_office_contact row.
|
||||
*/
|
||||
|
||||
create or replace function insertTriggerForHsOfficeContact_tf()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
strict as $$
|
||||
begin
|
||||
if TG_OP <> 'INSERT' then
|
||||
raise exception 'invalid usage of TRIGGER AFTER INSERT';
|
||||
end if;
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeContactOwner(NEW),
|
||||
permissions => array['DELETE'],
|
||||
incomingSuperRoles => array[globalAdmin()],
|
||||
userUuids => array[currentUserUuid()],
|
||||
grantedByRole => globalAdmin()
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeContactAdmin(NEW),
|
||||
permissions => array['UPDATE'],
|
||||
incomingSuperRoles => array[hsOfficeContactOwner(NEW)]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeContactTenant(NEW),
|
||||
incomingSuperRoles => array[hsOfficeContactAdmin(NEW)]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeContactGuest(NEW),
|
||||
permissions => array['SELECT'],
|
||||
incomingSuperRoles => array[hsOfficeContactTenant(NEW)]
|
||||
);
|
||||
|
||||
call buildRbacSystemForHsOfficeContact(NEW);
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
/*
|
||||
An AFTER INSERT TRIGGER which creates the role structure for a new customer.
|
||||
*/
|
||||
|
||||
create trigger createRbacRolesForHsOfficeContact_Trigger
|
||||
after insert
|
||||
on hs_office_contact
|
||||
create trigger insertTriggerForHsOfficeContact_tg
|
||||
after insert on hs_office_contact
|
||||
for each row
|
||||
execute procedure createRbacRolesForHsOfficeContact();
|
||||
execute procedure insertTriggerForHsOfficeContact_tf();
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-contact-rbac-INSERT:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
Creates INSERT INTO hs_office_contact permissions for the related global rows.
|
||||
*/
|
||||
do language plpgsql $$
|
||||
declare
|
||||
row global;
|
||||
begin
|
||||
call defineContext('create INSERT INTO hs_office_contact permissions for the related global rows');
|
||||
|
||||
FOR row IN SELECT * FROM global
|
||||
LOOP
|
||||
call grantPermissionToRole(
|
||||
createPermission(row.uuid, 'INSERT', 'hs_office_contact'),
|
||||
globalGuest());
|
||||
END LOOP;
|
||||
END;
|
||||
$$;
|
||||
|
||||
/**
|
||||
Adds hs_office_contact INSERT permission to specified role of new global rows.
|
||||
*/
|
||||
create or replace function hs_office_contact_global_insert_tf()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
strict as $$
|
||||
begin
|
||||
call grantPermissionToRole(
|
||||
createPermission(NEW.uuid, 'INSERT', 'hs_office_contact'),
|
||||
globalGuest());
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
-- z_... is to put it at the end of after insert triggers, to make sure the roles exist
|
||||
create trigger z_hs_office_contact_global_insert_tg
|
||||
after insert on global
|
||||
for each row
|
||||
execute procedure hs_office_contact_global_insert_tf();
|
||||
--//
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-contact-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
call generateRbacIdentityViewFromProjection('hs_office_contact', $idName$
|
||||
target.label
|
||||
call generateRbacIdentityViewFromProjection('hs_office_contact',
|
||||
$idName$
|
||||
label
|
||||
$idName$);
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-contact-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacRestrictedView('hs_office_contact', 'target.label',
|
||||
call generateRbacRestrictedView('hs_office_contact',
|
||||
$orderBy$
|
||||
label
|
||||
$orderBy$,
|
||||
$updates$
|
||||
label = new.label,
|
||||
postalAddress = new.postalAddress,
|
||||
emailAddresses = new.emailAddresses,
|
||||
phoneNumbers = new.phoneNumbers
|
||||
$updates$);
|
||||
--/
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-contact-rbac-NEW-CONTACT:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
/*
|
||||
Creates a global permission for new-contact and assigns it to the hostsharing admins role.
|
||||
*/
|
||||
do language plpgsql $$
|
||||
declare
|
||||
addCustomerPermissions uuid[];
|
||||
globalObjectUuid uuid;
|
||||
globalAdminRoleUuid uuid;
|
||||
begin
|
||||
call defineContext('granting global new-contact permission to global admin role', null, null, null);
|
||||
|
||||
globalAdminRoleUuid := findRoleId(globalAdmin());
|
||||
globalObjectUuid := (select uuid from global);
|
||||
addCustomerPermissions := createPermissions(globalObjectUuid, array ['new-contact']);
|
||||
call grantPermissionsToRole(globalAdminRoleUuid, addCustomerPermissions);
|
||||
end;
|
||||
$$;
|
||||
|
||||
/**
|
||||
Used by the trigger to prevent the add-customer to current user respectively assumed roles.
|
||||
*/
|
||||
create or replace function addHsOfficeContactNotAllowedForCurrentSubjects()
|
||||
returns trigger
|
||||
language PLPGSQL
|
||||
as $$
|
||||
begin
|
||||
raise exception '[403] new-contact not permitted for %',
|
||||
array_to_string(currentSubjects(), ';', 'null');
|
||||
end; $$;
|
||||
|
||||
/**
|
||||
Checks if the user or assumed roles are allowed to create a new customer.
|
||||
*/
|
||||
create trigger hs_office_contact_insert_trigger
|
||||
before insert
|
||||
on hs_office_contact
|
||||
for each row
|
||||
-- TODO.spec: who is allowed to create new contacts
|
||||
when ( not hasAssumedRole() )
|
||||
execute procedure addHsOfficeContactNotAllowedForCurrentSubjects();
|
||||
--//
|
||||
|
||||
|
@ -22,7 +22,6 @@ create table if not exists hs_office_person
|
||||
givenName varchar(48),
|
||||
familyName varchar(48)
|
||||
);
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
|
@ -1,126 +0,0 @@
|
||||
--liquibase formatted sql
|
||||
-- This code generated was by RbacViewPostgresGenerator, do not amend manually.
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-person-rbac-OBJECT:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRelatedRbacObject('hs_office_person');
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-person-rbac-ROLE-DESCRIPTORS:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacRoleDescriptors('hsOfficePerson', 'hs_office_person');
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-person-rbac-insert-trigger:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
Creates the roles, grants and permission for the AFTER INSERT TRIGGER.
|
||||
*/
|
||||
|
||||
create or replace procedure buildRbacSystemForHsOfficePerson(
|
||||
NEW hs_office_person
|
||||
)
|
||||
language plpgsql as $$
|
||||
|
||||
declare
|
||||
|
||||
begin
|
||||
call enterTriggerForObjectUuid(NEW.uuid);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficePersonOwner(NEW),
|
||||
permissions => array['DELETE'],
|
||||
incomingSuperRoles => array[globalAdmin()],
|
||||
userUuids => array[currentUserUuid()]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficePersonAdmin(NEW),
|
||||
permissions => array['UPDATE'],
|
||||
incomingSuperRoles => array[hsOfficePersonOwner(NEW)]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficePersonReferrer(NEW),
|
||||
permissions => array['SELECT'],
|
||||
incomingSuperRoles => array[hsOfficePersonAdmin(NEW)]
|
||||
);
|
||||
|
||||
call leaveTriggerForObjectUuid(NEW.uuid);
|
||||
end; $$;
|
||||
|
||||
/*
|
||||
AFTER INSERT TRIGGER to create the role+grant structure for a new hs_office_person row.
|
||||
*/
|
||||
|
||||
create or replace function insertTriggerForHsOfficePerson_tf()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
strict as $$
|
||||
begin
|
||||
call buildRbacSystemForHsOfficePerson(NEW);
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
create trigger insertTriggerForHsOfficePerson_tg
|
||||
after insert on hs_office_person
|
||||
for each row
|
||||
execute procedure insertTriggerForHsOfficePerson_tf();
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-person-rbac-INSERT:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
Checks if the user or assumed roles are allowed to insert a row to hs_office_person,
|
||||
where only global-admin has that permission.
|
||||
*/
|
||||
create or replace function hs_office_person_insert_permission_missing_tf()
|
||||
returns trigger
|
||||
language plpgsql as $$
|
||||
begin
|
||||
raise exception '[403] insert into hs_office_person not allowed for current subjects % (%)',
|
||||
currentSubjects(), currentSubjectsUuids();
|
||||
end; $$;
|
||||
|
||||
create trigger hs_office_person_insert_permission_check_tg
|
||||
before insert on hs_office_person
|
||||
for each row
|
||||
when ( not isGlobalAdmin() )
|
||||
execute procedure hs_office_person_insert_permission_missing_tf();
|
||||
--//
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-person-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
call generateRbacIdentityViewFromProjection('hs_office_person',
|
||||
$idName$
|
||||
concat(tradeName, familyName, givenName)
|
||||
$idName$);
|
||||
--//
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-person-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacRestrictedView('hs_office_person',
|
||||
$orderBy$
|
||||
concat(tradeName, familyName, givenName)
|
||||
$orderBy$,
|
||||
$updates$
|
||||
personType = new.personType,
|
||||
tradeName = new.tradeName,
|
||||
givenName = new.givenName,
|
||||
familyName = new.familyName
|
||||
$updates$);
|
||||
--//
|
||||
|
@ -21,6 +21,7 @@ subgraph person["`**person**`"]
|
||||
subgraph person:permissions[ ]
|
||||
style person:permissions fill:#dd4901,stroke:white
|
||||
|
||||
perm:person:INSERT{{person:INSERT}}
|
||||
perm:person:DELETE{{person:DELETE}}
|
||||
perm:person:UPDATE{{person:UPDATE}}
|
||||
perm:person:SELECT{{person:SELECT}}
|
||||
@ -36,6 +37,7 @@ role:person:owner ==> role:person:admin
|
||||
role:person:admin ==> role:person:referrer
|
||||
|
||||
%% granting permissions to roles
|
||||
role:global:guest ==> perm:person:INSERT
|
||||
role:person:owner ==> perm:person:DELETE
|
||||
role:person:admin ==> perm:person:UPDATE
|
||||
role:person:referrer ==> perm:person:SELECT
|
@ -1,4 +1,6 @@
|
||||
--liquibase formatted sql
|
||||
-- This code generated was by RbacViewPostgresGenerator, do not amend manually.
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-person-rbac-OBJECT:1 endDelimiter:--//
|
||||
@ -15,74 +17,125 @@ call generateRbacRoleDescriptors('hsOfficePerson', 'hs_office_person');
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-person-rbac-ROLES-CREATION:1 endDelimiter:--//
|
||||
--changeset hs-office-person-rbac-insert-trigger:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
Creates the roles and their assignments for a new person for the AFTER INSERT TRIGGER.
|
||||
Creates the roles, grants and permission for the AFTER INSERT TRIGGER.
|
||||
*/
|
||||
create or replace function createRbacRolesForHsOfficePerson()
|
||||
|
||||
create or replace procedure buildRbacSystemForHsOfficePerson(
|
||||
NEW hs_office_person
|
||||
)
|
||||
language plpgsql as $$
|
||||
|
||||
declare
|
||||
|
||||
begin
|
||||
call enterTriggerForObjectUuid(NEW.uuid);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficePersonOwner(NEW),
|
||||
permissions => array['DELETE'],
|
||||
incomingSuperRoles => array[globalAdmin()],
|
||||
userUuids => array[currentUserUuid()]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficePersonAdmin(NEW),
|
||||
permissions => array['UPDATE'],
|
||||
incomingSuperRoles => array[hsOfficePersonOwner(NEW)]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficePersonReferrer(NEW),
|
||||
permissions => array['SELECT'],
|
||||
incomingSuperRoles => array[hsOfficePersonAdmin(NEW)]
|
||||
);
|
||||
|
||||
call leaveTriggerForObjectUuid(NEW.uuid);
|
||||
end; $$;
|
||||
|
||||
/*
|
||||
AFTER INSERT TRIGGER to create the role+grant structure for a new hs_office_person row.
|
||||
*/
|
||||
|
||||
create or replace function insertTriggerForHsOfficePerson_tf()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
strict as $$
|
||||
begin
|
||||
if TG_OP <> 'INSERT' then
|
||||
raise exception 'invalid usage of TRIGGER AFTER INSERT';
|
||||
end if;
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficePersonOwner(NEW),
|
||||
permissions => array['DELETE'],
|
||||
incomingSuperRoles => array[globalAdmin()],
|
||||
userUuids => array[currentUserUuid()],
|
||||
grantedByRole => globalAdmin()
|
||||
);
|
||||
|
||||
-- TODO: who is admin? the person itself? is it allowed for the person itself or a representative to update the data?
|
||||
perform createRoleWithGrants(
|
||||
hsOfficePersonAdmin(NEW),
|
||||
permissions => array['UPDATE'],
|
||||
incomingSuperRoles => array[hsOfficePersonOwner(NEW)]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficePersonTenant(NEW),
|
||||
incomingSuperRoles => array[hsOfficePersonAdmin(NEW)]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficePersonGuest(NEW),
|
||||
permissions => array['SELECT'],
|
||||
incomingSuperRoles => array[hsOfficePersonTenant(NEW)]
|
||||
);
|
||||
|
||||
call buildRbacSystemForHsOfficePerson(NEW);
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
/*
|
||||
An AFTER INSERT TRIGGER which creates the role structure for a new customer.
|
||||
*/
|
||||
|
||||
create trigger createRbacRolesForHsOfficePerson_Trigger
|
||||
after insert
|
||||
on hs_office_person
|
||||
create trigger insertTriggerForHsOfficePerson_tg
|
||||
after insert on hs_office_person
|
||||
for each row
|
||||
execute procedure createRbacRolesForHsOfficePerson();
|
||||
execute procedure insertTriggerForHsOfficePerson_tf();
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-person-rbac-INSERT:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
Creates INSERT INTO hs_office_person permissions for the related global rows.
|
||||
*/
|
||||
do language plpgsql $$
|
||||
declare
|
||||
row global;
|
||||
begin
|
||||
call defineContext('create INSERT INTO hs_office_person permissions for the related global rows');
|
||||
|
||||
FOR row IN SELECT * FROM global
|
||||
LOOP
|
||||
call grantPermissionToRole(
|
||||
createPermission(row.uuid, 'INSERT', 'hs_office_person'),
|
||||
globalGuest());
|
||||
END LOOP;
|
||||
END;
|
||||
$$;
|
||||
|
||||
/**
|
||||
Adds hs_office_person INSERT permission to specified role of new global rows.
|
||||
*/
|
||||
create or replace function hs_office_person_global_insert_tf()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
strict as $$
|
||||
begin
|
||||
call grantPermissionToRole(
|
||||
createPermission(NEW.uuid, 'INSERT', 'hs_office_person'),
|
||||
globalGuest());
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
-- z_... is to put it at the end of after insert triggers, to make sure the roles exist
|
||||
create trigger z_hs_office_person_global_insert_tg
|
||||
after insert on global
|
||||
for each row
|
||||
execute procedure hs_office_person_global_insert_tf();
|
||||
--//
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-person-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacIdentityViewFromProjection('hs_office_person', $idName$
|
||||
concat(target.tradeName, target.familyName, target.givenName)
|
||||
|
||||
call generateRbacIdentityViewFromProjection('hs_office_person',
|
||||
$idName$
|
||||
concat(tradeName, familyName, givenName)
|
||||
$idName$);
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-person-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacRestrictedView('hs_office_person', 'concat(target.tradeName, target.familyName, target.givenName)',
|
||||
call generateRbacRestrictedView('hs_office_person',
|
||||
$orderBy$
|
||||
concat(tradeName, familyName, givenName)
|
||||
$orderBy$,
|
||||
$updates$
|
||||
personType = new.personType,
|
||||
tradeName = new.tradeName,
|
||||
@ -91,49 +144,3 @@ call generateRbacRestrictedView('hs_office_person', 'concat(target.tradeName, ta
|
||||
$updates$);
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-person-rbac-NEW-PERSON:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
/*
|
||||
Creates a global permission for new-person and assigns it to the hostsharing admins role.
|
||||
*/
|
||||
do language plpgsql $$
|
||||
declare
|
||||
addCustomerPermissions uuid[];
|
||||
globalObjectUuid uuid;
|
||||
globalAdminRoleUuid uuid ;
|
||||
begin
|
||||
call defineContext('granting global new-person permission to global admin role', null, null, null);
|
||||
|
||||
globalAdminRoleUuid := findRoleId(globalAdmin());
|
||||
globalObjectUuid := (select uuid from global);
|
||||
addCustomerPermissions := createPermissions(globalObjectUuid, array ['new-person']);
|
||||
call grantPermissionsToRole(globalAdminRoleUuid, addCustomerPermissions);
|
||||
end;
|
||||
$$;
|
||||
|
||||
/**
|
||||
Used by the trigger to prevent the add-customer to current user respectively assumed roles.
|
||||
*/
|
||||
create or replace function addHsOfficePersonNotAllowedForCurrentSubjects()
|
||||
returns trigger
|
||||
language PLPGSQL
|
||||
as $$
|
||||
begin
|
||||
raise exception '[403] new-person not permitted for %',
|
||||
array_to_string(currentSubjects(), ';', 'null');
|
||||
end; $$;
|
||||
|
||||
/**
|
||||
Checks if the user or assumed roles are allowed to create a new customer.
|
||||
*/
|
||||
create trigger hs_office_person_insert_trigger
|
||||
before insert
|
||||
on hs_office_person
|
||||
for each row
|
||||
-- TODO.spec: who is allowed to create new persons
|
||||
when ( not hasAssumedRole() )
|
||||
execute procedure addHsOfficePersonNotAllowedForCurrentSubjects();
|
||||
--//
|
||||
|
||||
|
@ -28,7 +28,7 @@ begin
|
||||
call defineContext(currentTask, null, emailAddr);
|
||||
execute format('set local hsadminng.currentTask to %L', currentTask);
|
||||
|
||||
raise notice 'creating test person: %', fullName;
|
||||
raise notice 'creating test person: % by %', fullName, emailAddr;
|
||||
insert
|
||||
into hs_office_person (persontype, tradename, givenname, familyname)
|
||||
values (newPersonType, newTradeName, newGivenName, newFamilyName);
|
||||
@ -67,9 +67,10 @@ do language plpgsql $$
|
||||
call createHsOfficePersonTestData('NP', null, 'Fouler', 'Ellie');
|
||||
call createHsOfficePersonTestData('LP', 'Second e.K.', 'Smith', 'Peter');
|
||||
call createHsOfficePersonTestData('IF', 'Third OHG');
|
||||
call createHsOfficePersonTestData('IF', 'Fourth eG');
|
||||
call createHsOfficePersonTestData('LP', 'Fourth eG');
|
||||
call createHsOfficePersonTestData('UF', 'Erben Bessler', 'Mel', 'Bessler');
|
||||
call createHsOfficePersonTestData('NP', null, 'Bessler', 'Anita');
|
||||
call createHsOfficePersonTestData('NP', null, 'Bessler', 'Bert');
|
||||
call createHsOfficePersonTestData('NP', null, 'Winkler', 'Paul');
|
||||
end;
|
||||
$$;
|
||||
|
@ -1,100 +0,0 @@
|
||||
### rbac relation
|
||||
|
||||
This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually.
|
||||
|
||||
```mermaid
|
||||
%%{init:{'flowchart':{'htmlLabels':false}}}%%
|
||||
flowchart TB
|
||||
|
||||
subgraph holderPerson["`**holderPerson**`"]
|
||||
direction TB
|
||||
style holderPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph holderPerson:roles[ ]
|
||||
style holderPerson:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:holderPerson:owner[[holderPerson:owner]]
|
||||
role:holderPerson:admin[[holderPerson:admin]]
|
||||
role:holderPerson:referrer[[holderPerson:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph anchorPerson["`**anchorPerson**`"]
|
||||
direction TB
|
||||
style anchorPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph anchorPerson:roles[ ]
|
||||
style anchorPerson:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:anchorPerson:owner[[anchorPerson:owner]]
|
||||
role:anchorPerson:admin[[anchorPerson:admin]]
|
||||
role:anchorPerson:referrer[[anchorPerson:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph contact["`**contact**`"]
|
||||
direction TB
|
||||
style contact fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph contact:roles[ ]
|
||||
style contact:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:contact:owner[[contact:owner]]
|
||||
role:contact:admin[[contact:admin]]
|
||||
role:contact:referrer[[contact:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph relation["`**relation**`"]
|
||||
direction TB
|
||||
style relation fill:#dd4901,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph relation:roles[ ]
|
||||
style relation:roles fill:#dd4901,stroke:white
|
||||
|
||||
role:relation:owner[[relation:owner]]
|
||||
role:relation:admin[[relation:admin]]
|
||||
role:relation:agent[[relation:agent]]
|
||||
role:relation:tenant[[relation:tenant]]
|
||||
end
|
||||
|
||||
subgraph relation:permissions[ ]
|
||||
style relation:permissions fill:#dd4901,stroke:white
|
||||
|
||||
perm:relation:DELETE{{relation:DELETE}}
|
||||
perm:relation:UPDATE{{relation:UPDATE}}
|
||||
perm:relation:SELECT{{relation:SELECT}}
|
||||
end
|
||||
end
|
||||
|
||||
%% granting roles to users
|
||||
user:creator ==> role:relation:owner
|
||||
|
||||
%% granting roles to roles
|
||||
role:global:admin -.-> role:anchorPerson:owner
|
||||
role:anchorPerson:owner -.-> role:anchorPerson:admin
|
||||
role:anchorPerson:admin -.-> role:anchorPerson:referrer
|
||||
role:global:admin -.-> role:holderPerson:owner
|
||||
role:holderPerson:owner -.-> role:holderPerson:admin
|
||||
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: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:relation:owner ==> perm:relation:DELETE
|
||||
role:relation:admin ==> perm:relation:UPDATE
|
||||
role:relation:tenant ==> perm:relation:SELECT
|
||||
|
||||
```
|
@ -1,191 +0,0 @@
|
||||
--liquibase formatted sql
|
||||
-- This code generated was by RbacViewPostgresGenerator, do not amend manually.
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-relation-rbac-OBJECT:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRelatedRbacObject('hs_office_relation');
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-relation-rbac-ROLE-DESCRIPTORS:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacRoleDescriptors('hsOfficeRelation', 'hs_office_relation');
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-relation-rbac-insert-trigger:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
Creates the roles, grants and permission for the AFTER INSERT TRIGGER.
|
||||
*/
|
||||
|
||||
create or replace procedure buildRbacSystemForHsOfficeRelation(
|
||||
NEW hs_office_relation
|
||||
)
|
||||
language plpgsql as $$
|
||||
|
||||
declare
|
||||
newHolderPerson hs_office_person;
|
||||
newAnchorPerson hs_office_person;
|
||||
newContact hs_office_contact;
|
||||
|
||||
begin
|
||||
call enterTriggerForObjectUuid(NEW.uuid);
|
||||
|
||||
SELECT * FROM hs_office_person WHERE uuid = NEW.holderUuid INTO newHolderPerson;
|
||||
|
||||
SELECT * FROM hs_office_person WHERE uuid = NEW.anchorUuid INTO newAnchorPerson;
|
||||
|
||||
SELECT * FROM hs_office_contact WHERE uuid = NEW.contactUuid INTO newContact;
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeRelationOwner(NEW),
|
||||
permissions => array['DELETE'],
|
||||
incomingSuperRoles => array[globalAdmin()],
|
||||
userUuids => array[currentUserUuid()]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeRelationAdmin(NEW),
|
||||
permissions => array['UPDATE'],
|
||||
incomingSuperRoles => array[
|
||||
hsOfficePersonAdmin(newAnchorPerson),
|
||||
hsOfficeRelationOwner(NEW)]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeRelationAgent(NEW),
|
||||
incomingSuperRoles => array[
|
||||
hsOfficePersonAdmin(newHolderPerson),
|
||||
hsOfficeRelationAdmin(NEW)]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeRelationTenant(NEW),
|
||||
permissions => array['SELECT'],
|
||||
incomingSuperRoles => array[
|
||||
hsOfficeContactAdmin(newContact),
|
||||
hsOfficePersonAdmin(newHolderPerson),
|
||||
hsOfficeRelationAgent(NEW)],
|
||||
outgoingSubRoles => array[
|
||||
hsOfficeContactReferrer(newContact),
|
||||
hsOfficePersonReferrer(newAnchorPerson),
|
||||
hsOfficePersonReferrer(newHolderPerson)]
|
||||
);
|
||||
|
||||
call leaveTriggerForObjectUuid(NEW.uuid);
|
||||
end; $$;
|
||||
|
||||
/*
|
||||
AFTER INSERT TRIGGER to create the role+grant structure for a new hs_office_relation row.
|
||||
*/
|
||||
|
||||
create or replace function insertTriggerForHsOfficeRelation_tf()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
strict as $$
|
||||
begin
|
||||
call buildRbacSystemForHsOfficeRelation(NEW);
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
create trigger insertTriggerForHsOfficeRelation_tg
|
||||
after insert on hs_office_relation
|
||||
for each row
|
||||
execute procedure insertTriggerForHsOfficeRelation_tf();
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-relation-rbac-update-trigger:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
Called from the AFTER UPDATE TRIGGER to re-wire the grants.
|
||||
*/
|
||||
|
||||
create or replace procedure updateRbacRulesForHsOfficeRelation(
|
||||
OLD hs_office_relation,
|
||||
NEW hs_office_relation
|
||||
)
|
||||
language plpgsql as $$
|
||||
begin
|
||||
|
||||
if NEW.contactUuid is distinct from OLD.contactUuid then
|
||||
delete from rbacgrants g where g.grantedbytriggerof = OLD.uuid;
|
||||
call buildRbacSystemForHsOfficeRelation(NEW);
|
||||
end if;
|
||||
end; $$;
|
||||
|
||||
/*
|
||||
AFTER INSERT TRIGGER to re-wire the grant structure for a new hs_office_relation row.
|
||||
*/
|
||||
|
||||
create or replace function updateTriggerForHsOfficeRelation_tf()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
strict as $$
|
||||
begin
|
||||
call updateRbacRulesForHsOfficeRelation(OLD, NEW);
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
create trigger updateTriggerForHsOfficeRelation_tg
|
||||
after update on hs_office_relation
|
||||
for each row
|
||||
execute procedure updateTriggerForHsOfficeRelation_tf();
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-relation-rbac-INSERT:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
Checks if the user or assumed roles are allowed to insert a row to hs_office_relation,
|
||||
where only global-admin has that permission.
|
||||
*/
|
||||
create or replace function hs_office_relation_insert_permission_missing_tf()
|
||||
returns trigger
|
||||
language plpgsql as $$
|
||||
begin
|
||||
raise exception '[403] insert into hs_office_relation not allowed for current subjects % (%)',
|
||||
currentSubjects(), currentSubjectsUuids();
|
||||
end; $$;
|
||||
|
||||
create trigger hs_office_relation_insert_permission_check_tg
|
||||
before insert on hs_office_relation
|
||||
for each row
|
||||
when ( not isGlobalAdmin() )
|
||||
execute procedure hs_office_relation_insert_permission_missing_tf();
|
||||
--//
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-relation-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
call generateRbacIdentityViewFromProjection('hs_office_relation',
|
||||
$idName$
|
||||
(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)
|
||||
$idName$);
|
||||
--//
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-relation-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacRestrictedView('hs_office_relation',
|
||||
$orderBy$
|
||||
(select idName from hs_office_person_iv p where p.uuid = target.holderUuid)
|
||||
$orderBy$,
|
||||
$updates$
|
||||
contactUuid = new.contactUuid
|
||||
$updates$);
|
||||
--//
|
||||
|
@ -1,44 +1,102 @@
|
||||
### hs_office_relation RBAC
|
||||
### rbac relation
|
||||
|
||||
This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually.
|
||||
|
||||
```mermaid
|
||||
|
||||
%%{init:{'flowchart':{'htmlLabels':false}}}%%
|
||||
flowchart TB
|
||||
|
||||
subgraph global
|
||||
style global fill:#eee
|
||||
|
||||
role:global.admin[global.admin]
|
||||
end
|
||||
|
||||
subgraph hsOfficeContact
|
||||
subgraph holderPerson["`**holderPerson**`"]
|
||||
direction TB
|
||||
style hsOfficeContact fill:#eee
|
||||
style holderPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
role:hsOfficeContact.admin[contact.admin]
|
||||
--> role:hsOfficeContact.tenant[contact.tenant]
|
||||
--> role:hsOfficeContact.guest[contact.guest]
|
||||
subgraph holderPerson:roles[ ]
|
||||
style holderPerson:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:holderPerson:owner[[holderPerson:owner]]
|
||||
role:holderPerson:admin[[holderPerson:admin]]
|
||||
role:holderPerson:referrer[[holderPerson:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph hsOfficePerson
|
||||
subgraph anchorPerson["`**anchorPerson**`"]
|
||||
direction TB
|
||||
style hsOfficePerson fill:#eee
|
||||
style anchorPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
role:hsOfficePerson.admin[person.admin]
|
||||
--> role:hsOfficePerson.tenant[person.tenant]
|
||||
--> role:hsOfficePerson.guest[person.guest]
|
||||
subgraph anchorPerson:roles[ ]
|
||||
style anchorPerson:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:anchorPerson:owner[[anchorPerson:owner]]
|
||||
role:anchorPerson:admin[[anchorPerson:admin]]
|
||||
role:anchorPerson:referrer[[anchorPerson:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph hsOfficeRelation
|
||||
subgraph contact["`**contact**`"]
|
||||
direction TB
|
||||
style contact fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
role:hsOfficePerson#anchor.admin[person#anchor.admin]
|
||||
--- role:hsOfficePerson.admin
|
||||
subgraph contact:roles[ ]
|
||||
style contact:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:hsOfficeRelation.owner[relation.owner]
|
||||
%% permissions
|
||||
role:hsOfficeRelation.owner --> perm:hsOfficeRelation.*{{relation.*}}
|
||||
%% incoming
|
||||
role:global.admin ---> role:hsOfficeRelation.owner
|
||||
role:hsOfficePersonAdmin#anchor.admin
|
||||
role:contact:owner[[contact:owner]]
|
||||
role:contact:admin[[contact:admin]]
|
||||
role:contact:referrer[[contact:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph relation["`**relation**`"]
|
||||
direction TB
|
||||
style relation fill:#dd4901,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph relation:roles[ ]
|
||||
style relation:roles fill:#dd4901,stroke:white
|
||||
|
||||
role:relation:owner[[relation:owner]]
|
||||
role:relation:admin[[relation:admin]]
|
||||
role:relation:agent[[relation:agent]]
|
||||
role:relation:tenant[[relation:tenant]]
|
||||
end
|
||||
|
||||
subgraph relation:permissions[ ]
|
||||
style relation:permissions fill:#dd4901,stroke:white
|
||||
|
||||
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:relation:owner
|
||||
|
||||
%% granting roles to roles
|
||||
role:global:admin -.-> role:anchorPerson:owner
|
||||
role:anchorPerson:owner -.-> role:anchorPerson:admin
|
||||
role:anchorPerson:admin -.-> role:anchorPerson:referrer
|
||||
role:global:admin -.-> role:holderPerson:owner
|
||||
role:holderPerson:owner -.-> role:holderPerson:admin
|
||||
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: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:relation:owner ==> perm:relation:DELETE
|
||||
role:relation:admin ==> perm:relation:UPDATE
|
||||
role:relation:tenant ==> perm:relation:SELECT
|
||||
role:anchorPerson:admin ==> perm:relation:INSERT
|
||||
|
||||
```
|
||||
|
||||
|
@ -1,4 +1,6 @@
|
||||
--liquibase formatted sql
|
||||
-- This code generated was by RbacViewPostgresGenerator, do not amend manually.
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-relation-rbac-OBJECT:1 endDelimiter:--//
|
||||
@ -15,178 +17,255 @@ call generateRbacRoleDescriptors('hsOfficeRelation', 'hs_office_relation');
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-relation-rbac-ROLES-CREATION:1 endDelimiter:--//
|
||||
--changeset hs-office-relation-rbac-insert-trigger:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
Creates and updates the roles and their assignments for relation entities.
|
||||
Creates the roles, grants and permission for the AFTER INSERT TRIGGER.
|
||||
*/
|
||||
|
||||
create or replace function hsOfficeRelationRbacRolesTrigger()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
strict as $$
|
||||
create or replace procedure buildRbacSystemForHsOfficeRelation(
|
||||
NEW hs_office_relation
|
||||
)
|
||||
language plpgsql as $$
|
||||
|
||||
declare
|
||||
hsOfficeRelationTenant RbacRoleDescriptor;
|
||||
newAnchor hs_office_person;
|
||||
newHolder hs_office_person;
|
||||
oldContact hs_office_contact;
|
||||
newContact hs_office_contact;
|
||||
newHolderPerson hs_office_person;
|
||||
newAnchorPerson hs_office_person;
|
||||
newContact hs_office_contact;
|
||||
|
||||
begin
|
||||
call enterTriggerForObjectUuid(NEW.uuid);
|
||||
|
||||
hsOfficeRelationTenant := hsOfficeRelationTenant(NEW);
|
||||
SELECT * FROM hs_office_person WHERE 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.anchorUuid into newAnchor;
|
||||
select * from hs_office_person as p where p.uuid = NEW.holderUuid into newHolder;
|
||||
select * from hs_office_contact as c where c.uuid = NEW.contactUuid into newContact;
|
||||
SELECT * FROM hs_office_person WHERE uuid = NEW.anchorUuid INTO newAnchorPerson;
|
||||
assert newAnchorPerson.uuid is not null, format('newAnchorPerson must not be null for NEW.anchorUuid = %s', NEW.anchorUuid);
|
||||
|
||||
if TG_OP = 'INSERT' then
|
||||
SELECT * FROM hs_office_contact WHERE 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(
|
||||
hsOfficeRelationOwner(NEW),
|
||||
permissions => array['DELETE'],
|
||||
incomingSuperRoles => array[
|
||||
globalAdmin(),
|
||||
hsOfficePersonAdmin(newAnchor)]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeRelationAdmin(NEW),
|
||||
permissions => array['UPDATE'],
|
||||
incomingSuperRoles => array[hsOfficeRelationOwner(NEW)]
|
||||
);
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeRelationOwner(NEW),
|
||||
permissions => array['DELETE'],
|
||||
incomingSuperRoles => array[globalAdmin()],
|
||||
userUuids => array[currentUserUuid()]
|
||||
);
|
||||
|
||||
-- the tenant role for those related users who can view the data
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeRelationTenant,
|
||||
permissions => array['SELECT'],
|
||||
incomingSuperRoles => array[
|
||||
hsOfficeRelationAdmin(NEW),
|
||||
hsOfficePersonAdmin(newAnchor),
|
||||
hsOfficePersonAdmin(newHolder),
|
||||
hsOfficeContactAdmin(newContact)],
|
||||
outgoingSubRoles => array[
|
||||
hsOfficePersonTenant(newAnchor),
|
||||
hsOfficePersonTenant(newHolder),
|
||||
hsOfficeContactTenant(newContact)]
|
||||
);
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeRelationAdmin(NEW),
|
||||
permissions => array['UPDATE'],
|
||||
incomingSuperRoles => array[
|
||||
hsOfficePersonAdmin(newAnchorPerson),
|
||||
hsOfficeRelationOwner(NEW)]
|
||||
);
|
||||
|
||||
-- anchor and holder admin roles need each others tenant role
|
||||
-- to be able to see the joined relation
|
||||
-- TODO: this can probably be avoided through agent+guest roles
|
||||
call grantRoleToRole(hsOfficePersonTenant(newAnchor), hsOfficePersonAdmin(newHolder));
|
||||
call grantRoleToRole(hsOfficePersonTenant(newHolder), hsOfficePersonAdmin(newAnchor));
|
||||
call grantRoleToRoleIfNotNull(hsOfficePersonTenant(newHolder), hsOfficeContactAdmin(newContact));
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeRelationAgent(NEW),
|
||||
incomingSuperRoles => array[
|
||||
hsOfficePersonAdmin(newHolderPerson),
|
||||
hsOfficeRelationAdmin(NEW)]
|
||||
);
|
||||
|
||||
elsif TG_OP = 'UPDATE' then
|
||||
|
||||
if OLD.contactUuid <> NEW.contactUuid then
|
||||
-- nothing but the contact can be updated,
|
||||
-- in other cases, a new relation needs to be created and the old updated
|
||||
|
||||
select * from hs_office_contact as c where c.uuid = OLD.contactUuid into oldContact;
|
||||
|
||||
call revokeRoleFromRole( hsOfficeRelationTenant, hsOfficeContactAdmin(oldContact) );
|
||||
call grantRoleToRole( hsOfficeRelationTenant, hsOfficeContactAdmin(newContact) );
|
||||
|
||||
call revokeRoleFromRole( hsOfficeContactTenant(oldContact), hsOfficeRelationTenant );
|
||||
call grantRoleToRole( hsOfficeContactTenant(newContact), hsOfficeRelationTenant );
|
||||
end if;
|
||||
else
|
||||
raise exception 'invalid usage of TRIGGER';
|
||||
end if;
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeRelationTenant(NEW),
|
||||
permissions => array['SELECT'],
|
||||
incomingSuperRoles => array[
|
||||
hsOfficeContactAdmin(newContact),
|
||||
hsOfficePersonAdmin(newHolderPerson),
|
||||
hsOfficeRelationAgent(NEW)],
|
||||
outgoingSubRoles => array[
|
||||
hsOfficeContactReferrer(newContact),
|
||||
hsOfficePersonReferrer(newAnchorPerson),
|
||||
hsOfficePersonReferrer(newHolderPerson)]
|
||||
);
|
||||
|
||||
call leaveTriggerForObjectUuid(NEW.uuid);
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
/*
|
||||
An AFTER INSERT TRIGGER which creates the role structure for a new customer.
|
||||
AFTER INSERT TRIGGER to create the role+grant structure for a new hs_office_relation row.
|
||||
*/
|
||||
create trigger createRbacRolesForHsOfficeRelation_Trigger
|
||||
after insert
|
||||
on hs_office_relation
|
||||
for each row
|
||||
execute procedure hsOfficeRelationRbacRolesTrigger();
|
||||
|
||||
/*
|
||||
An AFTER UPDATE TRIGGER which updates the role structure of a customer.
|
||||
*/
|
||||
create trigger updateRbacRolesForHsOfficeRelation_Trigger
|
||||
after update
|
||||
on hs_office_relation
|
||||
create or replace function insertTriggerForHsOfficeRelation_tf()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
strict as $$
|
||||
begin
|
||||
call buildRbacSystemForHsOfficeRelation(NEW);
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
create trigger insertTriggerForHsOfficeRelation_tg
|
||||
after insert on hs_office_relation
|
||||
for each row
|
||||
execute procedure hsOfficeRelationRbacRolesTrigger();
|
||||
execute procedure insertTriggerForHsOfficeRelation_tf();
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-relation-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
||||
--changeset hs-office-relation-rbac-update-trigger:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacIdentityViewFromProjection('hs_office_relation', $idName$
|
||||
(select idName from hs_office_person_iv p where p.uuid = target.anchorUuid)
|
||||
|| '-with-' || target.type || '-' ||
|
||||
(select idName from hs_office_person_iv p where p.uuid = target.holderUuid)
|
||||
$idName$);
|
||||
|
||||
/*
|
||||
Called from the AFTER UPDATE TRIGGER to re-wire the grants.
|
||||
*/
|
||||
|
||||
create or replace procedure updateRbacRulesForHsOfficeRelation(
|
||||
OLD hs_office_relation,
|
||||
NEW hs_office_relation
|
||||
)
|
||||
language plpgsql as $$
|
||||
|
||||
declare
|
||||
oldHolderPerson hs_office_person;
|
||||
newHolderPerson hs_office_person;
|
||||
oldAnchorPerson hs_office_person;
|
||||
newAnchorPerson hs_office_person;
|
||||
oldContact hs_office_contact;
|
||||
newContact hs_office_contact;
|
||||
|
||||
begin
|
||||
call enterTriggerForObjectUuid(NEW.uuid);
|
||||
|
||||
SELECT * FROM hs_office_person WHERE 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 WHERE 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 WHERE 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 WHERE 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 WHERE uuid = OLD.contactUuid INTO oldContact;
|
||||
assert oldContact.uuid is not null, format('oldContact must not be null for OLD.contactUuid = %s', OLD.contactUuid);
|
||||
|
||||
SELECT * FROM hs_office_contact WHERE uuid = NEW.contactUuid INTO newContact;
|
||||
assert newContact.uuid is not null, format('newContact must not be null for NEW.contactUuid = %s', NEW.contactUuid);
|
||||
|
||||
|
||||
if NEW.contactUuid <> OLD.contactUuid then
|
||||
|
||||
call revokeRoleFromRole(hsOfficeRelationTenant(OLD), hsOfficeContactAdmin(oldContact));
|
||||
call grantRoleToRole(hsOfficeRelationTenant(NEW), hsOfficeContactAdmin(newContact));
|
||||
|
||||
call revokeRoleFromRole(hsOfficeContactReferrer(oldContact), hsOfficeRelationTenant(OLD));
|
||||
call grantRoleToRole(hsOfficeContactReferrer(newContact), hsOfficeRelationTenant(NEW));
|
||||
|
||||
end if;
|
||||
|
||||
call leaveTriggerForObjectUuid(NEW.uuid);
|
||||
end; $$;
|
||||
|
||||
/*
|
||||
AFTER INSERT TRIGGER to re-wire the grant structure for a new hs_office_relation row.
|
||||
*/
|
||||
|
||||
create or replace function updateTriggerForHsOfficeRelation_tf()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
strict as $$
|
||||
begin
|
||||
call updateRbacRulesForHsOfficeRelation(OLD, NEW);
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
create trigger updateTriggerForHsOfficeRelation_tg
|
||||
after update on hs_office_relation
|
||||
for each row
|
||||
execute procedure updateTriggerForHsOfficeRelation_tf();
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-relation-rbac-INSERT:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
Creates INSERT INTO hs_office_relation permissions for the related hs_office_person rows.
|
||||
*/
|
||||
do language plpgsql $$
|
||||
declare
|
||||
row hs_office_person;
|
||||
begin
|
||||
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
|
||||
call grantPermissionToRole(
|
||||
createPermission(row.uuid, 'INSERT', 'hs_office_relation'),
|
||||
hsOfficePersonAdmin(row));
|
||||
END LOOP;
|
||||
END;
|
||||
$$;
|
||||
|
||||
/**
|
||||
Adds hs_office_relation INSERT permission to specified role of new hs_office_person rows.
|
||||
*/
|
||||
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_relation'),
|
||||
hsOfficePersonAdmin(NEW));
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
-- z_... is to put it at the end of after insert triggers, to make sure the roles exist
|
||||
create trigger z_hs_office_relation_hs_office_person_insert_tg
|
||||
after insert on hs_office_person
|
||||
for each row
|
||||
execute procedure hs_office_relation_hs_office_person_insert_tf();
|
||||
|
||||
/**
|
||||
Checks if the user or assumed roles are allowed to insert a row to hs_office_relation,
|
||||
where the check is performed by a direct role.
|
||||
|
||||
A direct role is a role depending on a foreign key directly available in the NEW row.
|
||||
*/
|
||||
create or replace function hs_office_relation_insert_permission_missing_tf()
|
||||
returns trigger
|
||||
language plpgsql as $$
|
||||
begin
|
||||
raise exception '[403] insert into hs_office_relation not allowed for current subjects % (%)',
|
||||
currentSubjects(), currentSubjectsUuids();
|
||||
end; $$;
|
||||
|
||||
create trigger hs_office_relation_insert_permission_check_tg
|
||||
before insert on hs_office_relation
|
||||
for each row
|
||||
when ( not hasInsertPermission(NEW.anchorUuid, 'INSERT', 'hs_office_relation') )
|
||||
execute procedure hs_office_relation_insert_permission_missing_tf();
|
||||
--//
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-relation-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
call generateRbacIdentityViewFromProjection('hs_office_relation',
|
||||
$idName$
|
||||
(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)
|
||||
$idName$);
|
||||
--//
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-relation-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacRestrictedView('hs_office_relation',
|
||||
'(select idName from hs_office_person_iv p where p.uuid = target.holderUuid)',
|
||||
$orderBy$
|
||||
(select idName from hs_office_person_iv p where p.uuid = target.holderUuid)
|
||||
$orderBy$,
|
||||
$updates$
|
||||
contactUuid = new.contactUuid
|
||||
$updates$);
|
||||
--//
|
||||
|
||||
-- TODO: exception if one tries to amend any other column
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-relation-rbac-NEW-RELATHIONSHIP:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
/*
|
||||
Creates a global permission for new-relation and assigns it to the hostsharing admins role.
|
||||
*/
|
||||
do language plpgsql $$
|
||||
declare
|
||||
addCustomerPermissions uuid[];
|
||||
globalObjectUuid uuid;
|
||||
globalAdminRoleUuid uuid ;
|
||||
begin
|
||||
call defineContext('granting global new-relation permission to global admin role', null, null, null);
|
||||
|
||||
globalAdminRoleUuid := findRoleId(globalAdmin());
|
||||
globalObjectUuid := (select uuid from global);
|
||||
addCustomerPermissions := createPermissions(globalObjectUuid, array ['new-relation']);
|
||||
call grantPermissionsToRole(globalAdminRoleUuid, addCustomerPermissions);
|
||||
end;
|
||||
$$;
|
||||
|
||||
/**
|
||||
Used by the trigger to prevent the add-customer to current user respectively assumed roles.
|
||||
*/
|
||||
create or replace function addHsOfficeRelationNotAllowedForCurrentSubjects()
|
||||
returns trigger
|
||||
language PLPGSQL
|
||||
as $$
|
||||
begin
|
||||
raise exception '[403] new-relation not permitted for %',
|
||||
array_to_string(currentSubjects(), ';', 'null');
|
||||
end; $$;
|
||||
|
||||
/**
|
||||
Checks if the user or assumed roles are allowed to create a new customer.
|
||||
*/
|
||||
create trigger hs_office_relation_insert_trigger
|
||||
before insert
|
||||
on hs_office_relation
|
||||
for each row
|
||||
-- TODO.spec: who is allowed to create new relations
|
||||
when ( not hasAssumedRole() )
|
||||
execute procedure addHsOfficeRelationNotAllowedForCurrentSubjects();
|
||||
--//
|
||||
|
||||
|
@ -11,7 +11,7 @@
|
||||
create or replace procedure createHsOfficeRelationTestData(
|
||||
holderPersonName varchar,
|
||||
relationType HsOfficeRelationType,
|
||||
anchorPersonTradeName varchar,
|
||||
anchorPersonName varchar,
|
||||
contactLabel varchar,
|
||||
mark varchar default null)
|
||||
language plpgsql as $$
|
||||
@ -23,24 +23,28 @@ declare
|
||||
contact hs_office_contact;
|
||||
|
||||
begin
|
||||
idName := cleanIdentifier( anchorPersonTradeName || '-' || holderPersonName);
|
||||
idName := cleanIdentifier( anchorPersonName || '-' || holderPersonName);
|
||||
currentTask := 'creating relation test-data ' || idName;
|
||||
call defineContext(currentTask, null, 'superuser-alex@hostsharing.net', 'global#global.admin');
|
||||
execute format('set local hsadminng.currentTask to %L', currentTask);
|
||||
|
||||
select p.* from hs_office_person p where p.tradeName = anchorPersonTradeName into anchorPerson;
|
||||
select p.*
|
||||
into anchorPerson
|
||||
from hs_office_person p
|
||||
where p.tradeName = anchorPersonName or p.familyName = anchorPersonName;
|
||||
if anchorPerson is null then
|
||||
raise exception 'anchorPerson "%" not found', anchorPersonTradeName;
|
||||
raise exception 'anchorPerson "%" not found', anchorPersonName;
|
||||
end if;
|
||||
|
||||
select p.* from hs_office_person p
|
||||
where p.tradeName = holderPersonName or p.familyName = holderPersonName
|
||||
into holderPerson;
|
||||
select p.*
|
||||
into holderPerson
|
||||
from hs_office_person p
|
||||
where p.tradeName = holderPersonName or p.familyName = holderPersonName;
|
||||
if holderPerson is null then
|
||||
raise exception 'holderPerson "%" not found', holderPersonName;
|
||||
end if;
|
||||
|
||||
select c.* from hs_office_contact c where c.label = contactLabel into contact;
|
||||
select c.* into contact from hs_office_contact c where c.label = contactLabel;
|
||||
if contact is null then
|
||||
raise exception 'contact "%" not found', contactLabel;
|
||||
end if;
|
||||
@ -87,17 +91,22 @@ do language plpgsql $$
|
||||
begin
|
||||
call createHsOfficeRelationTestData('First GmbH', 'PARTNER', 'Hostsharing eG', 'first contact');
|
||||
call createHsOfficeRelationTestData('Firby', 'REPRESENTATIVE', 'First GmbH', 'first contact');
|
||||
call createHsOfficeRelationTestData('First GmbH', 'DEBITOR', 'First GmbH', 'first contact');
|
||||
|
||||
call createHsOfficeRelationTestData('Second e.K.', 'PARTNER', 'Hostsharing eG', 'second contact');
|
||||
call createHsOfficeRelationTestData('Smith', 'REPRESENTATIVE', 'Second e.K.', 'second contact');
|
||||
call createHsOfficeRelationTestData('Second e.K.', 'DEBITOR', 'Second e.K.', 'second contact');
|
||||
|
||||
call createHsOfficeRelationTestData('Third OHG', 'PARTNER', 'Hostsharing eG', 'third contact');
|
||||
call createHsOfficeRelationTestData('Tucker', 'REPRESENTATIVE', 'Third OHG', 'third contact');
|
||||
call createHsOfficeRelationTestData('Third OHG', 'DEBITOR', 'Third OHG', 'third contact');
|
||||
|
||||
call createHsOfficeRelationTestData('Fourth eG', 'PARTNER', 'Hostsharing eG', 'fourth contact');
|
||||
call createHsOfficeRelationTestData('Fouler', 'REPRESENTATIVE', 'Third OHG', 'third contact');
|
||||
call createHsOfficeRelationTestData('Third OHG', 'DEBITOR', 'Third OHG', 'third contact');
|
||||
|
||||
call createHsOfficeRelationTestData('Smith', 'PARTNER', 'Hostsharing eG', 'sixth contact');
|
||||
call createHsOfficeRelationTestData('Smith', 'DEBITOR', 'Smith', 'third contact');
|
||||
call createHsOfficeRelationTestData('Smith', 'SUBSCRIBER', 'Third OHG', 'third contact', 'members-announce');
|
||||
end;
|
||||
$$;
|
||||
|
@ -33,23 +33,20 @@ create table hs_office_partner
|
||||
(
|
||||
uuid uuid unique references RbacObject (uuid) initially deferred,
|
||||
partnerNumber numeric(5) unique not null,
|
||||
partnerRelUuid uuid not null references hs_office_relation(uuid), -- TODO: delete in after delete trigger
|
||||
personUuid uuid not null references hs_office_person(uuid), -- TODO: remove, replaced by partnerRelUuid
|
||||
contactUuid uuid not null references hs_office_contact(uuid), -- TODO: remove, replaced by partnerRelUuid
|
||||
partnerRelUuid uuid not null references hs_office_relation(uuid), -- deleted in after delete trigger
|
||||
detailsUuid uuid not null references hs_office_partner_details(uuid) -- deleted in after delete trigger
|
||||
);
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-partner-DELETE-DETAILS-TRIGGER:1 endDelimiter:--//
|
||||
--changeset hs-office-partner-DELETE-DEPENDENTS-TRIGGER:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
|
||||
/**
|
||||
Trigger function to delete related details of a partner to delete.
|
||||
*/
|
||||
create or replace function deleteHsOfficeDetailsOnPartnerDelete()
|
||||
create or replace function deleteHsOfficeDependentsOnPartnerDelete()
|
||||
returns trigger
|
||||
language PLPGSQL
|
||||
as $$
|
||||
@ -61,17 +58,24 @@ begin
|
||||
if counter = 0 then
|
||||
raise exception 'partner details % could not be deleted', OLD.detailsUuid;
|
||||
end if;
|
||||
|
||||
DELETE FROM hs_office_relation r WHERE r.uuid = OLD.partnerRelUuid;
|
||||
GET DIAGNOSTICS counter = ROW_COUNT;
|
||||
if counter = 0 then
|
||||
raise exception 'partner relation % could not be deleted', OLD.partnerRelUuid;
|
||||
end if;
|
||||
|
||||
RETURN OLD;
|
||||
end; $$;
|
||||
|
||||
/**
|
||||
Triggers deletion of related details of a partner to delete.
|
||||
Triggers deletion of related rows of a partner to delete.
|
||||
*/
|
||||
create trigger hs_office_partner_delete_details_trigger
|
||||
create trigger hs_office_partner_delete_dependents_trigger
|
||||
after delete
|
||||
on hs_office_partner
|
||||
for each row
|
||||
execute procedure deleteHsOfficeDetailsOnPartnerDelete();
|
||||
execute procedure deleteHsOfficeDependentsOnPartnerDelete();
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-partner-MAIN-TABLE-JOURNAL:1 endDelimiter:--//
|
||||
|
@ -1,158 +0,0 @@
|
||||
### rbac partner
|
||||
|
||||
This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually.
|
||||
|
||||
```mermaid
|
||||
%%{init:{'flowchart':{'htmlLabels':false}}}%%
|
||||
flowchart TB
|
||||
|
||||
subgraph partnerRel.contact["`**partnerRel.contact**`"]
|
||||
direction TB
|
||||
style partnerRel.contact fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph partnerRel.contact:roles[ ]
|
||||
style partnerRel.contact:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:partnerRel.contact:owner[[partnerRel.contact:owner]]
|
||||
role:partnerRel.contact:admin[[partnerRel.contact:admin]]
|
||||
role:partnerRel.contact:referrer[[partnerRel.contact:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph partner["`**partner**`"]
|
||||
direction TB
|
||||
style partner fill:#dd4901,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph partner:permissions[ ]
|
||||
style partner:permissions fill:#dd4901,stroke:white
|
||||
|
||||
perm:partner:INSERT{{partner:INSERT}}
|
||||
perm:partner:DELETE{{partner:DELETE}}
|
||||
perm:partner:UPDATE{{partner:UPDATE}}
|
||||
perm:partner:SELECT{{partner:SELECT}}
|
||||
end
|
||||
|
||||
subgraph partnerRel["`**partnerRel**`"]
|
||||
direction TB
|
||||
style partnerRel fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
subgraph partnerRel.contact["`**partnerRel.contact**`"]
|
||||
direction TB
|
||||
style partnerRel.contact fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph partnerRel.contact:roles[ ]
|
||||
style partnerRel.contact:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:partnerRel.contact:owner[[partnerRel.contact:owner]]
|
||||
role:partnerRel.contact:admin[[partnerRel.contact:admin]]
|
||||
role:partnerRel.contact:referrer[[partnerRel.contact:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph partnerRel.anchorPerson["`**partnerRel.anchorPerson**`"]
|
||||
direction TB
|
||||
style partnerRel.anchorPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph partnerRel.anchorPerson:roles[ ]
|
||||
style partnerRel.anchorPerson:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:partnerRel.anchorPerson:owner[[partnerRel.anchorPerson:owner]]
|
||||
role:partnerRel.anchorPerson:admin[[partnerRel.anchorPerson:admin]]
|
||||
role:partnerRel.anchorPerson:referrer[[partnerRel.anchorPerson:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph partnerRel.holderPerson["`**partnerRel.holderPerson**`"]
|
||||
direction TB
|
||||
style partnerRel.holderPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph partnerRel.holderPerson:roles[ ]
|
||||
style partnerRel.holderPerson:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:partnerRel.holderPerson:owner[[partnerRel.holderPerson:owner]]
|
||||
role:partnerRel.holderPerson:admin[[partnerRel.holderPerson:admin]]
|
||||
role:partnerRel.holderPerson:referrer[[partnerRel.holderPerson:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph partnerRel:roles[ ]
|
||||
style partnerRel:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:partnerRel:owner[[partnerRel:owner]]
|
||||
role:partnerRel:admin[[partnerRel:admin]]
|
||||
role:partnerRel:agent[[partnerRel:agent]]
|
||||
role:partnerRel:tenant[[partnerRel:tenant]]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
subgraph partnerDetails["`**partnerDetails**`"]
|
||||
direction TB
|
||||
style partnerDetails fill:#feb28c,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph partnerDetails:permissions[ ]
|
||||
style partnerDetails:permissions fill:#feb28c,stroke:white
|
||||
|
||||
perm:partnerDetails:DELETE{{partnerDetails:DELETE}}
|
||||
perm:partnerDetails:UPDATE{{partnerDetails:UPDATE}}
|
||||
perm:partnerDetails:SELECT{{partnerDetails:SELECT}}
|
||||
end
|
||||
end
|
||||
|
||||
subgraph partnerRel.anchorPerson["`**partnerRel.anchorPerson**`"]
|
||||
direction TB
|
||||
style partnerRel.anchorPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph partnerRel.anchorPerson:roles[ ]
|
||||
style partnerRel.anchorPerson:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:partnerRel.anchorPerson:owner[[partnerRel.anchorPerson:owner]]
|
||||
role:partnerRel.anchorPerson:admin[[partnerRel.anchorPerson:admin]]
|
||||
role:partnerRel.anchorPerson:referrer[[partnerRel.anchorPerson:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph partnerRel.holderPerson["`**partnerRel.holderPerson**`"]
|
||||
direction TB
|
||||
style partnerRel.holderPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph partnerRel.holderPerson:roles[ ]
|
||||
style partnerRel.holderPerson:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:partnerRel.holderPerson:owner[[partnerRel.holderPerson:owner]]
|
||||
role:partnerRel.holderPerson:admin[[partnerRel.holderPerson:admin]]
|
||||
role:partnerRel.holderPerson:referrer[[partnerRel.holderPerson:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
%% granting roles to roles
|
||||
role:global:admin -.-> role:partnerRel.anchorPerson:owner
|
||||
role:partnerRel.anchorPerson:owner -.-> role:partnerRel.anchorPerson:admin
|
||||
role:partnerRel.anchorPerson:admin -.-> role:partnerRel.anchorPerson:referrer
|
||||
role:global:admin -.-> role:partnerRel.holderPerson:owner
|
||||
role:partnerRel.holderPerson:owner -.-> role:partnerRel.holderPerson:admin
|
||||
role:partnerRel.holderPerson:admin -.-> role:partnerRel.holderPerson:referrer
|
||||
role:global:admin -.-> role:partnerRel.contact:owner
|
||||
role:partnerRel.contact:owner -.-> role:partnerRel.contact:admin
|
||||
role:partnerRel.contact:admin -.-> role:partnerRel.contact:referrer
|
||||
role:global:admin -.-> role:partnerRel:owner
|
||||
role:partnerRel:owner -.-> role:partnerRel:admin
|
||||
role:partnerRel.anchorPerson:admin -.-> role:partnerRel:admin
|
||||
role:partnerRel:admin -.-> role:partnerRel:agent
|
||||
role:partnerRel.holderPerson:admin -.-> role:partnerRel:agent
|
||||
role:partnerRel:agent -.-> role:partnerRel:tenant
|
||||
role:partnerRel.holderPerson:admin -.-> role:partnerRel:tenant
|
||||
role:partnerRel.contact:admin -.-> role:partnerRel:tenant
|
||||
role:partnerRel:tenant -.-> role:partnerRel.anchorPerson:referrer
|
||||
role:partnerRel:tenant -.-> role:partnerRel.holderPerson:referrer
|
||||
role:partnerRel:tenant -.-> role:partnerRel.contact:referrer
|
||||
|
||||
%% granting permissions to roles
|
||||
role:global:admin ==> perm:partner:INSERT
|
||||
role:partnerRel:admin ==> perm:partner:DELETE
|
||||
role:partnerRel:agent ==> perm:partner:UPDATE
|
||||
role:partnerRel:tenant ==> perm:partner:SELECT
|
||||
role:partnerRel:admin ==> perm:partnerDetails:DELETE
|
||||
role:partnerRel:agent ==> perm:partnerDetails:UPDATE
|
||||
role:partnerRel:agent ==> perm:partnerDetails:SELECT
|
||||
|
||||
```
|
@ -1,248 +0,0 @@
|
||||
--liquibase formatted sql
|
||||
-- This code generated was by RbacViewPostgresGenerator, do not amend manually.
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-partner-rbac-OBJECT:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRelatedRbacObject('hs_office_partner');
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-partner-rbac-ROLE-DESCRIPTORS:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacRoleDescriptors('hsOfficePartner', 'hs_office_partner');
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-partner-rbac-insert-trigger:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
Creates the roles, grants and permission for the AFTER INSERT TRIGGER.
|
||||
*/
|
||||
|
||||
create or replace procedure buildRbacSystemForHsOfficePartner(
|
||||
NEW hs_office_partner
|
||||
)
|
||||
language plpgsql as $$
|
||||
|
||||
declare
|
||||
newPartnerRel hs_office_relation;
|
||||
newPartnerDetails hs_office_partner_details;
|
||||
|
||||
begin
|
||||
call enterTriggerForObjectUuid(NEW.uuid);
|
||||
|
||||
SELECT * FROM hs_office_relation WHERE uuid = NEW.partnerRelUuid INTO newPartnerRel;
|
||||
assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.partnerRelUuid = %s', NEW.partnerRelUuid);
|
||||
|
||||
SELECT * FROM hs_office_partner_details WHERE uuid = NEW.detailsUuid INTO newPartnerDetails;
|
||||
assert newPartnerDetails.uuid is not null, format('newPartnerDetails must not be null for NEW.detailsUuid = %s', NEW.detailsUuid);
|
||||
|
||||
call grantPermissionToRole(createPermission(NEW.uuid, 'DELETE'), hsOfficeRelationAdmin(newPartnerRel));
|
||||
call grantPermissionToRole(createPermission(NEW.uuid, 'SELECT'), hsOfficeRelationTenant(newPartnerRel));
|
||||
call grantPermissionToRole(createPermission(NEW.uuid, 'UPDATE'), hsOfficeRelationAgent(newPartnerRel));
|
||||
call grantPermissionToRole(createPermission(newPartnerDetails.uuid, 'DELETE'), hsOfficeRelationAdmin(newPartnerRel));
|
||||
call grantPermissionToRole(createPermission(newPartnerDetails.uuid, 'SELECT'), hsOfficeRelationAgent(newPartnerRel));
|
||||
call grantPermissionToRole(createPermission(newPartnerDetails.uuid, 'UPDATE'), hsOfficeRelationAgent(newPartnerRel));
|
||||
|
||||
call leaveTriggerForObjectUuid(NEW.uuid);
|
||||
end; $$;
|
||||
|
||||
/*
|
||||
AFTER INSERT TRIGGER to create the role+grant structure for a new hs_office_partner row.
|
||||
*/
|
||||
|
||||
create or replace function insertTriggerForHsOfficePartner_tf()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
strict as $$
|
||||
begin
|
||||
call buildRbacSystemForHsOfficePartner(NEW);
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
create trigger insertTriggerForHsOfficePartner_tg
|
||||
after insert on hs_office_partner
|
||||
for each row
|
||||
execute procedure insertTriggerForHsOfficePartner_tf();
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-partner-rbac-update-trigger:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
Called from the AFTER UPDATE TRIGGER to re-wire the grants.
|
||||
*/
|
||||
|
||||
create or replace procedure updateRbacRulesForHsOfficePartner(
|
||||
OLD hs_office_partner,
|
||||
NEW hs_office_partner
|
||||
)
|
||||
language plpgsql as $$
|
||||
|
||||
declare
|
||||
oldPartnerRel hs_office_relation;
|
||||
newPartnerRel hs_office_relation;
|
||||
oldPartnerDetails hs_office_partner_details;
|
||||
newPartnerDetails hs_office_partner_details;
|
||||
|
||||
begin
|
||||
call enterTriggerForObjectUuid(NEW.uuid);
|
||||
|
||||
SELECT * FROM hs_office_relation WHERE uuid = OLD.partnerRelUuid INTO oldPartnerRel;
|
||||
assert oldPartnerRel.uuid is not null, format('oldPartnerRel must not be null for OLD.partnerRelUuid = %s', OLD.partnerRelUuid);
|
||||
|
||||
SELECT * FROM hs_office_relation WHERE uuid = NEW.partnerRelUuid INTO newPartnerRel;
|
||||
assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.partnerRelUuid = %s', NEW.partnerRelUuid);
|
||||
|
||||
SELECT * FROM hs_office_partner_details WHERE uuid = OLD.detailsUuid INTO oldPartnerDetails;
|
||||
assert oldPartnerDetails.uuid is not null, format('oldPartnerDetails must not be null for OLD.detailsUuid = %s', OLD.detailsUuid);
|
||||
|
||||
SELECT * FROM hs_office_partner_details WHERE uuid = NEW.detailsUuid INTO newPartnerDetails;
|
||||
assert newPartnerDetails.uuid is not null, format('newPartnerDetails must not be null for NEW.detailsUuid = %s', NEW.detailsUuid);
|
||||
|
||||
|
||||
if NEW.partnerRelUuid <> OLD.partnerRelUuid then
|
||||
|
||||
call revokePermissionFromRole(getPermissionId(OLD.uuid, 'DELETE'), hsOfficeRelationAdmin(oldPartnerRel));
|
||||
call grantPermissionToRole(createPermission(NEW.uuid, 'DELETE'), hsOfficeRelationAdmin(newPartnerRel));
|
||||
|
||||
call revokePermissionFromRole(getPermissionId(OLD.uuid, 'UPDATE'), hsOfficeRelationAgent(oldPartnerRel));
|
||||
call grantPermissionToRole(createPermission(NEW.uuid, 'UPDATE'), hsOfficeRelationAgent(newPartnerRel));
|
||||
|
||||
call revokePermissionFromRole(getPermissionId(OLD.uuid, 'SELECT'), hsOfficeRelationTenant(oldPartnerRel));
|
||||
call grantPermissionToRole(createPermission(NEW.uuid, 'SELECT'), hsOfficeRelationTenant(newPartnerRel));
|
||||
|
||||
call revokePermissionFromRole(getPermissionId(oldPartnerDetails.uuid, 'DELETE'), hsOfficeRelationAdmin(oldPartnerRel));
|
||||
call grantPermissionToRole(createPermission(newPartnerDetails.uuid, 'DELETE'), hsOfficeRelationAdmin(newPartnerRel));
|
||||
|
||||
call revokePermissionFromRole(getPermissionId(oldPartnerDetails.uuid, 'UPDATE'), hsOfficeRelationAgent(oldPartnerRel));
|
||||
call grantPermissionToRole(createPermission(newPartnerDetails.uuid, 'UPDATE'), hsOfficeRelationAgent(newPartnerRel));
|
||||
|
||||
call revokePermissionFromRole(getPermissionId(oldPartnerDetails.uuid, 'SELECT'), hsOfficeRelationAgent(oldPartnerRel));
|
||||
call grantPermissionToRole(createPermission(newPartnerDetails.uuid, 'SELECT'), hsOfficeRelationAgent(newPartnerRel));
|
||||
|
||||
end if;
|
||||
|
||||
call leaveTriggerForObjectUuid(NEW.uuid);
|
||||
end; $$;
|
||||
|
||||
/*
|
||||
AFTER INSERT TRIGGER to re-wire the grant structure for a new hs_office_partner row.
|
||||
*/
|
||||
|
||||
create or replace function updateTriggerForHsOfficePartner_tf()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
strict as $$
|
||||
begin
|
||||
call updateRbacRulesForHsOfficePartner(OLD, NEW);
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
create trigger updateTriggerForHsOfficePartner_tg
|
||||
after update on hs_office_partner
|
||||
for each row
|
||||
execute procedure updateTriggerForHsOfficePartner_tf();
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-partner-rbac-INSERT:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
Creates INSERT INTO hs_office_partner permissions for the related global rows.
|
||||
*/
|
||||
do language plpgsql $$
|
||||
declare
|
||||
row global;
|
||||
permissionUuid uuid;
|
||||
roleUuid uuid;
|
||||
begin
|
||||
call defineContext('create INSERT INTO hs_office_partner permissions for the related global rows');
|
||||
|
||||
FOR row IN SELECT * FROM global
|
||||
LOOP
|
||||
roleUuid := findRoleId(globalAdmin());
|
||||
permissionUuid := createPermission(row.uuid, 'INSERT', 'hs_office_partner');
|
||||
call grantPermissionToRole(permissionUuid, roleUuid);
|
||||
END LOOP;
|
||||
END;
|
||||
$$;
|
||||
|
||||
/**
|
||||
Adds hs_office_partner INSERT permission to specified role of new global rows.
|
||||
*/
|
||||
create or replace function hs_office_partner_global_insert_tf()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
strict as $$
|
||||
begin
|
||||
call grantPermissionToRole(
|
||||
createPermission(NEW.uuid, 'INSERT', 'hs_office_partner'),
|
||||
globalAdmin());
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
-- z_... is to put it at the end of after insert triggers, to make sure the roles exist
|
||||
create trigger z_hs_office_partner_global_insert_tg
|
||||
after insert on global
|
||||
for each row
|
||||
execute procedure hs_office_partner_global_insert_tf();
|
||||
|
||||
/**
|
||||
Checks if the user or assumed roles are allowed to insert a row to hs_office_partner,
|
||||
where only global-admin has that permission.
|
||||
*/
|
||||
create or replace function hs_office_partner_insert_permission_missing_tf()
|
||||
returns trigger
|
||||
language plpgsql as $$
|
||||
begin
|
||||
raise exception '[403] insert into hs_office_partner not allowed for current subjects % (%)',
|
||||
currentSubjects(), currentSubjectsUuids();
|
||||
end; $$;
|
||||
|
||||
create trigger hs_office_partner_insert_permission_check_tg
|
||||
before insert on hs_office_partner
|
||||
for each row
|
||||
when ( not isGlobalAdmin() )
|
||||
execute procedure hs_office_partner_insert_permission_missing_tf();
|
||||
--//
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-partner-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
call generateRbacIdentityViewFromQuery('hs_office_partner',
|
||||
$idName$
|
||||
SELECT partner.partnerNumber
|
||||
|| ':' || (SELECT idName FROM hs_office_person_iv p WHERE p.uuid = partner.personUuid)
|
||||
|| '-' || (SELECT idName FROM hs_office_contact_iv c WHERE c.uuid = partner.contactUuid)
|
||||
FROM hs_office_partner AS partner
|
||||
$idName$);
|
||||
--//
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-partner-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacRestrictedView('hs_office_partner',
|
||||
$orderBy$
|
||||
SELECT partner.partnerNumber
|
||||
|| ':' || (SELECT idName FROM hs_office_person_iv p WHERE p.uuid = partner.personUuid)
|
||||
|| '-' || (SELECT idName FROM hs_office_contact_iv c WHERE c.uuid = partner.contactUuid)
|
||||
FROM hs_office_partner AS partner
|
||||
$orderBy$,
|
||||
$updates$
|
||||
partnerRelUuid = new.partnerRelUuid,
|
||||
personUuid = new.personUuid,
|
||||
contactUuid = new.contactUuid
|
||||
$updates$);
|
||||
--//
|
||||
|
@ -1,78 +1,158 @@
|
||||
### hs_office_partner RBAC
|
||||
### rbac partner
|
||||
|
||||
This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually.
|
||||
|
||||
```mermaid
|
||||
%%{init:{'flowchart':{'htmlLabels':false}}}%%
|
||||
flowchart TB
|
||||
|
||||
subgraph global
|
||||
style global fill:#eee
|
||||
|
||||
role:global.admin[global.admin]
|
||||
end
|
||||
|
||||
subgraph hsOfficeContact
|
||||
subgraph partnerRel.contact["`**partnerRel.contact**`"]
|
||||
direction TB
|
||||
style hsOfficeContact fill:#eee
|
||||
style partnerRel.contact fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
role:hsOfficeContact.admin[contact.admin]
|
||||
--> role:hsOfficeContact.tenant[contact.tenant]
|
||||
--> role:hsOfficeContact.guest[contact.guest]
|
||||
subgraph partnerRel.contact:roles[ ]
|
||||
style partnerRel.contact:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:partnerRel.contact:owner[[partnerRel.contact:owner]]
|
||||
role:partnerRel.contact:admin[[partnerRel.contact:admin]]
|
||||
role:partnerRel.contact:referrer[[partnerRel.contact:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph hsOfficePerson
|
||||
subgraph partner["`**partner**`"]
|
||||
direction TB
|
||||
style hsOfficePerson fill:#eee
|
||||
style partner fill:#dd4901,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
role:hsOfficePerson.admin[person.admin]
|
||||
--> role:hsOfficePerson.tenant[person.tenant]
|
||||
--> role:hsOfficePerson.guest[person.guest]
|
||||
subgraph partner:permissions[ ]
|
||||
style partner:permissions fill:#dd4901,stroke:white
|
||||
|
||||
perm:partner:INSERT{{partner:INSERT}}
|
||||
perm:partner:DELETE{{partner:DELETE}}
|
||||
perm:partner:UPDATE{{partner:UPDATE}}
|
||||
perm:partner:SELECT{{partner:SELECT}}
|
||||
end
|
||||
|
||||
subgraph partnerRel["`**partnerRel**`"]
|
||||
direction TB
|
||||
style partnerRel fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
subgraph partnerRel.contact["`**partnerRel.contact**`"]
|
||||
direction TB
|
||||
style partnerRel.contact fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph partnerRel.contact:roles[ ]
|
||||
style partnerRel.contact:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:partnerRel.contact:owner[[partnerRel.contact:owner]]
|
||||
role:partnerRel.contact:admin[[partnerRel.contact:admin]]
|
||||
role:partnerRel.contact:referrer[[partnerRel.contact:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph partnerRel.anchorPerson["`**partnerRel.anchorPerson**`"]
|
||||
direction TB
|
||||
style partnerRel.anchorPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph partnerRel.anchorPerson:roles[ ]
|
||||
style partnerRel.anchorPerson:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:partnerRel.anchorPerson:owner[[partnerRel.anchorPerson:owner]]
|
||||
role:partnerRel.anchorPerson:admin[[partnerRel.anchorPerson:admin]]
|
||||
role:partnerRel.anchorPerson:referrer[[partnerRel.anchorPerson:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph partnerRel.holderPerson["`**partnerRel.holderPerson**`"]
|
||||
direction TB
|
||||
style partnerRel.holderPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph partnerRel.holderPerson:roles[ ]
|
||||
style partnerRel.holderPerson:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:partnerRel.holderPerson:owner[[partnerRel.holderPerson:owner]]
|
||||
role:partnerRel.holderPerson:admin[[partnerRel.holderPerson:admin]]
|
||||
role:partnerRel.holderPerson:referrer[[partnerRel.holderPerson:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph partnerRel:roles[ ]
|
||||
style partnerRel:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:partnerRel:owner[[partnerRel:owner]]
|
||||
role:partnerRel:admin[[partnerRel:admin]]
|
||||
role:partnerRel:agent[[partnerRel:agent]]
|
||||
role:partnerRel:tenant[[partnerRel:tenant]]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
subgraph hsOfficePartnerDetails
|
||||
subgraph partnerDetails["`**partnerDetails**`"]
|
||||
direction TB
|
||||
style partnerDetails fill:#feb28c,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
perm:hsOfficePartnerDetails.*{{partner.*}}
|
||||
perm:hsOfficePartnerDetails.edit{{partner.edit}}
|
||||
perm:hsOfficePartnerDetails.view{{partner.view}}
|
||||
subgraph partnerDetails:permissions[ ]
|
||||
style partnerDetails:permissions fill:#feb28c,stroke:white
|
||||
|
||||
perm:partnerDetails:DELETE{{partnerDetails:DELETE}}
|
||||
perm:partnerDetails:UPDATE{{partnerDetails:UPDATE}}
|
||||
perm:partnerDetails:SELECT{{partnerDetails:SELECT}}
|
||||
end
|
||||
end
|
||||
|
||||
subgraph hsOfficePartner
|
||||
subgraph partnerRel.anchorPerson["`**partnerRel.anchorPerson**`"]
|
||||
direction TB
|
||||
style partnerRel.anchorPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
role:hsOfficePartner.owner[partner.owner]
|
||||
%% permissions
|
||||
role:hsOfficePartner.owner --> perm:hsOfficePartner.*{{partner.*}}
|
||||
role:hsOfficePartner.owner --> perm:hsOfficePartnerDetails.*{{partner.*}}
|
||||
%% incoming
|
||||
role:global.admin ---> role:hsOfficePartner.owner
|
||||
subgraph partnerRel.anchorPerson:roles[ ]
|
||||
style partnerRel.anchorPerson:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:hsOfficePartner.admin[partner.admin]
|
||||
%% permissions
|
||||
role:hsOfficePartner.admin --> perm:hsOfficePartner.edit{{partner.edit}}
|
||||
role:hsOfficePartner.admin --> perm:hsOfficePartnerDetails.edit{{partner.edit}}
|
||||
%% incoming
|
||||
role:hsOfficePartner.owner ---> role:hsOfficePartner.admin
|
||||
%% outgoing
|
||||
role:hsOfficePartner.admin --> role:hsOfficePerson.tenant
|
||||
role:hsOfficePartner.admin --> role:hsOfficeContact.tenant
|
||||
|
||||
role:hsOfficePartner.agent[partner.agent]
|
||||
%% permissions
|
||||
role:hsOfficePartner.agent --> perm:hsOfficePartnerDetails.view{{partner.view}}
|
||||
%% incoming
|
||||
role:hsOfficePartner.admin ---> role:hsOfficePartner.agent
|
||||
role:hsOfficePerson.admin --> role:hsOfficePartner.agent
|
||||
role:hsOfficeContact.admin --> role:hsOfficePartner.agent
|
||||
|
||||
role:hsOfficePartner.tenant[partner.tenant]
|
||||
%% incoming
|
||||
role:hsOfficePartner.agent --> role:hsOfficePartner.tenant
|
||||
%% outgoing
|
||||
role:hsOfficePartner.tenant --> role:hsOfficePerson.guest
|
||||
role:hsOfficePartner.tenant --> role:hsOfficeContact.guest
|
||||
|
||||
role:hsOfficePartner.guest[partner.guest]
|
||||
%% permissions
|
||||
role:hsOfficePartner.guest --> perm:hsOfficePartner.view{{partner.view}}
|
||||
%% incoming
|
||||
role:hsOfficePartner.tenant --> role:hsOfficePartner.guest
|
||||
role:partnerRel.anchorPerson:owner[[partnerRel.anchorPerson:owner]]
|
||||
role:partnerRel.anchorPerson:admin[[partnerRel.anchorPerson:admin]]
|
||||
role:partnerRel.anchorPerson:referrer[[partnerRel.anchorPerson:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph partnerRel.holderPerson["`**partnerRel.holderPerson**`"]
|
||||
direction TB
|
||||
style partnerRel.holderPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph partnerRel.holderPerson:roles[ ]
|
||||
style partnerRel.holderPerson:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:partnerRel.holderPerson:owner[[partnerRel.holderPerson:owner]]
|
||||
role:partnerRel.holderPerson:admin[[partnerRel.holderPerson:admin]]
|
||||
role:partnerRel.holderPerson:referrer[[partnerRel.holderPerson:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
%% granting roles to roles
|
||||
role:global:admin -.-> role:partnerRel.anchorPerson:owner
|
||||
role:partnerRel.anchorPerson:owner -.-> role:partnerRel.anchorPerson:admin
|
||||
role:partnerRel.anchorPerson:admin -.-> role:partnerRel.anchorPerson:referrer
|
||||
role:global:admin -.-> role:partnerRel.holderPerson:owner
|
||||
role:partnerRel.holderPerson:owner -.-> role:partnerRel.holderPerson:admin
|
||||
role:partnerRel.holderPerson:admin -.-> role:partnerRel.holderPerson:referrer
|
||||
role:global:admin -.-> role:partnerRel.contact:owner
|
||||
role:partnerRel.contact:owner -.-> role:partnerRel.contact:admin
|
||||
role:partnerRel.contact:admin -.-> role:partnerRel.contact:referrer
|
||||
role:global:admin -.-> role:partnerRel:owner
|
||||
role:partnerRel:owner -.-> role:partnerRel:admin
|
||||
role:partnerRel.anchorPerson:admin -.-> role:partnerRel:admin
|
||||
role:partnerRel:admin -.-> role:partnerRel:agent
|
||||
role:partnerRel.holderPerson:admin -.-> role:partnerRel:agent
|
||||
role:partnerRel:agent -.-> role:partnerRel:tenant
|
||||
role:partnerRel.holderPerson:admin -.-> role:partnerRel:tenant
|
||||
role:partnerRel.contact:admin -.-> role:partnerRel:tenant
|
||||
role:partnerRel:tenant -.-> role:partnerRel.anchorPerson:referrer
|
||||
role:partnerRel:tenant -.-> role:partnerRel.holderPerson:referrer
|
||||
role:partnerRel:tenant -.-> role:partnerRel.contact:referrer
|
||||
|
||||
%% granting permissions to roles
|
||||
role:global:admin ==> perm:partner:INSERT
|
||||
role:partnerRel:admin ==> perm:partner:DELETE
|
||||
role:partnerRel:agent ==> perm:partner:UPDATE
|
||||
role:partnerRel:tenant ==> perm:partner:SELECT
|
||||
role:partnerRel:admin ==> perm:partnerDetails:DELETE
|
||||
role:partnerRel:agent ==> perm:partnerDetails:UPDATE
|
||||
role:partnerRel:agent ==> perm:partnerDetails:SELECT
|
||||
|
||||
```
|
||||
|
@ -1,4 +1,6 @@
|
||||
--liquibase formatted sql
|
||||
-- This code generated was by RbacViewPostgresGenerator, do not amend manually.
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-partner-rbac-OBJECT:1 endDelimiter:--//
|
||||
@ -15,242 +17,222 @@ call generateRbacRoleDescriptors('hsOfficePartner', 'hs_office_partner');
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-partner-rbac-ROLES-CREATION:1 endDelimiter:--//
|
||||
--changeset hs-office-partner-rbac-insert-trigger:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
Creates and updates the roles and their assignments for partner entities.
|
||||
Creates the roles, grants and permission for the AFTER INSERT TRIGGER.
|
||||
*/
|
||||
|
||||
create or replace function hsOfficePartnerRbacRolesTrigger()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
strict as $$
|
||||
create or replace procedure buildRbacSystemForHsOfficePartner(
|
||||
NEW hs_office_partner
|
||||
)
|
||||
language plpgsql as $$
|
||||
|
||||
declare
|
||||
oldPartnerRel hs_office_relation;
|
||||
newPartnerRel hs_office_relation;
|
||||
newPartnerRel hs_office_relation;
|
||||
newPartnerDetails hs_office_partner_details;
|
||||
|
||||
oldPerson hs_office_person;
|
||||
newPerson hs_office_person;
|
||||
|
||||
oldContact hs_office_contact;
|
||||
newContact hs_office_contact;
|
||||
begin
|
||||
call enterTriggerForObjectUuid(NEW.uuid);
|
||||
|
||||
select * from hs_office_relation as r where r.uuid = NEW.partnerReluuid into newPartnerRel;
|
||||
select * from hs_office_person as p where p.uuid = NEW.personUuid into newPerson;
|
||||
select * from hs_office_contact as c where c.uuid = NEW.contactUuid into newContact;
|
||||
SELECT * FROM hs_office_relation WHERE uuid = NEW.partnerRelUuid INTO newPartnerRel;
|
||||
assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.partnerRelUuid = %s', NEW.partnerRelUuid);
|
||||
|
||||
if TG_OP = 'INSERT' then
|
||||
SELECT * FROM hs_office_partner_details WHERE uuid = NEW.detailsUuid INTO newPartnerDetails;
|
||||
assert newPartnerDetails.uuid is not null, format('newPartnerDetails must not be null for NEW.detailsUuid = %s', NEW.detailsUuid);
|
||||
|
||||
-- === ATTENTION: code generated from related Mermaid flowchart: ===
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficePartnerOwner(NEW),
|
||||
permissions => array['DELETE'],
|
||||
incomingSuperRoles => array[globalAdmin()]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficePartnerAdmin(NEW),
|
||||
permissions => array['UPDATE'],
|
||||
incomingSuperRoles => array[
|
||||
hsOfficePartnerOwner(NEW)],
|
||||
outgoingSubRoles => array[
|
||||
hsOfficeRelationTenant(newPartnerRel),
|
||||
hsOfficePersonTenant(newPerson),
|
||||
hsOfficeContactTenant(newContact)]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficePartnerAgent(NEW),
|
||||
incomingSuperRoles => array[
|
||||
hsOfficePartnerAdmin(NEW),
|
||||
hsOfficeRelationAdmin(newPartnerRel),
|
||||
hsOfficePersonAdmin(newPerson),
|
||||
hsOfficeContactAdmin(newContact)]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficePartnerTenant(NEW),
|
||||
incomingSuperRoles => array[
|
||||
hsOfficePartnerAgent(NEW)],
|
||||
outgoingSubRoles => array[
|
||||
hsOfficeRelationTenant(newPartnerRel),
|
||||
hsOfficePersonGuest(newPerson),
|
||||
hsOfficeContactGuest(newContact)]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficePartnerGuest(NEW),
|
||||
permissions => array['SELECT'],
|
||||
incomingSuperRoles => array[hsOfficePartnerTenant(NEW)]
|
||||
);
|
||||
|
||||
-- === END of code generated from Mermaid flowchart. ===
|
||||
|
||||
-- Each partner-details entity belong exactly to one partner entity
|
||||
-- and it makes little sense just to delegate partner-details roles.
|
||||
-- Therefore, we did not model partner-details roles,
|
||||
-- but instead just assign extra permissions to existing partner-roles.
|
||||
|
||||
--Attention: Cannot be in partner-details because of insert order (partner is not in database yet)
|
||||
|
||||
call grantPermissionsToRole(
|
||||
getRoleId(hsOfficePartnerOwner(NEW)),
|
||||
createPermissions(NEW.detailsUuid, array ['DELETE'])
|
||||
);
|
||||
|
||||
call grantPermissionsToRole(
|
||||
getRoleId(hsOfficePartnerAdmin(NEW)),
|
||||
createPermissions(NEW.detailsUuid, array ['UPDATE'])
|
||||
);
|
||||
|
||||
call grantPermissionsToRole(
|
||||
-- Yes, here hsOfficePartnerAGENT is used, not hsOfficePartnerTENANT.
|
||||
-- Do NOT grant view permission on partner-details to hsOfficePartnerTENANT!
|
||||
-- Otherwise package-admins etc. would be able to read the data.
|
||||
getRoleId(hsOfficePartnerAgent(NEW)),
|
||||
createPermissions(NEW.detailsUuid, array ['SELECT'])
|
||||
);
|
||||
|
||||
|
||||
elsif TG_OP = 'UPDATE' then
|
||||
|
||||
if OLD.partnerRelUuid <> NEW.partnerRelUuid then
|
||||
select * from hs_office_relation as r where r.uuid = OLD.partnerRelUuid into oldPartnerRel;
|
||||
|
||||
call revokeRoleFromRole(hsOfficeRelationTenant(oldPartnerRel), hsOfficePartnerAdmin(OLD));
|
||||
call grantRoleToRole(hsOfficeRelationTenant(newPartnerRel), hsOfficePartnerAdmin(NEW));
|
||||
|
||||
call revokeRoleFromRole(hsOfficePartnerAgent(OLD), hsOfficeRelationAdmin(oldPartnerRel));
|
||||
call grantRoleToRole(hsOfficePartnerAgent(NEW), hsOfficeRelationAdmin(newPartnerRel));
|
||||
|
||||
call revokeRoleFromRole(hsOfficeRelationGuest(oldPartnerRel), hsOfficePartnerTenant(OLD));
|
||||
call grantRoleToRole(hsOfficeRelationGuest(newPartnerRel), hsOfficePartnerTenant(NEW));
|
||||
end if;
|
||||
|
||||
if OLD.personUuid <> NEW.personUuid then
|
||||
select * from hs_office_person as p where p.uuid = OLD.personUuid into oldPerson;
|
||||
|
||||
call revokeRoleFromRole(hsOfficePersonTenant(oldPerson), hsOfficePartnerAdmin(OLD));
|
||||
call grantRoleToRole(hsOfficePersonTenant(newPerson), hsOfficePartnerAdmin(NEW));
|
||||
|
||||
call revokeRoleFromRole(hsOfficePartnerAgent(OLD), hsOfficePersonAdmin(oldPerson));
|
||||
call grantRoleToRole(hsOfficePartnerAgent(NEW), hsOfficePersonAdmin(newPerson));
|
||||
|
||||
call revokeRoleFromRole(hsOfficePersonGuest(oldPerson), hsOfficePartnerTenant(OLD));
|
||||
call grantRoleToRole(hsOfficePersonGuest(newPerson), hsOfficePartnerTenant(NEW));
|
||||
end if;
|
||||
|
||||
if OLD.contactUuid <> NEW.contactUuid then
|
||||
select * from hs_office_contact as c where c.uuid = OLD.contactUuid into oldContact;
|
||||
|
||||
call revokeRoleFromRole(hsOfficeContactTenant(oldContact), hsOfficePartnerAdmin(OLD));
|
||||
call grantRoleToRole(hsOfficeContactTenant(newContact), hsOfficePartnerAdmin(NEW));
|
||||
|
||||
call revokeRoleFromRole(hsOfficePartnerAgent(OLD), hsOfficeContactAdmin(oldContact));
|
||||
call grantRoleToRole(hsOfficePartnerAgent(NEW), hsOfficeContactAdmin(newContact));
|
||||
|
||||
call revokeRoleFromRole(hsOfficeContactGuest(oldContact), hsOfficePartnerTenant(OLD));
|
||||
call grantRoleToRole(hsOfficeContactGuest(newContact), hsOfficePartnerTenant(NEW));
|
||||
end if;
|
||||
else
|
||||
raise exception 'invalid usage of TRIGGER';
|
||||
end if;
|
||||
call grantPermissionToRole(createPermission(NEW.uuid, 'DELETE'), hsOfficeRelationAdmin(newPartnerRel));
|
||||
call grantPermissionToRole(createPermission(NEW.uuid, 'SELECT'), hsOfficeRelationTenant(newPartnerRel));
|
||||
call grantPermissionToRole(createPermission(NEW.uuid, 'UPDATE'), hsOfficeRelationAgent(newPartnerRel));
|
||||
call grantPermissionToRole(createPermission(newPartnerDetails.uuid, 'DELETE'), hsOfficeRelationAdmin(newPartnerRel));
|
||||
call grantPermissionToRole(createPermission(newPartnerDetails.uuid, 'SELECT'), hsOfficeRelationAgent(newPartnerRel));
|
||||
call grantPermissionToRole(createPermission(newPartnerDetails.uuid, 'UPDATE'), hsOfficeRelationAgent(newPartnerRel));
|
||||
|
||||
call leaveTriggerForObjectUuid(NEW.uuid);
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
/*
|
||||
An AFTER INSERT TRIGGER which creates the role structure for a new customer.
|
||||
AFTER INSERT TRIGGER to create the role+grant structure for a new hs_office_partner row.
|
||||
*/
|
||||
create trigger createRbacRolesForHsOfficePartner_Trigger
|
||||
after insert
|
||||
on hs_office_partner
|
||||
for each row
|
||||
execute procedure hsOfficePartnerRbacRolesTrigger();
|
||||
|
||||
/*
|
||||
An AFTER UPDATE TRIGGER which updates the role structure of a customer.
|
||||
*/
|
||||
create trigger updateRbacRolesForHsOfficePartner_Trigger
|
||||
after update
|
||||
on hs_office_partner
|
||||
create or replace function insertTriggerForHsOfficePartner_tf()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
strict as $$
|
||||
begin
|
||||
call buildRbacSystemForHsOfficePartner(NEW);
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
create trigger insertTriggerForHsOfficePartner_tg
|
||||
after insert on hs_office_partner
|
||||
for each row
|
||||
execute procedure hsOfficePartnerRbacRolesTrigger();
|
||||
execute procedure insertTriggerForHsOfficePartner_tf();
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-partner-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
||||
--changeset hs-office-partner-rbac-update-trigger:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacIdentityViewFromProjection('hs_office_partner', $idName$
|
||||
partnerNumber || ':' ||
|
||||
(select idName from hs_office_person_iv p where p.uuid = target.personuuid)
|
||||
|| '-' ||
|
||||
(select idName from hs_office_contact_iv c where c.uuid = target.contactuuid)
|
||||
$idName$);
|
||||
|
||||
/*
|
||||
Called from the AFTER UPDATE TRIGGER to re-wire the grants.
|
||||
*/
|
||||
|
||||
create or replace procedure updateRbacRulesForHsOfficePartner(
|
||||
OLD hs_office_partner,
|
||||
NEW hs_office_partner
|
||||
)
|
||||
language plpgsql as $$
|
||||
|
||||
declare
|
||||
oldPartnerRel hs_office_relation;
|
||||
newPartnerRel hs_office_relation;
|
||||
oldPartnerDetails hs_office_partner_details;
|
||||
newPartnerDetails hs_office_partner_details;
|
||||
|
||||
begin
|
||||
call enterTriggerForObjectUuid(NEW.uuid);
|
||||
|
||||
SELECT * FROM hs_office_relation WHERE uuid = OLD.partnerRelUuid INTO oldPartnerRel;
|
||||
assert oldPartnerRel.uuid is not null, format('oldPartnerRel must not be null for OLD.partnerRelUuid = %s', OLD.partnerRelUuid);
|
||||
|
||||
SELECT * FROM hs_office_relation WHERE uuid = NEW.partnerRelUuid INTO newPartnerRel;
|
||||
assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.partnerRelUuid = %s', NEW.partnerRelUuid);
|
||||
|
||||
SELECT * FROM hs_office_partner_details WHERE uuid = OLD.detailsUuid INTO oldPartnerDetails;
|
||||
assert oldPartnerDetails.uuid is not null, format('oldPartnerDetails must not be null for OLD.detailsUuid = %s', OLD.detailsUuid);
|
||||
|
||||
SELECT * FROM hs_office_partner_details WHERE uuid = NEW.detailsUuid INTO newPartnerDetails;
|
||||
assert newPartnerDetails.uuid is not null, format('newPartnerDetails must not be null for NEW.detailsUuid = %s', NEW.detailsUuid);
|
||||
|
||||
|
||||
if NEW.partnerRelUuid <> OLD.partnerRelUuid then
|
||||
|
||||
call revokePermissionFromRole(getPermissionId(OLD.uuid, 'DELETE'), hsOfficeRelationAdmin(oldPartnerRel));
|
||||
call grantPermissionToRole(createPermission(NEW.uuid, 'DELETE'), hsOfficeRelationAdmin(newPartnerRel));
|
||||
|
||||
call revokePermissionFromRole(getPermissionId(OLD.uuid, 'UPDATE'), hsOfficeRelationAgent(oldPartnerRel));
|
||||
call grantPermissionToRole(createPermission(NEW.uuid, 'UPDATE'), hsOfficeRelationAgent(newPartnerRel));
|
||||
|
||||
call revokePermissionFromRole(getPermissionId(OLD.uuid, 'SELECT'), hsOfficeRelationTenant(oldPartnerRel));
|
||||
call grantPermissionToRole(createPermission(NEW.uuid, 'SELECT'), hsOfficeRelationTenant(newPartnerRel));
|
||||
|
||||
call revokePermissionFromRole(getPermissionId(oldPartnerDetails.uuid, 'DELETE'), hsOfficeRelationAdmin(oldPartnerRel));
|
||||
call grantPermissionToRole(createPermission(newPartnerDetails.uuid, 'DELETE'), hsOfficeRelationAdmin(newPartnerRel));
|
||||
|
||||
call revokePermissionFromRole(getPermissionId(oldPartnerDetails.uuid, 'UPDATE'), hsOfficeRelationAgent(oldPartnerRel));
|
||||
call grantPermissionToRole(createPermission(newPartnerDetails.uuid, 'UPDATE'), hsOfficeRelationAgent(newPartnerRel));
|
||||
|
||||
call revokePermissionFromRole(getPermissionId(oldPartnerDetails.uuid, 'SELECT'), hsOfficeRelationAgent(oldPartnerRel));
|
||||
call grantPermissionToRole(createPermission(newPartnerDetails.uuid, 'SELECT'), hsOfficeRelationAgent(newPartnerRel));
|
||||
|
||||
end if;
|
||||
|
||||
call leaveTriggerForObjectUuid(NEW.uuid);
|
||||
end; $$;
|
||||
|
||||
/*
|
||||
AFTER INSERT TRIGGER to re-wire the grant structure for a new hs_office_partner row.
|
||||
*/
|
||||
|
||||
create or replace function updateTriggerForHsOfficePartner_tf()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
strict as $$
|
||||
begin
|
||||
call updateRbacRulesForHsOfficePartner(OLD, NEW);
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
create trigger updateTriggerForHsOfficePartner_tg
|
||||
after update on hs_office_partner
|
||||
for each row
|
||||
execute procedure updateTriggerForHsOfficePartner_tf();
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-partner-rbac-INSERT:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
Creates INSERT INTO hs_office_partner permissions for the related global rows.
|
||||
*/
|
||||
do language plpgsql $$
|
||||
declare
|
||||
row global;
|
||||
begin
|
||||
call defineContext('create INSERT INTO hs_office_partner permissions for the related global rows');
|
||||
|
||||
FOR row IN SELECT * FROM global
|
||||
LOOP
|
||||
call grantPermissionToRole(
|
||||
createPermission(row.uuid, 'INSERT', 'hs_office_partner'),
|
||||
globalAdmin());
|
||||
END LOOP;
|
||||
END;
|
||||
$$;
|
||||
|
||||
/**
|
||||
Adds hs_office_partner INSERT permission to specified role of new global rows.
|
||||
*/
|
||||
create or replace function hs_office_partner_global_insert_tf()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
strict as $$
|
||||
begin
|
||||
call grantPermissionToRole(
|
||||
createPermission(NEW.uuid, 'INSERT', 'hs_office_partner'),
|
||||
globalAdmin());
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
-- z_... is to put it at the end of after insert triggers, to make sure the roles exist
|
||||
create trigger z_hs_office_partner_global_insert_tg
|
||||
after insert on global
|
||||
for each row
|
||||
execute procedure hs_office_partner_global_insert_tf();
|
||||
|
||||
/**
|
||||
Checks if the user or assumed roles are allowed to insert a row to hs_office_partner,
|
||||
where only global-admin has that permission.
|
||||
*/
|
||||
create or replace function hs_office_partner_insert_permission_missing_tf()
|
||||
returns trigger
|
||||
language plpgsql as $$
|
||||
begin
|
||||
raise exception '[403] insert into hs_office_partner not allowed for current subjects % (%)',
|
||||
currentSubjects(), currentSubjectsUuids();
|
||||
end; $$;
|
||||
|
||||
create trigger hs_office_partner_insert_permission_check_tg
|
||||
before insert on hs_office_partner
|
||||
for each row
|
||||
when ( not isGlobalAdmin() )
|
||||
execute procedure hs_office_partner_insert_permission_missing_tf();
|
||||
--//
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-partner-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
call generateRbacIdentityViewFromProjection('hs_office_partner',
|
||||
$idName$
|
||||
'P-' || partnerNumber
|
||||
$idName$);
|
||||
--//
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-partner-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacRestrictedView('hs_office_partner',
|
||||
'(select idName from hs_office_person_iv p where p.uuid = target.personUuid)',
|
||||
$orderBy$
|
||||
'P-' || partnerNumber
|
||||
$orderBy$,
|
||||
$updates$
|
||||
partnerRelUuid = new.partnerRelUuid,
|
||||
personUuid = new.personUuid,
|
||||
contactUuid = new.contactUuid
|
||||
partnerRelUuid = new.partnerRelUuid
|
||||
$updates$);
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-partner-rbac-NEW-PARTNER:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
/*
|
||||
Creates a global permission for new-partner and assigns it to the Hostsharing admins role.
|
||||
*/
|
||||
do language plpgsql $$
|
||||
declare
|
||||
addCustomerPermissions uuid[];
|
||||
globalObjectUuid uuid;
|
||||
globalAdminRoleUuid uuid ;
|
||||
begin
|
||||
call defineContext('granting global new-partner permission to global admin role', null, null, null);
|
||||
|
||||
globalAdminRoleUuid := findRoleId(globalAdmin());
|
||||
globalObjectUuid := (select uuid from global);
|
||||
addCustomerPermissions := createPermissions(globalObjectUuid, array ['new-partner']);
|
||||
call grantPermissionsToRole(globalAdminRoleUuid, addCustomerPermissions);
|
||||
end;
|
||||
$$;
|
||||
|
||||
/**
|
||||
Used by the trigger to prevent the add-customer to current user respectively assumed roles.
|
||||
*/
|
||||
create or replace function addHsOfficePartnerNotAllowedForCurrentSubjects()
|
||||
returns trigger
|
||||
language PLPGSQL
|
||||
as $$
|
||||
begin
|
||||
raise exception '[403] new-partner not permitted for %',
|
||||
array_to_string(currentSubjects(), ';', 'null');
|
||||
end; $$;
|
||||
|
||||
/**
|
||||
Checks if the user or assumed roles are allowed to create a new customer.
|
||||
*/
|
||||
create trigger hs_office_partner_insert_trigger
|
||||
before insert
|
||||
on hs_office_partner
|
||||
for each row
|
||||
-- TODO.spec: who is allowed to create new partners
|
||||
when ( not hasAssumedRole() )
|
||||
execute procedure addHsOfficePartnerNotAllowedForCurrentSubjects();
|
||||
--//
|
||||
|
||||
|
@ -1,136 +0,0 @@
|
||||
### rbac partnerDetails
|
||||
|
||||
This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually.
|
||||
|
||||
```mermaid
|
||||
%%{init:{'flowchart':{'htmlLabels':false}}}%%
|
||||
flowchart TB
|
||||
|
||||
subgraph partnerRel.contact["`**partnerRel.contact**`"]
|
||||
direction TB
|
||||
style partnerRel.contact fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph partnerRel.contact:roles[ ]
|
||||
style partnerRel.contact:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:partnerRel.contact:owner[[partnerRel.contact:owner]]
|
||||
role:partnerRel.contact:admin[[partnerRel.contact:admin]]
|
||||
role:partnerRel.contact:referrer[[partnerRel.contact:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph partnerDetails["`**partnerDetails**`"]
|
||||
direction TB
|
||||
style partnerDetails fill:#dd4901,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph partnerDetails:permissions[ ]
|
||||
style partnerDetails:permissions fill:#dd4901,stroke:white
|
||||
|
||||
perm:partnerDetails:INSERT{{partnerDetails:INSERT}}
|
||||
end
|
||||
|
||||
subgraph partnerRel["`**partnerRel**`"]
|
||||
direction TB
|
||||
style partnerRel fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
subgraph partnerRel.contact["`**partnerRel.contact**`"]
|
||||
direction TB
|
||||
style partnerRel.contact fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph partnerRel.contact:roles[ ]
|
||||
style partnerRel.contact:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:partnerRel.contact:owner[[partnerRel.contact:owner]]
|
||||
role:partnerRel.contact:admin[[partnerRel.contact:admin]]
|
||||
role:partnerRel.contact:referrer[[partnerRel.contact:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph partnerRel.anchorPerson["`**partnerRel.anchorPerson**`"]
|
||||
direction TB
|
||||
style partnerRel.anchorPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph partnerRel.anchorPerson:roles[ ]
|
||||
style partnerRel.anchorPerson:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:partnerRel.anchorPerson:owner[[partnerRel.anchorPerson:owner]]
|
||||
role:partnerRel.anchorPerson:admin[[partnerRel.anchorPerson:admin]]
|
||||
role:partnerRel.anchorPerson:referrer[[partnerRel.anchorPerson:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph partnerRel.holderPerson["`**partnerRel.holderPerson**`"]
|
||||
direction TB
|
||||
style partnerRel.holderPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph partnerRel.holderPerson:roles[ ]
|
||||
style partnerRel.holderPerson:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:partnerRel.holderPerson:owner[[partnerRel.holderPerson:owner]]
|
||||
role:partnerRel.holderPerson:admin[[partnerRel.holderPerson:admin]]
|
||||
role:partnerRel.holderPerson:referrer[[partnerRel.holderPerson:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph partnerRel:roles[ ]
|
||||
style partnerRel:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:partnerRel:owner[[partnerRel:owner]]
|
||||
role:partnerRel:admin[[partnerRel:admin]]
|
||||
role:partnerRel:agent[[partnerRel:agent]]
|
||||
role:partnerRel:tenant[[partnerRel:tenant]]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
subgraph partnerRel.anchorPerson["`**partnerRel.anchorPerson**`"]
|
||||
direction TB
|
||||
style partnerRel.anchorPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph partnerRel.anchorPerson:roles[ ]
|
||||
style partnerRel.anchorPerson:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:partnerRel.anchorPerson:owner[[partnerRel.anchorPerson:owner]]
|
||||
role:partnerRel.anchorPerson:admin[[partnerRel.anchorPerson:admin]]
|
||||
role:partnerRel.anchorPerson:referrer[[partnerRel.anchorPerson:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph partnerRel.holderPerson["`**partnerRel.holderPerson**`"]
|
||||
direction TB
|
||||
style partnerRel.holderPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph partnerRel.holderPerson:roles[ ]
|
||||
style partnerRel.holderPerson:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:partnerRel.holderPerson:owner[[partnerRel.holderPerson:owner]]
|
||||
role:partnerRel.holderPerson:admin[[partnerRel.holderPerson:admin]]
|
||||
role:partnerRel.holderPerson:referrer[[partnerRel.holderPerson:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
%% granting roles to roles
|
||||
role:global:admin -.-> role:partnerRel.anchorPerson:owner
|
||||
role:partnerRel.anchorPerson:owner -.-> role:partnerRel.anchorPerson:admin
|
||||
role:partnerRel.anchorPerson:admin -.-> role:partnerRel.anchorPerson:referrer
|
||||
role:global:admin -.-> role:partnerRel.holderPerson:owner
|
||||
role:partnerRel.holderPerson:owner -.-> role:partnerRel.holderPerson:admin
|
||||
role:partnerRel.holderPerson:admin -.-> role:partnerRel.holderPerson:referrer
|
||||
role:global:admin -.-> role:partnerRel.contact:owner
|
||||
role:partnerRel.contact:owner -.-> role:partnerRel.contact:admin
|
||||
role:partnerRel.contact:admin -.-> role:partnerRel.contact:referrer
|
||||
role:global:admin -.-> role:partnerRel:owner
|
||||
role:partnerRel:owner -.-> role:partnerRel:admin
|
||||
role:partnerRel.anchorPerson:admin -.-> role:partnerRel:admin
|
||||
role:partnerRel:admin -.-> role:partnerRel:agent
|
||||
role:partnerRel.holderPerson:admin -.-> role:partnerRel:agent
|
||||
role:partnerRel:agent -.-> role:partnerRel:tenant
|
||||
role:partnerRel.holderPerson:admin -.-> role:partnerRel:tenant
|
||||
role:partnerRel.contact:admin -.-> role:partnerRel:tenant
|
||||
role:partnerRel:tenant -.-> role:partnerRel.anchorPerson:referrer
|
||||
role:partnerRel:tenant -.-> role:partnerRel.holderPerson:referrer
|
||||
role:partnerRel:tenant -.-> role:partnerRel.contact:referrer
|
||||
|
||||
%% granting permissions to roles
|
||||
role:global:admin ==> perm:partnerDetails:INSERT
|
||||
|
||||
```
|
@ -1,164 +0,0 @@
|
||||
--liquibase formatted sql
|
||||
-- This code generated was by RbacViewPostgresGenerator, do not amend manually.
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-partner-details-rbac-OBJECT:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRelatedRbacObject('hs_office_partner_details');
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-partner-details-rbac-ROLE-DESCRIPTORS:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacRoleDescriptors('hsOfficePartnerDetails', 'hs_office_partner_details');
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-partner-details-rbac-insert-trigger:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
Creates the roles, grants and permission for the AFTER INSERT TRIGGER.
|
||||
*/
|
||||
|
||||
create or replace procedure buildRbacSystemForHsOfficePartnerDetails(
|
||||
NEW hs_office_partner_details
|
||||
)
|
||||
language plpgsql as $$
|
||||
|
||||
declare
|
||||
newPartnerRel hs_office_relation;
|
||||
|
||||
begin
|
||||
call enterTriggerForObjectUuid(NEW.uuid);
|
||||
|
||||
SELECT partnerRel.*
|
||||
FROM hs_office_relation AS partnerRel
|
||||
JOIN hs_office_partner AS partner
|
||||
ON partner.detailsUuid = NEW.uuid
|
||||
WHERE partnerRel.uuid = partner.partnerRelUuid
|
||||
INTO newPartnerRel;
|
||||
assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.partnerRelUuid = %s', NEW.partnerRelUuid);
|
||||
|
||||
call leaveTriggerForObjectUuid(NEW.uuid);
|
||||
end; $$;
|
||||
|
||||
/*
|
||||
AFTER INSERT TRIGGER to create the role+grant structure for a new hs_office_partner_details row.
|
||||
*/
|
||||
|
||||
create or replace function insertTriggerForHsOfficePartnerDetails_tf()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
strict as $$
|
||||
begin
|
||||
call buildRbacSystemForHsOfficePartnerDetails(NEW);
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
create trigger insertTriggerForHsOfficePartnerDetails_tg
|
||||
after insert on hs_office_partner_details
|
||||
for each row
|
||||
execute procedure insertTriggerForHsOfficePartnerDetails_tf();
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-partner-details-rbac-INSERT:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
Creates INSERT INTO hs_office_partner_details permissions for the related global rows.
|
||||
*/
|
||||
do language plpgsql $$
|
||||
declare
|
||||
row global;
|
||||
permissionUuid uuid;
|
||||
roleUuid uuid;
|
||||
begin
|
||||
call defineContext('create INSERT INTO hs_office_partner_details permissions for the related global rows');
|
||||
|
||||
FOR row IN SELECT * FROM global
|
||||
LOOP
|
||||
roleUuid := findRoleId(globalAdmin());
|
||||
permissionUuid := createPermission(row.uuid, 'INSERT', 'hs_office_partner_details');
|
||||
call grantPermissionToRole(permissionUuid, roleUuid);
|
||||
END LOOP;
|
||||
END;
|
||||
$$;
|
||||
|
||||
/**
|
||||
Adds hs_office_partner_details INSERT permission to specified role of new global rows.
|
||||
*/
|
||||
create or replace function hs_office_partner_details_global_insert_tf()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
strict as $$
|
||||
begin
|
||||
call grantPermissionToRole(
|
||||
createPermission(NEW.uuid, 'INSERT', 'hs_office_partner_details'),
|
||||
globalAdmin());
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
-- z_... is to put it at the end of after insert triggers, to make sure the roles exist
|
||||
create trigger z_hs_office_partner_details_global_insert_tg
|
||||
after insert on global
|
||||
for each row
|
||||
execute procedure hs_office_partner_details_global_insert_tf();
|
||||
|
||||
/**
|
||||
Checks if the user or assumed roles are allowed to insert a row to hs_office_partner_details,
|
||||
where only global-admin has that permission.
|
||||
*/
|
||||
create or replace function hs_office_partner_details_insert_permission_missing_tf()
|
||||
returns trigger
|
||||
language plpgsql as $$
|
||||
begin
|
||||
raise exception '[403] insert into hs_office_partner_details not allowed for current subjects % (%)',
|
||||
currentSubjects(), currentSubjectsUuids();
|
||||
end; $$;
|
||||
|
||||
create trigger hs_office_partner_details_insert_permission_check_tg
|
||||
before insert on hs_office_partner_details
|
||||
for each row
|
||||
when ( not isGlobalAdmin() )
|
||||
execute procedure hs_office_partner_details_insert_permission_missing_tf();
|
||||
--//
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-partner-details-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
call generateRbacIdentityViewFromQuery('hs_office_partner_details',
|
||||
$idName$
|
||||
SELECT partner_iv.idName || '-details'
|
||||
FROM hs_office_partner_details AS partnerDetails
|
||||
JOIN hs_office_partner partner ON partner.detailsUuid = partnerDetails.uuid
|
||||
JOIN hs_office_partner_iv partner_iv ON partner_iv.uuid = partner.uuid
|
||||
$idName$);
|
||||
--//
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-partner-details-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacRestrictedView('hs_office_partner_details',
|
||||
$orderBy$
|
||||
SELECT partner_iv.idName || '-details'
|
||||
FROM hs_office_partner_details AS partnerDetails
|
||||
JOIN hs_office_partner partner ON partner.detailsUuid = partnerDetails.uuid
|
||||
JOIN hs_office_partner_iv partner_iv ON partner_iv.uuid = partner.uuid
|
||||
$orderBy$,
|
||||
$updates$
|
||||
registrationOffice = new.registrationOffice,
|
||||
registrationNumber = new.registrationNumber,
|
||||
birthPlace = new.birthPlace,
|
||||
birthName = new.birthName,
|
||||
birthday = new.birthday,
|
||||
dateOfDeath = new.dateOfDeath
|
||||
$updates$);
|
||||
--//
|
||||
|
@ -0,0 +1,23 @@
|
||||
### rbac partnerDetails
|
||||
|
||||
This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually.
|
||||
|
||||
```mermaid
|
||||
%%{init:{'flowchart':{'htmlLabels':false}}}%%
|
||||
flowchart TB
|
||||
|
||||
subgraph partnerDetails["`**partnerDetails**`"]
|
||||
direction TB
|
||||
style partnerDetails fill:#dd4901,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph partnerDetails:permissions[ ]
|
||||
style partnerDetails:permissions fill:#dd4901,stroke:white
|
||||
|
||||
perm:partnerDetails:INSERT{{partnerDetails:INSERT}}
|
||||
end
|
||||
end
|
||||
|
||||
%% granting permissions to roles
|
||||
role:global:admin ==> perm:partnerDetails:INSERT
|
||||
|
||||
```
|
@ -1,4 +1,6 @@
|
||||
--liquibase formatted sql
|
||||
-- This code generated was by RbacViewPostgresGenerator, do not amend manually.
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-partner-details-rbac-OBJECT:1 endDelimiter:--//
|
||||
@ -8,76 +10,141 @@ call generateRelatedRbacObject('hs_office_partner_details');
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-partner-details-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
||||
--changeset hs-office-partner-details-rbac-ROLE-DESCRIPTORS:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacIdentityViewFromProjection('hs_office_partner_details', $idName$
|
||||
(select idName || '-details' from hs_office_partner_iv partner_iv
|
||||
join hs_office_partner partner on (partner_iv.uuid = partner.uuid)
|
||||
where partner.detailsUuid = target.uuid)
|
||||
$idName$);
|
||||
call generateRbacRoleDescriptors('hsOfficePartnerDetails', 'hs_office_partner_details');
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-partner-details-rbac-insert-trigger:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
Creates the roles, grants and permission for the AFTER INSERT TRIGGER.
|
||||
*/
|
||||
|
||||
create or replace procedure buildRbacSystemForHsOfficePartnerDetails(
|
||||
NEW hs_office_partner_details
|
||||
)
|
||||
language plpgsql as $$
|
||||
|
||||
declare
|
||||
|
||||
begin
|
||||
call enterTriggerForObjectUuid(NEW.uuid);
|
||||
|
||||
call leaveTriggerForObjectUuid(NEW.uuid);
|
||||
end; $$;
|
||||
|
||||
/*
|
||||
AFTER INSERT TRIGGER to create the role+grant structure for a new hs_office_partner_details row.
|
||||
*/
|
||||
|
||||
create or replace function insertTriggerForHsOfficePartnerDetails_tf()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
strict as $$
|
||||
begin
|
||||
call buildRbacSystemForHsOfficePartnerDetails(NEW);
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
create trigger insertTriggerForHsOfficePartnerDetails_tg
|
||||
after insert on hs_office_partner_details
|
||||
for each row
|
||||
execute procedure insertTriggerForHsOfficePartnerDetails_tf();
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-partner-details-rbac-INSERT:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
Creates INSERT INTO hs_office_partner_details permissions for the related global rows.
|
||||
*/
|
||||
do language plpgsql $$
|
||||
declare
|
||||
row global;
|
||||
begin
|
||||
call defineContext('create INSERT INTO hs_office_partner_details permissions for the related global rows');
|
||||
|
||||
FOR row IN SELECT * FROM global
|
||||
LOOP
|
||||
call grantPermissionToRole(
|
||||
createPermission(row.uuid, 'INSERT', 'hs_office_partner_details'),
|
||||
globalAdmin());
|
||||
END LOOP;
|
||||
END;
|
||||
$$;
|
||||
|
||||
/**
|
||||
Adds hs_office_partner_details INSERT permission to specified role of new global rows.
|
||||
*/
|
||||
create or replace function hs_office_partner_details_global_insert_tf()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
strict as $$
|
||||
begin
|
||||
call grantPermissionToRole(
|
||||
createPermission(NEW.uuid, 'INSERT', 'hs_office_partner_details'),
|
||||
globalAdmin());
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
-- z_... is to put it at the end of after insert triggers, to make sure the roles exist
|
||||
create trigger z_hs_office_partner_details_global_insert_tg
|
||||
after insert on global
|
||||
for each row
|
||||
execute procedure hs_office_partner_details_global_insert_tf();
|
||||
|
||||
/**
|
||||
Checks if the user or assumed roles are allowed to insert a row to hs_office_partner_details,
|
||||
where only global-admin has that permission.
|
||||
*/
|
||||
create or replace function hs_office_partner_details_insert_permission_missing_tf()
|
||||
returns trigger
|
||||
language plpgsql as $$
|
||||
begin
|
||||
raise exception '[403] insert into hs_office_partner_details not allowed for current subjects % (%) assumed by user % (%)',
|
||||
currentSubjects(), currentSubjectsUuids(), currentUser(), currentUserUuid();
|
||||
end; $$;
|
||||
|
||||
create trigger hs_office_partner_details_insert_permission_check_tg
|
||||
before insert on hs_office_partner_details
|
||||
for each row
|
||||
when ( not isGlobalAdmin() )
|
||||
execute procedure hs_office_partner_details_insert_permission_missing_tf();
|
||||
--//
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-partner-details-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
call generateRbacIdentityViewFromQuery('hs_office_partner_details',
|
||||
$idName$
|
||||
SELECT partnerDetails.uuid as uuid, partner_iv.idName || '-details' as idName
|
||||
FROM hs_office_partner_details AS partnerDetails
|
||||
JOIN hs_office_partner partner ON partner.detailsUuid = partnerDetails.uuid
|
||||
JOIN hs_office_partner_iv partner_iv ON partner_iv.uuid = partner.uuid
|
||||
$idName$);
|
||||
--//
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-partner-details-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacRestrictedView('hs_office_partner_details',
|
||||
'target.uuid', -- no specific order required
|
||||
$orderBy$
|
||||
uuid
|
||||
$orderBy$,
|
||||
$updates$
|
||||
registrationOffice = new.registrationOffice,
|
||||
registrationNumber = new.registrationNumber,
|
||||
birthPlace = new.birthPlace,
|
||||
birthName = new.birthName,
|
||||
birthday = new.birthday,
|
||||
dateOfDeath = new.dateOfDeath
|
||||
birthPlace = new.birthPlace,
|
||||
birthName = new.birthName,
|
||||
birthday = new.birthday,
|
||||
dateOfDeath = new.dateOfDeath
|
||||
$updates$);
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-partner-details-rbac-NEW-PARTNER-DETAILS:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
/*
|
||||
Creates a global permission for new-partner-details and assigns it to the hostsharing admins role.
|
||||
*/
|
||||
do language plpgsql $$
|
||||
declare
|
||||
addCustomerPermissions uuid[];
|
||||
globalObjectUuid uuid;
|
||||
globalAdminRoleUuid uuid ;
|
||||
begin
|
||||
call defineContext('granting global new-partner-details permission to global admin role', null, null, null);
|
||||
|
||||
globalAdminRoleUuid := findRoleId(globalAdmin());
|
||||
globalObjectUuid := (select uuid from global);
|
||||
addCustomerPermissions := createPermissions(globalObjectUuid, array ['new-partner-details']);
|
||||
call grantPermissionsToRole(globalAdminRoleUuid, addCustomerPermissions);
|
||||
end;
|
||||
$$;
|
||||
|
||||
-- TODO.refa: the code below could be moved to a generator, maybe even the code above.
|
||||
-- Additionally, the code below is not neccesary for all entities, specifiy when it is!
|
||||
|
||||
/**
|
||||
Used by the trigger to prevent the add-partner-details to current user respectively assumed roles.
|
||||
*/
|
||||
create or replace function addHsOfficePartnerDetailsNotAllowedForCurrentSubjects()
|
||||
returns trigger
|
||||
language PLPGSQL
|
||||
as $$
|
||||
begin
|
||||
raise exception '[403] new-partner-details not permitted for %',
|
||||
array_to_string(currentSubjects(), ';', 'null');
|
||||
end; $$;
|
||||
|
||||
/**
|
||||
Checks if the user or assumed roles are allowed to create new partner-details.
|
||||
*/
|
||||
create trigger hs_office_partner_details_insert_trigger
|
||||
before insert
|
||||
on hs_office_partner_details
|
||||
for each row
|
||||
when ( not hasAssumedRole() )
|
||||
execute procedure addHsOfficePartnerDetailsNotAllowedForCurrentSubjects();
|
||||
--//
|
||||
|
||||
|
@ -9,18 +9,17 @@
|
||||
Creates a single partner test record.
|
||||
*/
|
||||
create or replace procedure createHsOfficePartnerTestData(
|
||||
mandantTradeName varchar,
|
||||
partnerNumber numeric(5),
|
||||
mandantTradeName varchar,
|
||||
newPartnerNumber numeric(5),
|
||||
partnerPersonName varchar,
|
||||
contactLabel varchar )
|
||||
contactLabel varchar )
|
||||
language plpgsql as $$
|
||||
declare
|
||||
currentTask varchar;
|
||||
idName varchar;
|
||||
mandantPerson hs_office_person;
|
||||
partnerRel hs_office_relation;
|
||||
partnerRel hs_office_relation;
|
||||
relatedPerson hs_office_person;
|
||||
relatedContact hs_office_contact;
|
||||
relatedDetailsUuid uuid;
|
||||
begin
|
||||
idName := cleanIdentifier( partnerPersonName|| '-' || contactLabel);
|
||||
@ -38,9 +37,6 @@ begin
|
||||
select p.* from hs_office_person p
|
||||
where p.tradeName = partnerPersonName or p.familyName = partnerPersonName
|
||||
into relatedPerson;
|
||||
select c.* from hs_office_contact c
|
||||
where c.label = contactLabel
|
||||
into relatedContact;
|
||||
|
||||
select r.* from hs_office_relation r
|
||||
where r.type = 'PARTNER'
|
||||
@ -53,7 +49,6 @@ begin
|
||||
raise notice 'creating test partner: %', idName;
|
||||
raise notice '- using partnerRel (%): %', partnerRel.uuid, partnerRel;
|
||||
raise notice '- using person (%): %', relatedPerson.uuid, relatedPerson;
|
||||
raise notice '- using contact (%): %', relatedContact.uuid, relatedContact;
|
||||
|
||||
if relatedPerson.persontype = 'NP' then
|
||||
insert
|
||||
@ -68,8 +63,8 @@ begin
|
||||
end if;
|
||||
|
||||
insert
|
||||
into hs_office_partner (uuid, partnerNumber, partnerRelUuid, personuuid, contactuuid, detailsUuid)
|
||||
values (uuid_generate_v4(), partnerNumber, partnerRel.uuid, relatedPerson.uuid, relatedContact.uuid, relatedDetailsUuid);
|
||||
into hs_office_partner (uuid, partnerNumber, partnerRelUuid, detailsUuid)
|
||||
values (uuid_generate_v4(), newPartnerNumber, partnerRel.uuid, relatedDetailsUuid);
|
||||
end; $$;
|
||||
--//
|
||||
|
||||
|
@ -1,43 +0,0 @@
|
||||
### rbac bankAccount
|
||||
|
||||
This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually.
|
||||
|
||||
```mermaid
|
||||
%%{init:{'flowchart':{'htmlLabels':false}}}%%
|
||||
flowchart TB
|
||||
|
||||
subgraph bankAccount["`**bankAccount**`"]
|
||||
direction TB
|
||||
style bankAccount fill:#dd4901,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph bankAccount:roles[ ]
|
||||
style bankAccount:roles fill:#dd4901,stroke:white
|
||||
|
||||
role:bankAccount:owner[[bankAccount:owner]]
|
||||
role:bankAccount:admin[[bankAccount:admin]]
|
||||
role:bankAccount:referrer[[bankAccount:referrer]]
|
||||
end
|
||||
|
||||
subgraph bankAccount:permissions[ ]
|
||||
style bankAccount:permissions fill:#dd4901,stroke:white
|
||||
|
||||
perm:bankAccount:DELETE{{bankAccount:DELETE}}
|
||||
perm:bankAccount:UPDATE{{bankAccount:UPDATE}}
|
||||
perm:bankAccount:SELECT{{bankAccount:SELECT}}
|
||||
end
|
||||
end
|
||||
|
||||
%% granting roles to users
|
||||
user:creator ==> role:bankAccount:owner
|
||||
|
||||
%% granting roles to roles
|
||||
role:global:admin ==> role:bankAccount:owner
|
||||
role:bankAccount:owner ==> role:bankAccount:admin
|
||||
role:bankAccount:admin ==> role:bankAccount:referrer
|
||||
|
||||
%% granting permissions to roles
|
||||
role:bankAccount:owner ==> perm:bankAccount:DELETE
|
||||
role:bankAccount:admin ==> perm:bankAccount:UPDATE
|
||||
role:bankAccount:referrer ==> perm:bankAccount:SELECT
|
||||
|
||||
```
|
@ -1,125 +0,0 @@
|
||||
--liquibase formatted sql
|
||||
-- This code generated was by RbacViewPostgresGenerator, do not amend manually.
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-bankaccount-rbac-OBJECT:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRelatedRbacObject('hs_office_bankaccount');
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-bankaccount-rbac-ROLE-DESCRIPTORS:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacRoleDescriptors('hsOfficeBankAccount', 'hs_office_bankaccount');
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-bankaccount-rbac-insert-trigger:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
Creates the roles, grants and permission for the AFTER INSERT TRIGGER.
|
||||
*/
|
||||
|
||||
create or replace procedure buildRbacSystemForHsOfficeBankAccount(
|
||||
NEW hs_office_bankaccount
|
||||
)
|
||||
language plpgsql as $$
|
||||
|
||||
declare
|
||||
|
||||
begin
|
||||
call enterTriggerForObjectUuid(NEW.uuid);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeBankAccountOwner(NEW),
|
||||
permissions => array['DELETE'],
|
||||
incomingSuperRoles => array[globalAdmin()],
|
||||
userUuids => array[currentUserUuid()]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeBankAccountAdmin(NEW),
|
||||
permissions => array['UPDATE'],
|
||||
incomingSuperRoles => array[hsOfficeBankAccountOwner(NEW)]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeBankAccountReferrer(NEW),
|
||||
permissions => array['SELECT'],
|
||||
incomingSuperRoles => array[hsOfficeBankAccountAdmin(NEW)]
|
||||
);
|
||||
|
||||
call leaveTriggerForObjectUuid(NEW.uuid);
|
||||
end; $$;
|
||||
|
||||
/*
|
||||
AFTER INSERT TRIGGER to create the role+grant structure for a new hs_office_bankaccount row.
|
||||
*/
|
||||
|
||||
create or replace function insertTriggerForHsOfficeBankAccount_tf()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
strict as $$
|
||||
begin
|
||||
call buildRbacSystemForHsOfficeBankAccount(NEW);
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
create trigger insertTriggerForHsOfficeBankAccount_tg
|
||||
after insert on hs_office_bankaccount
|
||||
for each row
|
||||
execute procedure insertTriggerForHsOfficeBankAccount_tf();
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-bankaccount-rbac-INSERT:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
Checks if the user or assumed roles are allowed to insert a row to hs_office_bankaccount,
|
||||
where only global-admin has that permission.
|
||||
*/
|
||||
create or replace function hs_office_bankaccount_insert_permission_missing_tf()
|
||||
returns trigger
|
||||
language plpgsql as $$
|
||||
begin
|
||||
raise exception '[403] insert into hs_office_bankaccount not allowed for current subjects % (%)',
|
||||
currentSubjects(), currentSubjectsUuids();
|
||||
end; $$;
|
||||
|
||||
create trigger hs_office_bankaccount_insert_permission_check_tg
|
||||
before insert on hs_office_bankaccount
|
||||
for each row
|
||||
when ( not isGlobalAdmin() )
|
||||
execute procedure hs_office_bankaccount_insert_permission_missing_tf();
|
||||
--//
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-bankaccount-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
call generateRbacIdentityViewFromProjection('hs_office_bankaccount',
|
||||
$idName$
|
||||
iban || ':' || holder
|
||||
$idName$);
|
||||
--//
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-bankaccount-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacRestrictedView('hs_office_bankaccount',
|
||||
$orderBy$
|
||||
iban || ':' || holder
|
||||
$orderBy$,
|
||||
$updates$
|
||||
holder = new.holder,
|
||||
iban = new.iban,
|
||||
bic = new.bic
|
||||
$updates$);
|
||||
--//
|
||||
|
@ -1,40 +1,45 @@
|
||||
### hs_office_bankaccount RBAC Roles
|
||||
### rbac bankAccount
|
||||
|
||||
This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually.
|
||||
|
||||
```mermaid
|
||||
%%{init:{'flowchart':{'htmlLabels':false}}}%%
|
||||
flowchart TB
|
||||
|
||||
subgraph global
|
||||
style global fill: lightgray
|
||||
|
||||
role:global.admin[global.admin]
|
||||
end
|
||||
|
||||
subgraph hsOfficeBankAccount
|
||||
subgraph bankAccount["`**bankAccount**`"]
|
||||
direction TB
|
||||
style hsOfficeBankAccount fill: lightgreen
|
||||
style bankAccount fill:#dd4901,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
user:hsOfficeBankAccount.creator([bankAccount.creator])
|
||||
subgraph bankAccount:roles[ ]
|
||||
style bankAccount:roles fill:#dd4901,stroke:white
|
||||
|
||||
role:hsOfficeBankAccount.owner[[bankAccount.owner]]
|
||||
%% permissions
|
||||
role:hsOfficeBankAccount.owner --> perm:hsOfficeBankAccount.*{{hsOfficeBankAccount.delete}}
|
||||
%% incoming
|
||||
role:global.admin --> role:hsOfficeBankAccount.owner
|
||||
user:hsOfficeBankAccount.creator ---> role:hsOfficeBankAccount.owner
|
||||
role:bankAccount:owner[[bankAccount:owner]]
|
||||
role:bankAccount:admin[[bankAccount:admin]]
|
||||
role:bankAccount:referrer[[bankAccount:referrer]]
|
||||
end
|
||||
|
||||
role:hsOfficeBankAccount.admin[[bankAccount.admin]]
|
||||
%% incoming
|
||||
role:hsOfficeBankAccount.owner ---> role:hsOfficeBankAccount.admin
|
||||
subgraph bankAccount:permissions[ ]
|
||||
style bankAccount:permissions fill:#dd4901,stroke:white
|
||||
|
||||
role:hsOfficeBankAccount.tenant[[bankAccount.tenant]]
|
||||
%% incoming
|
||||
role:hsOfficeBankAccount.admin ---> role:hsOfficeBankAccount.tenant
|
||||
|
||||
role:hsOfficeBankAccount.guest[[bankAccount.guest]]
|
||||
%% permissions
|
||||
role:hsOfficeBankAccount.guest --> perm:hsOfficeBankAccount.view{{hsOfficeBankAccount.view}}
|
||||
%% incoming
|
||||
role:hsOfficeBankAccount.tenant ---> role:hsOfficeBankAccount.guest
|
||||
perm:bankAccount:INSERT{{bankAccount:INSERT}}
|
||||
perm:bankAccount:DELETE{{bankAccount:DELETE}}
|
||||
perm:bankAccount:UPDATE{{bankAccount:UPDATE}}
|
||||
perm:bankAccount:SELECT{{bankAccount:SELECT}}
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
%% granting roles to users
|
||||
user:creator ==> role:bankAccount:owner
|
||||
|
||||
%% granting roles to roles
|
||||
role:global:admin ==> role:bankAccount:owner
|
||||
role:bankAccount:owner ==> role:bankAccount:admin
|
||||
role:bankAccount:admin ==> role:bankAccount:referrer
|
||||
|
||||
%% granting permissions to roles
|
||||
role:global:guest ==> perm:bankAccount:INSERT
|
||||
role:bankAccount:owner ==> perm:bankAccount:DELETE
|
||||
role:bankAccount:admin ==> perm:bankAccount:UPDATE
|
||||
role:bankAccount:referrer ==> perm:bankAccount:SELECT
|
||||
|
||||
```
|
||||
|
@ -1,4 +1,6 @@
|
||||
--liquibase formatted sql
|
||||
-- This code generated was by RbacViewPostgresGenerator, do not amend manually.
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-bankaccount-rbac-OBJECT:1 endDelimiter:--//
|
||||
@ -15,125 +17,129 @@ call generateRbacRoleDescriptors('hsOfficeBankAccount', 'hs_office_bankaccount')
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-bankaccount-rbac-ROLES-CREATION:1 endDelimiter:--//
|
||||
--changeset hs-office-bankaccount-rbac-insert-trigger:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
Creates the roles and their assignments for a new bankaccount for the AFTER INSERT TRIGGER.
|
||||
Creates the roles, grants and permission for the AFTER INSERT TRIGGER.
|
||||
*/
|
||||
|
||||
create or replace function createRbacRolesForHsOfficeBankAccount()
|
||||
create or replace procedure buildRbacSystemForHsOfficeBankAccount(
|
||||
NEW hs_office_bankaccount
|
||||
)
|
||||
language plpgsql as $$
|
||||
|
||||
declare
|
||||
|
||||
begin
|
||||
call enterTriggerForObjectUuid(NEW.uuid);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeBankAccountOwner(NEW),
|
||||
permissions => array['DELETE'],
|
||||
incomingSuperRoles => array[globalAdmin()],
|
||||
userUuids => array[currentUserUuid()]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeBankAccountAdmin(NEW),
|
||||
permissions => array['UPDATE'],
|
||||
incomingSuperRoles => array[hsOfficeBankAccountOwner(NEW)]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeBankAccountReferrer(NEW),
|
||||
permissions => array['SELECT'],
|
||||
incomingSuperRoles => array[hsOfficeBankAccountAdmin(NEW)]
|
||||
);
|
||||
|
||||
call leaveTriggerForObjectUuid(NEW.uuid);
|
||||
end; $$;
|
||||
|
||||
/*
|
||||
AFTER INSERT TRIGGER to create the role+grant structure for a new hs_office_bankaccount row.
|
||||
*/
|
||||
|
||||
create or replace function insertTriggerForHsOfficeBankAccount_tf()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
strict as $$
|
||||
begin
|
||||
if TG_OP <> 'INSERT' then
|
||||
raise exception 'invalid usage of TRIGGER AFTER INSERT';
|
||||
end if;
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeBankAccountOwner(NEW),
|
||||
permissions => array['DELETE'],
|
||||
incomingSuperRoles => array[globalAdmin()],
|
||||
userUuids => array[currentUserUuid()],
|
||||
grantedByRole => globalAdmin()
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeBankAccountAdmin(NEW),
|
||||
incomingSuperRoles => array[hsOfficeBankAccountOwner(NEW)]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeBankAccountTenant(NEW),
|
||||
incomingSuperRoles => array[hsOfficeBankAccountAdmin(NEW)]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeBankAccountGuest(NEW),
|
||||
permissions => array['SELECT'],
|
||||
incomingSuperRoles => array[hsOfficeBankAccountTenant(NEW)]
|
||||
);
|
||||
|
||||
call buildRbacSystemForHsOfficeBankAccount(NEW);
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
/*
|
||||
An AFTER INSERT TRIGGER which creates the role structure for a new customer.
|
||||
*/
|
||||
|
||||
create trigger createRbacRolesForHsOfficeBankAccount_Trigger
|
||||
after insert
|
||||
on hs_office_bankaccount
|
||||
create trigger insertTriggerForHsOfficeBankAccount_tg
|
||||
after insert on hs_office_bankaccount
|
||||
for each row
|
||||
execute procedure createRbacRolesForHsOfficeBankAccount();
|
||||
execute procedure insertTriggerForHsOfficeBankAccount_tf();
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-bankaccount-rbac-INSERT:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
Creates INSERT INTO hs_office_bankaccount permissions for the related global rows.
|
||||
*/
|
||||
do language plpgsql $$
|
||||
declare
|
||||
row global;
|
||||
begin
|
||||
call defineContext('create INSERT INTO hs_office_bankaccount permissions for the related global rows');
|
||||
|
||||
FOR row IN SELECT * FROM global
|
||||
LOOP
|
||||
call grantPermissionToRole(
|
||||
createPermission(row.uuid, 'INSERT', 'hs_office_bankaccount'),
|
||||
globalGuest());
|
||||
END LOOP;
|
||||
END;
|
||||
$$;
|
||||
|
||||
/**
|
||||
Adds hs_office_bankaccount INSERT permission to specified role of new global rows.
|
||||
*/
|
||||
create or replace function hs_office_bankaccount_global_insert_tf()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
strict as $$
|
||||
begin
|
||||
call grantPermissionToRole(
|
||||
createPermission(NEW.uuid, 'INSERT', 'hs_office_bankaccount'),
|
||||
globalGuest());
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
-- z_... is to put it at the end of after insert triggers, to make sure the roles exist
|
||||
create trigger z_hs_office_bankaccount_global_insert_tg
|
||||
after insert on global
|
||||
for each row
|
||||
execute procedure hs_office_bankaccount_global_insert_tf();
|
||||
--//
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-bankaccount-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
call generateRbacIdentityViewFromProjection('hs_office_bankaccount', $idName$
|
||||
target.holder
|
||||
call generateRbacIdentityViewFromProjection('hs_office_bankaccount',
|
||||
$idName$
|
||||
iban
|
||||
$idName$);
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-bankaccount-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacRestrictedView('hs_office_bankaccount', 'target.holder',
|
||||
call generateRbacRestrictedView('hs_office_bankaccount',
|
||||
$orderBy$
|
||||
iban
|
||||
$orderBy$,
|
||||
$updates$
|
||||
holder = new.holder,
|
||||
iban = new.iban,
|
||||
bic = new.bic
|
||||
$updates$);
|
||||
--/
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-bankaccount-rbac-NEW-BANKACCOUNT:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
/*
|
||||
Creates a global permission for new-bankaccount and assigns it to the hostsharing admins role.
|
||||
*/
|
||||
do language plpgsql $$
|
||||
declare
|
||||
addCustomerPermissions uuid[];
|
||||
globalObjectUuid uuid;
|
||||
globalAdminRoleUuid uuid ;
|
||||
begin
|
||||
call defineContext('granting global new-bankaccount permission to global admin role', null, null, null);
|
||||
|
||||
globalAdminRoleUuid := findRoleId(globalAdmin());
|
||||
globalObjectUuid := (select uuid from global);
|
||||
addCustomerPermissions := createPermissions(globalObjectUuid, array ['new-bankaccount']);
|
||||
call grantPermissionsToRole(globalAdminRoleUuid, addCustomerPermissions);
|
||||
end;
|
||||
$$;
|
||||
|
||||
/**
|
||||
Used by the trigger to prevent the add-customer to current user respectively assumed roles.
|
||||
*/
|
||||
create or replace function addHsOfficeBankAccountNotAllowedForCurrentSubjects()
|
||||
returns trigger
|
||||
language PLPGSQL
|
||||
as $$
|
||||
begin
|
||||
raise exception '[403] new-bankaccount not permitted for %',
|
||||
array_to_string(currentSubjects(), ';', 'null');
|
||||
end; $$;
|
||||
|
||||
/**
|
||||
Checks if the user or assumed roles are allowed to create a new customer.
|
||||
*/
|
||||
create trigger hs_office_bankaccount_insert_trigger
|
||||
before insert
|
||||
on hs_office_bankaccount
|
||||
for each row
|
||||
-- TODO.spec: who is allowed to create new bankaccounts
|
||||
when ( not hasAssumedRole() )
|
||||
execute procedure addHsOfficeBankAccountNotAllowedForCurrentSubjects();
|
||||
--//
|
||||
|
||||
|
@ -1,178 +0,0 @@
|
||||
### rbac sepaMandate
|
||||
|
||||
This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually.
|
||||
|
||||
```mermaid
|
||||
%%{init:{'flowchart':{'htmlLabels':false}}}%%
|
||||
flowchart TB
|
||||
|
||||
subgraph bankAccount["`**bankAccount**`"]
|
||||
direction TB
|
||||
style bankAccount fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph bankAccount:roles[ ]
|
||||
style bankAccount:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:bankAccount:owner[[bankAccount:owner]]
|
||||
role:bankAccount:admin[[bankAccount:admin]]
|
||||
role:bankAccount:referrer[[bankAccount:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph debitorRel.contact["`**debitorRel.contact**`"]
|
||||
direction TB
|
||||
style debitorRel.contact fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph debitorRel.contact:roles[ ]
|
||||
style debitorRel.contact:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:debitorRel.contact:owner[[debitorRel.contact:owner]]
|
||||
role:debitorRel.contact:admin[[debitorRel.contact:admin]]
|
||||
role:debitorRel.contact:referrer[[debitorRel.contact:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph debitorRel.anchorPerson["`**debitorRel.anchorPerson**`"]
|
||||
direction TB
|
||||
style debitorRel.anchorPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph debitorRel.anchorPerson:roles[ ]
|
||||
style debitorRel.anchorPerson:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:debitorRel.anchorPerson:owner[[debitorRel.anchorPerson:owner]]
|
||||
role:debitorRel.anchorPerson:admin[[debitorRel.anchorPerson:admin]]
|
||||
role:debitorRel.anchorPerson:referrer[[debitorRel.anchorPerson:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph debitorRel.holderPerson["`**debitorRel.holderPerson**`"]
|
||||
direction TB
|
||||
style debitorRel.holderPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph debitorRel.holderPerson:roles[ ]
|
||||
style debitorRel.holderPerson:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:debitorRel.holderPerson:owner[[debitorRel.holderPerson:owner]]
|
||||
role:debitorRel.holderPerson:admin[[debitorRel.holderPerson:admin]]
|
||||
role:debitorRel.holderPerson:referrer[[debitorRel.holderPerson:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph sepaMandate["`**sepaMandate**`"]
|
||||
direction TB
|
||||
style sepaMandate fill:#dd4901,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph sepaMandate:roles[ ]
|
||||
style sepaMandate:roles fill:#dd4901,stroke:white
|
||||
|
||||
role:sepaMandate:owner[[sepaMandate:owner]]
|
||||
role:sepaMandate:admin[[sepaMandate:admin]]
|
||||
role:sepaMandate:agent[[sepaMandate:agent]]
|
||||
role:sepaMandate:referrer[[sepaMandate:referrer]]
|
||||
end
|
||||
|
||||
subgraph sepaMandate:permissions[ ]
|
||||
style sepaMandate:permissions fill:#dd4901,stroke:white
|
||||
|
||||
perm:sepaMandate:DELETE{{sepaMandate:DELETE}}
|
||||
perm:sepaMandate:UPDATE{{sepaMandate:UPDATE}}
|
||||
perm:sepaMandate:SELECT{{sepaMandate:SELECT}}
|
||||
end
|
||||
end
|
||||
|
||||
subgraph debitorRel["`**debitorRel**`"]
|
||||
direction TB
|
||||
style debitorRel fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph debitorRel.contact["`**debitorRel.contact**`"]
|
||||
direction TB
|
||||
style debitorRel.contact fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph debitorRel.contact:roles[ ]
|
||||
style debitorRel.contact:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:debitorRel.contact:owner[[debitorRel.contact:owner]]
|
||||
role:debitorRel.contact:admin[[debitorRel.contact:admin]]
|
||||
role:debitorRel.contact:referrer[[debitorRel.contact:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph debitorRel.anchorPerson["`**debitorRel.anchorPerson**`"]
|
||||
direction TB
|
||||
style debitorRel.anchorPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph debitorRel.anchorPerson:roles[ ]
|
||||
style debitorRel.anchorPerson:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:debitorRel.anchorPerson:owner[[debitorRel.anchorPerson:owner]]
|
||||
role:debitorRel.anchorPerson:admin[[debitorRel.anchorPerson:admin]]
|
||||
role:debitorRel.anchorPerson:referrer[[debitorRel.anchorPerson:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph debitorRel.holderPerson["`**debitorRel.holderPerson**`"]
|
||||
direction TB
|
||||
style debitorRel.holderPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph debitorRel.holderPerson:roles[ ]
|
||||
style debitorRel.holderPerson:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:debitorRel.holderPerson:owner[[debitorRel.holderPerson:owner]]
|
||||
role:debitorRel.holderPerson:admin[[debitorRel.holderPerson:admin]]
|
||||
role:debitorRel.holderPerson:referrer[[debitorRel.holderPerson:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph debitorRel:roles[ ]
|
||||
style debitorRel:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:debitorRel:owner[[debitorRel:owner]]
|
||||
role:debitorRel:admin[[debitorRel:admin]]
|
||||
role:debitorRel:agent[[debitorRel:agent]]
|
||||
role:debitorRel:tenant[[debitorRel:tenant]]
|
||||
end
|
||||
end
|
||||
|
||||
%% granting roles to users
|
||||
user:creator ==> role:sepaMandate:owner
|
||||
|
||||
%% granting roles to roles
|
||||
role:global:admin -.-> role:debitorRel.anchorPerson:owner
|
||||
role:debitorRel.anchorPerson:owner -.-> role:debitorRel.anchorPerson:admin
|
||||
role:debitorRel.anchorPerson:admin -.-> role:debitorRel.anchorPerson:referrer
|
||||
role:global:admin -.-> role:debitorRel.holderPerson:owner
|
||||
role:debitorRel.holderPerson:owner -.-> role:debitorRel.holderPerson:admin
|
||||
role:debitorRel.holderPerson:admin -.-> role:debitorRel.holderPerson:referrer
|
||||
role:global:admin -.-> role:debitorRel.contact:owner
|
||||
role:debitorRel.contact:owner -.-> role:debitorRel.contact:admin
|
||||
role:debitorRel.contact:admin -.-> role:debitorRel.contact:referrer
|
||||
role:global:admin -.-> role:debitorRel:owner
|
||||
role:debitorRel:owner -.-> role:debitorRel:admin
|
||||
role:debitorRel.anchorPerson:admin -.-> role:debitorRel:admin
|
||||
role:debitorRel:admin -.-> role:debitorRel:agent
|
||||
role:debitorRel.holderPerson:admin -.-> role:debitorRel:agent
|
||||
role:debitorRel:agent -.-> role:debitorRel:tenant
|
||||
role:debitorRel.holderPerson:admin -.-> role:debitorRel:tenant
|
||||
role:debitorRel.contact:admin -.-> role:debitorRel:tenant
|
||||
role:debitorRel:tenant -.-> role:debitorRel.anchorPerson:referrer
|
||||
role:debitorRel:tenant -.-> role:debitorRel.holderPerson:referrer
|
||||
role:debitorRel:tenant -.-> role:debitorRel.contact:referrer
|
||||
role:global:admin -.-> role:bankAccount:owner
|
||||
role:bankAccount:owner -.-> role:bankAccount:admin
|
||||
role:bankAccount:admin -.-> role:bankAccount:referrer
|
||||
role:global:admin ==> role:sepaMandate:owner
|
||||
role:sepaMandate:owner ==> role:sepaMandate:admin
|
||||
role:sepaMandate:admin ==> role:sepaMandate:agent
|
||||
role:sepaMandate:agent ==> role:bankAccount:referrer
|
||||
role:sepaMandate:agent ==> role:debitorRel:agent
|
||||
role:sepaMandate:agent ==> role:sepaMandate:referrer
|
||||
role:bankAccount:admin ==> role:sepaMandate:referrer
|
||||
role:debitorRel:agent ==> role:sepaMandate:referrer
|
||||
role:sepaMandate:referrer ==> role:debitorRel:tenant
|
||||
|
||||
%% granting permissions to roles
|
||||
role:sepaMandate:owner ==> perm:sepaMandate:DELETE
|
||||
role:sepaMandate:admin ==> perm:sepaMandate:UPDATE
|
||||
role:sepaMandate:referrer ==> perm:sepaMandate:SELECT
|
||||
|
||||
```
|
@ -1,143 +0,0 @@
|
||||
--liquibase formatted sql
|
||||
-- This code generated was by RbacViewPostgresGenerator, do not amend manually.
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-sepamandate-rbac-OBJECT:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRelatedRbacObject('hs_office_sepamandate');
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-sepamandate-rbac-ROLE-DESCRIPTORS:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacRoleDescriptors('hsOfficeSepaMandate', 'hs_office_sepamandate');
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-sepamandate-rbac-insert-trigger:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
Creates the roles, grants and permission for the AFTER INSERT TRIGGER.
|
||||
*/
|
||||
|
||||
create or replace procedure buildRbacSystemForHsOfficeSepaMandate(
|
||||
NEW hs_office_sepamandate
|
||||
)
|
||||
language plpgsql as $$
|
||||
|
||||
declare
|
||||
newBankAccount hs_office_bankaccount;
|
||||
newDebitorRel hs_office_relation;
|
||||
|
||||
begin
|
||||
call enterTriggerForObjectUuid(NEW.uuid);
|
||||
|
||||
SELECT * FROM hs_office_bankaccount WHERE uuid = NEW.bankAccountUuid INTO newBankAccount;
|
||||
|
||||
SELECT * FROM hs_office_relation WHERE uuid = NEW.debitorRelUuid INTO newDebitorRel;
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeSepaMandateOwner(NEW),
|
||||
permissions => array['DELETE'],
|
||||
incomingSuperRoles => array[globalAdmin()],
|
||||
userUuids => array[currentUserUuid()]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeSepaMandateAdmin(NEW),
|
||||
permissions => array['UPDATE'],
|
||||
incomingSuperRoles => array[hsOfficeSepaMandateOwner(NEW)]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeSepaMandateAgent(NEW),
|
||||
incomingSuperRoles => array[hsOfficeSepaMandateAdmin(NEW)],
|
||||
outgoingSubRoles => array[
|
||||
hsOfficeBankAccountReferrer(newBankAccount),
|
||||
hsOfficeRelationAgent(newDebitorRel)]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeSepaMandateReferrer(NEW),
|
||||
permissions => array['SELECT'],
|
||||
incomingSuperRoles => array[
|
||||
hsOfficeBankAccountAdmin(newBankAccount),
|
||||
hsOfficeRelationAgent(newDebitorRel),
|
||||
hsOfficeSepaMandateAgent(NEW)],
|
||||
outgoingSubRoles => array[hsOfficeRelationTenant(newDebitorRel)]
|
||||
);
|
||||
|
||||
call leaveTriggerForObjectUuid(NEW.uuid);
|
||||
end; $$;
|
||||
|
||||
/*
|
||||
AFTER INSERT TRIGGER to create the role+grant structure for a new hs_office_sepamandate row.
|
||||
*/
|
||||
|
||||
create or replace function insertTriggerForHsOfficeSepaMandate_tf()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
strict as $$
|
||||
begin
|
||||
call buildRbacSystemForHsOfficeSepaMandate(NEW);
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
create trigger insertTriggerForHsOfficeSepaMandate_tg
|
||||
after insert on hs_office_sepamandate
|
||||
for each row
|
||||
execute procedure insertTriggerForHsOfficeSepaMandate_tf();
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-sepamandate-rbac-INSERT:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
Checks if the user or assumed roles are allowed to insert a row to hs_office_sepamandate,
|
||||
where only global-admin has that permission.
|
||||
*/
|
||||
create or replace function hs_office_sepamandate_insert_permission_missing_tf()
|
||||
returns trigger
|
||||
language plpgsql as $$
|
||||
begin
|
||||
raise exception '[403] insert into hs_office_sepamandate not allowed for current subjects % (%)',
|
||||
currentSubjects(), currentSubjectsUuids();
|
||||
end; $$;
|
||||
|
||||
create trigger hs_office_sepamandate_insert_permission_check_tg
|
||||
before insert on hs_office_sepamandate
|
||||
for each row
|
||||
when ( not isGlobalAdmin() )
|
||||
execute procedure hs_office_sepamandate_insert_permission_missing_tf();
|
||||
--//
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-sepamandate-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
call generateRbacIdentityViewFromProjection('hs_office_sepamandate',
|
||||
$idName$
|
||||
concat(tradeName, familyName, givenName)
|
||||
$idName$);
|
||||
--//
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-sepamandate-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacRestrictedView('hs_office_sepamandate',
|
||||
$orderBy$
|
||||
concat(tradeName, familyName, givenName)
|
||||
$orderBy$,
|
||||
$updates$
|
||||
reference = new.reference,
|
||||
agreement = new.agreement,
|
||||
validity = new.validity
|
||||
$updates$);
|
||||
--//
|
||||
|
@ -1,71 +1,180 @@
|
||||
### hs_office_sepaMandate RBAC
|
||||
### rbac sepaMandate
|
||||
|
||||
This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually.
|
||||
|
||||
```mermaid
|
||||
%%{init:{'flowchart':{'htmlLabels':false}}}%%
|
||||
flowchart TB
|
||||
|
||||
subgraph global
|
||||
style global fill:#eee
|
||||
|
||||
role:global.admin[global.admin]
|
||||
end
|
||||
|
||||
subgraph hsOfficeBankAccount
|
||||
subgraph bankAccount["`**bankAccount**`"]
|
||||
direction TB
|
||||
style hsOfficeBankAccount fill:#eee
|
||||
style bankAccount fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
role:hsOfficeBankAccount.owner[bankAccount.owner]
|
||||
--> role:hsOfficeBankAccount.admin[bankAccount.admin]
|
||||
--> role:hsOfficeBankAccount.tenant[bankAccount.tenant]
|
||||
--> role:hsOfficeBankAccount.guest[bankAccount.guest]
|
||||
subgraph bankAccount:roles[ ]
|
||||
style bankAccount:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:bankAccount:owner[[bankAccount:owner]]
|
||||
role:bankAccount:admin[[bankAccount:admin]]
|
||||
role:bankAccount:referrer[[bankAccount:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph hsOfficeDebitor
|
||||
subgraph debitorRel.contact["`**debitorRel.contact**`"]
|
||||
direction TB
|
||||
style hsOfficeDebitor fill:#eee
|
||||
style debitorRel.contact fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
role:hsOfficeDebitor.owner[debitor.admin]
|
||||
--> role:hsOfficeDebitor.admin[debitor.admin]
|
||||
--> role:hsOfficeDebitor.agent[debitor.agent]
|
||||
--> role:hsOfficeDebitor.tenant[debitor.tenant]
|
||||
--> role:hsOfficeDebitor.guest[debitor.guest]
|
||||
subgraph debitorRel.contact:roles[ ]
|
||||
style debitorRel.contact:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:debitorRel.contact:owner[[debitorRel.contact:owner]]
|
||||
role:debitorRel.contact:admin[[debitorRel.contact:admin]]
|
||||
role:debitorRel.contact:referrer[[debitorRel.contact:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph hsOfficeSepaMandate
|
||||
subgraph debitorRel.anchorPerson["`**debitorRel.anchorPerson**`"]
|
||||
direction TB
|
||||
style debitorRel.anchorPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
role:hsOfficeSepaMandate.owner[sepaMandate.owner]
|
||||
%% permissions
|
||||
role:hsOfficeSepaMandate.owner --> perm:hsOfficeSepaMandate.*{{sepaMandate.*}}
|
||||
%% incoming
|
||||
role:global.admin ---> role:hsOfficeSepaMandate.owner
|
||||
subgraph debitorRel.anchorPerson:roles[ ]
|
||||
style debitorRel.anchorPerson:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:hsOfficeSepaMandate.admin[sepaMandate.admin]
|
||||
%% permissions
|
||||
role:hsOfficeSepaMandate.admin --> perm:hsOfficeSepaMandate.edit{{sepaMandate.edit}}
|
||||
%% incoming
|
||||
role:hsOfficeSepaMandate.owner ---> role:hsOfficeSepaMandate.admin
|
||||
|
||||
role:hsOfficeSepaMandate.agent[sepaMandate.agent]
|
||||
%% incoming
|
||||
role:hsOfficeSepaMandate.admin ---> role:hsOfficeSepaMandate.agent
|
||||
role:hsOfficeDebitor.admin --> role:hsOfficeSepaMandate.agent
|
||||
role:hsOfficeBankAccount.admin --> role:hsOfficeSepaMandate.agent
|
||||
%% outgoing
|
||||
role:hsOfficeSepaMandate.agent --> role:hsOfficeDebitor.tenant
|
||||
role:hsOfficeSepaMandate.admin --> role:hsOfficeBankAccount.tenant
|
||||
|
||||
role:hsOfficeSepaMandate.tenant[sepaMandate.tenant]
|
||||
%% incoming
|
||||
role:hsOfficeSepaMandate.agent --> role:hsOfficeSepaMandate.tenant
|
||||
%% outgoing
|
||||
role:hsOfficeSepaMandate.tenant --> role:hsOfficeDebitor.guest
|
||||
role:hsOfficeSepaMandate.tenant --> role:hsOfficeBankAccount.guest
|
||||
|
||||
role:hsOfficeSepaMandate.guest[sepaMandate.guest]
|
||||
%% permissions
|
||||
role:hsOfficeSepaMandate.guest --> perm:hsOfficeSepaMandate.view{{sepaMandate.view}}
|
||||
%% incoming
|
||||
role:hsOfficeSepaMandate.tenant --> role:hsOfficeSepaMandate.guest
|
||||
role:debitorRel.anchorPerson:owner[[debitorRel.anchorPerson:owner]]
|
||||
role:debitorRel.anchorPerson:admin[[debitorRel.anchorPerson:admin]]
|
||||
role:debitorRel.anchorPerson:referrer[[debitorRel.anchorPerson:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph debitorRel.holderPerson["`**debitorRel.holderPerson**`"]
|
||||
direction TB
|
||||
style debitorRel.holderPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph debitorRel.holderPerson:roles[ ]
|
||||
style debitorRel.holderPerson:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:debitorRel.holderPerson:owner[[debitorRel.holderPerson:owner]]
|
||||
role:debitorRel.holderPerson:admin[[debitorRel.holderPerson:admin]]
|
||||
role:debitorRel.holderPerson:referrer[[debitorRel.holderPerson:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph sepaMandate["`**sepaMandate**`"]
|
||||
direction TB
|
||||
style sepaMandate fill:#dd4901,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph sepaMandate:roles[ ]
|
||||
style sepaMandate:roles fill:#dd4901,stroke:white
|
||||
|
||||
role:sepaMandate:owner[[sepaMandate:owner]]
|
||||
role:sepaMandate:admin[[sepaMandate:admin]]
|
||||
role:sepaMandate:agent[[sepaMandate:agent]]
|
||||
role:sepaMandate:referrer[[sepaMandate:referrer]]
|
||||
end
|
||||
|
||||
subgraph sepaMandate:permissions[ ]
|
||||
style sepaMandate:permissions fill:#dd4901,stroke:white
|
||||
|
||||
perm:sepaMandate:DELETE{{sepaMandate:DELETE}}
|
||||
perm:sepaMandate:UPDATE{{sepaMandate:UPDATE}}
|
||||
perm:sepaMandate:SELECT{{sepaMandate:SELECT}}
|
||||
perm:sepaMandate:INSERT{{sepaMandate:INSERT}}
|
||||
end
|
||||
end
|
||||
|
||||
subgraph debitorRel["`**debitorRel**`"]
|
||||
direction TB
|
||||
style debitorRel fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph debitorRel.contact["`**debitorRel.contact**`"]
|
||||
direction TB
|
||||
style debitorRel.contact fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph debitorRel.contact:roles[ ]
|
||||
style debitorRel.contact:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:debitorRel.contact:owner[[debitorRel.contact:owner]]
|
||||
role:debitorRel.contact:admin[[debitorRel.contact:admin]]
|
||||
role:debitorRel.contact:referrer[[debitorRel.contact:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph debitorRel.anchorPerson["`**debitorRel.anchorPerson**`"]
|
||||
direction TB
|
||||
style debitorRel.anchorPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph debitorRel.anchorPerson:roles[ ]
|
||||
style debitorRel.anchorPerson:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:debitorRel.anchorPerson:owner[[debitorRel.anchorPerson:owner]]
|
||||
role:debitorRel.anchorPerson:admin[[debitorRel.anchorPerson:admin]]
|
||||
role:debitorRel.anchorPerson:referrer[[debitorRel.anchorPerson:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph debitorRel.holderPerson["`**debitorRel.holderPerson**`"]
|
||||
direction TB
|
||||
style debitorRel.holderPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph debitorRel.holderPerson:roles[ ]
|
||||
style debitorRel.holderPerson:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:debitorRel.holderPerson:owner[[debitorRel.holderPerson:owner]]
|
||||
role:debitorRel.holderPerson:admin[[debitorRel.holderPerson:admin]]
|
||||
role:debitorRel.holderPerson:referrer[[debitorRel.holderPerson:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph debitorRel:roles[ ]
|
||||
style debitorRel:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:debitorRel:owner[[debitorRel:owner]]
|
||||
role:debitorRel:admin[[debitorRel:admin]]
|
||||
role:debitorRel:agent[[debitorRel:agent]]
|
||||
role:debitorRel:tenant[[debitorRel:tenant]]
|
||||
end
|
||||
end
|
||||
|
||||
%% granting roles to users
|
||||
user:creator ==> role:sepaMandate:owner
|
||||
|
||||
%% granting roles to roles
|
||||
role:global:admin -.-> role:debitorRel.anchorPerson:owner
|
||||
role:debitorRel.anchorPerson:owner -.-> role:debitorRel.anchorPerson:admin
|
||||
role:debitorRel.anchorPerson:admin -.-> role:debitorRel.anchorPerson:referrer
|
||||
role:global:admin -.-> role:debitorRel.holderPerson:owner
|
||||
role:debitorRel.holderPerson:owner -.-> role:debitorRel.holderPerson:admin
|
||||
role:debitorRel.holderPerson:admin -.-> role:debitorRel.holderPerson:referrer
|
||||
role:global:admin -.-> role:debitorRel.contact:owner
|
||||
role:debitorRel.contact:owner -.-> role:debitorRel.contact:admin
|
||||
role:debitorRel.contact:admin -.-> role:debitorRel.contact:referrer
|
||||
role:global:admin -.-> role:debitorRel:owner
|
||||
role:debitorRel:owner -.-> role:debitorRel:admin
|
||||
role:debitorRel.anchorPerson:admin -.-> role:debitorRel:admin
|
||||
role:debitorRel:admin -.-> role:debitorRel:agent
|
||||
role:debitorRel.holderPerson:admin -.-> role:debitorRel:agent
|
||||
role:debitorRel:agent -.-> role:debitorRel:tenant
|
||||
role:debitorRel.holderPerson:admin -.-> role:debitorRel:tenant
|
||||
role:debitorRel.contact:admin -.-> role:debitorRel:tenant
|
||||
role:debitorRel:tenant -.-> role:debitorRel.anchorPerson:referrer
|
||||
role:debitorRel:tenant -.-> role:debitorRel.holderPerson:referrer
|
||||
role:debitorRel:tenant -.-> role:debitorRel.contact:referrer
|
||||
role:global:admin -.-> role:bankAccount:owner
|
||||
role:bankAccount:owner -.-> role:bankAccount:admin
|
||||
role:bankAccount:admin -.-> role:bankAccount:referrer
|
||||
role:global:admin ==> role:sepaMandate:owner
|
||||
role:sepaMandate:owner ==> role:sepaMandate:admin
|
||||
role:sepaMandate:admin ==> role:sepaMandate:agent
|
||||
role:sepaMandate:agent ==> role:bankAccount:referrer
|
||||
role:sepaMandate:agent ==> role:debitorRel:agent
|
||||
role:sepaMandate:agent ==> role:sepaMandate:referrer
|
||||
role:bankAccount:admin ==> role:sepaMandate:referrer
|
||||
role:debitorRel:agent ==> role:sepaMandate:referrer
|
||||
role:sepaMandate:referrer ==> role:debitorRel:tenant
|
||||
|
||||
%% granting permissions to roles
|
||||
role:sepaMandate:owner ==> perm:sepaMandate:DELETE
|
||||
role:sepaMandate:admin ==> perm:sepaMandate:UPDATE
|
||||
role:sepaMandate:referrer ==> perm:sepaMandate:SELECT
|
||||
role:debitorRel:admin ==> perm:sepaMandate:INSERT
|
||||
|
||||
```
|
||||
|
@ -1,4 +1,6 @@
|
||||
--liquibase formatted sql
|
||||
-- This code generated was by RbacViewPostgresGenerator, do not amend manually.
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-sepamandate-rbac-OBJECT:1 endDelimiter:--//
|
||||
@ -15,144 +17,191 @@ call generateRbacRoleDescriptors('hsOfficeSepaMandate', 'hs_office_sepamandate')
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-sepamandate-rbac-ROLES-CREATION:1 endDelimiter:--//
|
||||
--changeset hs-office-sepamandate-rbac-insert-trigger:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
Creates and updates the roles and their assignments for sepaMandate entities.
|
||||
Creates the roles, grants and permission for the AFTER INSERT TRIGGER.
|
||||
*/
|
||||
|
||||
create or replace function hsOfficeSepaMandateRbacRolesTrigger()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
strict as $$
|
||||
create or replace procedure buildRbacSystemForHsOfficeSepaMandate(
|
||||
NEW hs_office_sepamandate
|
||||
)
|
||||
language plpgsql as $$
|
||||
|
||||
declare
|
||||
newHsOfficeDebitor hs_office_debitor;
|
||||
newHsOfficeBankAccount hs_office_bankAccount;
|
||||
newBankAccount hs_office_bankaccount;
|
||||
newDebitorRel hs_office_relation;
|
||||
|
||||
begin
|
||||
call enterTriggerForObjectUuid(NEW.uuid);
|
||||
|
||||
select * from hs_office_debitor as p where p.uuid = NEW.debitorUuid into newHsOfficeDebitor;
|
||||
select * from hs_office_bankAccount as c where c.uuid = NEW.bankAccountUuid into newHsOfficeBankAccount;
|
||||
SELECT * FROM hs_office_bankaccount WHERE uuid = NEW.bankAccountUuid INTO newBankAccount;
|
||||
assert newBankAccount.uuid is not null, format('newBankAccount must not be null for NEW.bankAccountUuid = %s', NEW.bankAccountUuid);
|
||||
|
||||
if TG_OP = 'INSERT' then
|
||||
SELECT debitorRel.*
|
||||
FROM hs_office_relation debitorRel
|
||||
JOIN hs_office_debitor debitor ON debitor.debitorRelUuid = debitorRel.uuid
|
||||
WHERE debitor.uuid = NEW.debitorUuid
|
||||
INTO newDebitorRel;
|
||||
assert newDebitorRel.uuid is not null, format('newDebitorRel must not be null for NEW.debitorUuid = %s', NEW.debitorUuid);
|
||||
|
||||
-- === ATTENTION: code generated from related Mermaid flowchart: ===
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeSepaMandateOwner(NEW),
|
||||
permissions => array['DELETE'],
|
||||
incomingSuperRoles => array[globalAdmin()]
|
||||
);
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeSepaMandateOwner(NEW),
|
||||
permissions => array['DELETE'],
|
||||
incomingSuperRoles => array[globalAdmin()],
|
||||
userUuids => array[currentUserUuid()]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeSepaMandateAdmin(NEW),
|
||||
permissions => array['UPDATE'],
|
||||
incomingSuperRoles => array[hsOfficeSepaMandateOwner(NEW)],
|
||||
outgoingSubRoles => array[hsOfficeBankAccountTenant(newHsOfficeBankAccount)]
|
||||
);
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeSepaMandateAdmin(NEW),
|
||||
permissions => array['UPDATE'],
|
||||
incomingSuperRoles => array[hsOfficeSepaMandateOwner(NEW)]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeSepaMandateAgent(NEW),
|
||||
incomingSuperRoles => array[hsOfficeSepaMandateAdmin(NEW), hsOfficeDebitorAdmin(newHsOfficeDebitor), hsOfficeBankAccountAdmin(newHsOfficeBankAccount)],
|
||||
outgoingSubRoles => array[hsOfficeDebitorTenant(newHsOfficeDebitor)]
|
||||
);
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeSepaMandateAgent(NEW),
|
||||
incomingSuperRoles => array[hsOfficeSepaMandateAdmin(NEW)],
|
||||
outgoingSubRoles => array[
|
||||
hsOfficeBankAccountReferrer(newBankAccount),
|
||||
hsOfficeRelationAgent(newDebitorRel)]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeSepaMandateTenant(NEW),
|
||||
incomingSuperRoles => array[hsOfficeSepaMandateAgent(NEW)],
|
||||
outgoingSubRoles => array[hsOfficeDebitorGuest(newHsOfficeDebitor), hsOfficeBankAccountGuest(newHsOfficeBankAccount)]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeSepaMandateGuest(NEW),
|
||||
permissions => array['SELECT'],
|
||||
incomingSuperRoles => array[hsOfficeSepaMandateTenant(NEW)]
|
||||
);
|
||||
|
||||
-- === END of code generated from Mermaid flowchart. ===
|
||||
|
||||
else
|
||||
raise exception 'invalid usage of TRIGGER';
|
||||
end if;
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeSepaMandateReferrer(NEW),
|
||||
permissions => array['SELECT'],
|
||||
incomingSuperRoles => array[
|
||||
hsOfficeBankAccountAdmin(newBankAccount),
|
||||
hsOfficeRelationAgent(newDebitorRel),
|
||||
hsOfficeSepaMandateAgent(NEW)],
|
||||
outgoingSubRoles => array[hsOfficeRelationTenant(newDebitorRel)]
|
||||
);
|
||||
|
||||
call leaveTriggerForObjectUuid(NEW.uuid);
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
/*
|
||||
An AFTER INSERT TRIGGER which creates the role structure for a new customer.
|
||||
AFTER INSERT TRIGGER to create the role+grant structure for a new hs_office_sepamandate row.
|
||||
*/
|
||||
create trigger createRbacRolesForHsOfficeSepaMandate_Trigger
|
||||
after insert
|
||||
on hs_office_sepamandate
|
||||
|
||||
create or replace function insertTriggerForHsOfficeSepaMandate_tf()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
strict as $$
|
||||
begin
|
||||
call buildRbacSystemForHsOfficeSepaMandate(NEW);
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
create trigger insertTriggerForHsOfficeSepaMandate_tg
|
||||
after insert on hs_office_sepamandate
|
||||
for each row
|
||||
execute procedure hsOfficeSepaMandateRbacRolesTrigger();
|
||||
execute procedure insertTriggerForHsOfficeSepaMandate_tf();
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-sepamandate-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
||||
--changeset hs-office-sepamandate-rbac-INSERT:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacIdentityViewFromProjection('hs_office_sepamandate', 'target.reference');
|
||||
|
||||
/*
|
||||
Creates INSERT INTO hs_office_sepamandate permissions for the related hs_office_relation rows.
|
||||
*/
|
||||
do language plpgsql $$
|
||||
declare
|
||||
row hs_office_relation;
|
||||
begin
|
||||
call defineContext('create INSERT INTO hs_office_sepamandate permissions for the related hs_office_relation rows');
|
||||
|
||||
FOR row IN SELECT * FROM hs_office_relation
|
||||
LOOP
|
||||
call grantPermissionToRole(
|
||||
createPermission(row.uuid, 'INSERT', 'hs_office_sepamandate'),
|
||||
hsOfficeRelationAdmin(row));
|
||||
END LOOP;
|
||||
END;
|
||||
$$;
|
||||
|
||||
/**
|
||||
Adds hs_office_sepamandate INSERT permission to specified role of new hs_office_relation rows.
|
||||
*/
|
||||
create or replace function hs_office_sepamandate_hs_office_relation_insert_tf()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
strict as $$
|
||||
begin
|
||||
call grantPermissionToRole(
|
||||
createPermission(NEW.uuid, 'INSERT', 'hs_office_sepamandate'),
|
||||
hsOfficeRelationAdmin(NEW));
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
-- z_... is to put it at the end of after insert triggers, to make sure the roles exist
|
||||
create trigger z_hs_office_sepamandate_hs_office_relation_insert_tg
|
||||
after insert on hs_office_relation
|
||||
for each row
|
||||
execute procedure hs_office_sepamandate_hs_office_relation_insert_tf();
|
||||
|
||||
/**
|
||||
Checks if the user or assumed roles are allowed to insert a row to hs_office_sepamandate,
|
||||
where the check is performed by an indirect role.
|
||||
|
||||
An indirect role is a role which depends on an object uuid which is not a direct foreign key
|
||||
of the source entity, but needs to be fetched via joined tables.
|
||||
*/
|
||||
create or replace function hs_office_sepamandate_insert_permission_check_tf()
|
||||
returns trigger
|
||||
language plpgsql as $$
|
||||
|
||||
declare
|
||||
superRoleObjectUuid uuid;
|
||||
|
||||
begin
|
||||
superRoleObjectUuid := (SELECT debitorRel.uuid
|
||||
FROM hs_office_relation debitorRel
|
||||
JOIN hs_office_debitor debitor ON debitor.debitorRelUuid = debitorRel.uuid
|
||||
WHERE debitor.uuid = NEW.debitorUuid
|
||||
);
|
||||
assert superRoleObjectUuid is not null, 'superRoleObjectUuid must not be null';
|
||||
|
||||
if ( not hasInsertPermission(superRoleObjectUuid, 'INSERT', 'hs_office_sepamandate') ) then
|
||||
raise exception
|
||||
'[403] insert into hs_office_sepamandate not allowed for current subjects % (%)',
|
||||
currentSubjects(), currentSubjectsUuids();
|
||||
end if;
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
create trigger hs_office_sepamandate_insert_permission_check_tg
|
||||
before insert on hs_office_sepamandate
|
||||
for each row
|
||||
execute procedure hs_office_sepamandate_insert_permission_check_tf();
|
||||
--//
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-sepamandate-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
call generateRbacIdentityViewFromQuery('hs_office_sepamandate',
|
||||
$idName$
|
||||
select sm.uuid as uuid, ba.iban || '-' || sm.validity as idName
|
||||
from hs_office_sepamandate sm
|
||||
join hs_office_bankaccount ba on ba.uuid = sm.bankAccountUuid
|
||||
$idName$);
|
||||
--//
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-sepamandate-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacRestrictedView('hs_office_sepamandate',
|
||||
orderby => 'target.reference',
|
||||
columnUpdates => $updates$
|
||||
$orderBy$
|
||||
validity
|
||||
$orderBy$,
|
||||
$updates$
|
||||
reference = new.reference,
|
||||
agreement = new.agreement,
|
||||
validity = new.validity
|
||||
$updates$);
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-sepamandate-rbac-NEW-SepaMandate:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
/*
|
||||
Creates a global permission for new-sepaMandate and assigns it to the hostsharing admins role.
|
||||
*/
|
||||
do language plpgsql $$
|
||||
declare
|
||||
addCustomerPermissions uuid[];
|
||||
globalObjectUuid uuid;
|
||||
globalAdminRoleUuid uuid ;
|
||||
begin
|
||||
call defineContext('granting global new-sepaMandate permission to global admin role', null, null, null);
|
||||
|
||||
globalAdminRoleUuid := findRoleId(globalAdmin());
|
||||
globalObjectUuid := (select uuid from global);
|
||||
addCustomerPermissions := createPermissions(globalObjectUuid, array ['new-sepamandate']);
|
||||
call grantPermissionsToRole(globalAdminRoleUuid, addCustomerPermissions);
|
||||
end;
|
||||
$$;
|
||||
|
||||
/**
|
||||
Used by the trigger to prevent the add-customer to current user respectively assumed roles.
|
||||
*/
|
||||
create or replace function addHsOfficeSepaMandateNotAllowedForCurrentSubjects()
|
||||
returns trigger
|
||||
language PLPGSQL
|
||||
as $$
|
||||
begin
|
||||
raise exception '[403] new-sepaMandate not permitted for %',
|
||||
array_to_string(currentSubjects(), ';', 'null');
|
||||
end; $$;
|
||||
|
||||
/**
|
||||
Checks if the user or assumed roles are allowed to create a new customer.
|
||||
*/
|
||||
create trigger hs_office_sepamandate_insert_trigger
|
||||
before insert
|
||||
on hs_office_sepamandate
|
||||
for each row
|
||||
-- TODO.spec: who is allowed to create new sepaMandates
|
||||
when ( not hasAssumedRole() )
|
||||
execute procedure addHsOfficeSepaMandateNotAllowedForCurrentSubjects();
|
||||
--//
|
||||
|
||||
|
@ -8,31 +8,36 @@
|
||||
/*
|
||||
Creates a single sepaMandate test record.
|
||||
*/
|
||||
create or replace procedure createHsOfficeSepaMandateTestData( tradeNameAndHolderName varchar )
|
||||
create or replace procedure createHsOfficeSepaMandateTestData(
|
||||
forPartnerNumber numeric(5),
|
||||
forDebitorSuffix numeric(2),
|
||||
forIban varchar,
|
||||
withReference varchar)
|
||||
language plpgsql as $$
|
||||
declare
|
||||
currentTask varchar;
|
||||
idName varchar;
|
||||
relatedDebitor hs_office_debitor;
|
||||
relatedBankAccount hs_office_bankAccount;
|
||||
begin
|
||||
idName := cleanIdentifier( tradeNameAndHolderName);
|
||||
currentTask := 'creating SEPA-mandate test-data ' || idName;
|
||||
currentTask := 'creating SEPA-mandate test-data ' || forPartnerNumber::text || forDebitorSuffix::text;
|
||||
call defineContext(currentTask, null, 'superuser-alex@hostsharing.net', 'global#global.admin');
|
||||
execute format('set local hsadminng.currentTask to %L', currentTask);
|
||||
|
||||
select debitor.* from hs_office_debitor debitor
|
||||
join hs_office_partner parter on parter.uuid = debitor.partnerUuid
|
||||
join hs_office_person person on person.uuid = parter.personUuid
|
||||
where person.tradeName = tradeNameAndHolderName into relatedDebitor;
|
||||
select c.* from hs_office_bankAccount c where c.holder = tradeNameAndHolderName into relatedBankAccount;
|
||||
select debitor.* into relatedDebitor
|
||||
from hs_office_debitor debitor
|
||||
join hs_office_relation debitorRel on debitorRel.uuid = debitor.debitorRelUuid
|
||||
join hs_office_relation partnerRel on partnerRel.holderUuid = debitorRel.anchorUuid
|
||||
join hs_office_partner partner on partner.partnerRelUuid = partnerRel.uuid
|
||||
where partner.partnerNumber = forPartnerNumber and debitor.debitorNumberSuffix = forDebitorSuffix;
|
||||
select b.* into relatedBankAccount
|
||||
from hs_office_bankAccount b where b.iban = forIban;
|
||||
|
||||
raise notice 'creating test SEPA-mandate: %', idName;
|
||||
raise notice 'creating test SEPA-mandate: %', forPartnerNumber::text || forDebitorSuffix::text;
|
||||
raise notice '- using debitor (%): %', relatedDebitor.uuid, relatedDebitor;
|
||||
raise notice '- using bankAccount (%): %', relatedBankAccount.uuid, relatedBankAccount;
|
||||
insert
|
||||
into hs_office_sepamandate (uuid, debitoruuid, bankAccountuuid, reference, agreement, validity)
|
||||
values (uuid_generate_v4(), relatedDebitor.uuid, relatedBankAccount.uuid, 'ref'||idName, '20220930', daterange('20221001' , '20261231', '[]'));
|
||||
values (uuid_generate_v4(), relatedDebitor.uuid, relatedBankAccount.uuid, withReference, '20220930', daterange('20221001' , '20261231', '[]'));
|
||||
end; $$;
|
||||
--//
|
||||
|
||||
@ -43,9 +48,9 @@ end; $$;
|
||||
|
||||
do language plpgsql $$
|
||||
begin
|
||||
call createHsOfficeSepaMandateTestData('First GmbH');
|
||||
call createHsOfficeSepaMandateTestData('Second e.K.');
|
||||
call createHsOfficeSepaMandateTestData('Third OHG');
|
||||
call createHsOfficeSepaMandateTestData(10001, 11, 'DE02120300000000202051', 'ref-10001-11');
|
||||
call createHsOfficeSepaMandateTestData(10002, 12, 'DE02100500000054540402', 'ref-10002-12');
|
||||
call createHsOfficeSepaMandateTestData(10003, 13, 'DE02300209000106531065', 'ref-10003-13');
|
||||
end;
|
||||
$$;
|
||||
--//
|
||||
|
@ -7,10 +7,9 @@
|
||||
create table hs_office_debitor
|
||||
(
|
||||
uuid uuid unique references RbacObject (uuid) initially deferred,
|
||||
partnerUuid uuid not null references hs_office_partner(uuid),
|
||||
billable boolean not null default true,
|
||||
debitorNumberSuffix numeric(2) not null,
|
||||
billingContactUuid uuid not null references hs_office_contact(uuid),
|
||||
debitorRelUuid uuid not null references hs_office_relation(uuid),
|
||||
billable boolean not null default true,
|
||||
vatId varchar(24), -- TODO.spec: here or in person?
|
||||
vatCountryCode varchar(2),
|
||||
vatBusiness boolean not null,
|
||||
@ -20,11 +19,43 @@ create table hs_office_debitor
|
||||
constraint check_default_prefix check (
|
||||
defaultPrefix::text ~ '^([a-z]{3}|al0|bh1|c4s|f3k|k8i|l3d|mh1|o13|p2m|s80|t4w)$'
|
||||
)
|
||||
-- TODO.impl: SEPA-mandate
|
||||
);
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-debitor-DELETE-DEPENDENTS-TRIGGER:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
Trigger function to delete related rows of a debitor to delete.
|
||||
*/
|
||||
create or replace function deleteHsOfficeDependentsOnDebitorDelete()
|
||||
returns trigger
|
||||
language PLPGSQL
|
||||
as $$
|
||||
declare
|
||||
counter integer;
|
||||
begin
|
||||
DELETE FROM hs_office_relation r WHERE r.uuid = OLD.debitorRelUuid;
|
||||
GET DIAGNOSTICS counter = ROW_COUNT;
|
||||
if counter = 0 then
|
||||
raise exception 'debitor relation % could not be deleted', OLD.debitorRelUuid;
|
||||
end if;
|
||||
|
||||
RETURN OLD;
|
||||
end; $$;
|
||||
|
||||
/**
|
||||
Triggers deletion of related details of a debitor to delete.
|
||||
*/
|
||||
create trigger hs_office_debitor_delete_dependents_trigger
|
||||
after delete
|
||||
on hs_office_debitor
|
||||
for each row
|
||||
execute procedure deleteHsOfficeDependentsOnDebitorDelete();
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-debitor-MAIN-TABLE-JOURNAL:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
@ -1,275 +0,0 @@
|
||||
### rbac debitor
|
||||
|
||||
This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually.
|
||||
|
||||
```mermaid
|
||||
%%{init:{'flowchart':{'htmlLabels':false}}}%%
|
||||
flowchart TB
|
||||
|
||||
subgraph debitorRel.anchorPerson["`**debitorRel.anchorPerson**`"]
|
||||
direction TB
|
||||
style debitorRel.anchorPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph debitorRel.anchorPerson:roles[ ]
|
||||
style debitorRel.anchorPerson:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:debitorRel.anchorPerson:owner[[debitorRel.anchorPerson:owner]]
|
||||
role:debitorRel.anchorPerson:admin[[debitorRel.anchorPerson:admin]]
|
||||
role:debitorRel.anchorPerson:referrer[[debitorRel.anchorPerson:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph debitorRel.holderPerson["`**debitorRel.holderPerson**`"]
|
||||
direction TB
|
||||
style debitorRel.holderPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph debitorRel.holderPerson:roles[ ]
|
||||
style debitorRel.holderPerson:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:debitorRel.holderPerson:owner[[debitorRel.holderPerson:owner]]
|
||||
role:debitorRel.holderPerson:admin[[debitorRel.holderPerson:admin]]
|
||||
role:debitorRel.holderPerson:referrer[[debitorRel.holderPerson:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph partnerRel.holderPerson["`**partnerRel.holderPerson**`"]
|
||||
direction TB
|
||||
style partnerRel.holderPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph partnerRel.holderPerson:roles[ ]
|
||||
style partnerRel.holderPerson:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:partnerRel.holderPerson:owner[[partnerRel.holderPerson:owner]]
|
||||
role:partnerRel.holderPerson:admin[[partnerRel.holderPerson:admin]]
|
||||
role:partnerRel.holderPerson:referrer[[partnerRel.holderPerson:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph debitor["`**debitor**`"]
|
||||
direction TB
|
||||
style debitor fill:#dd4901,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph debitor:permissions[ ]
|
||||
style debitor:permissions fill:#dd4901,stroke:white
|
||||
|
||||
perm:debitor:INSERT{{debitor:INSERT}}
|
||||
perm:debitor:DELETE{{debitor:DELETE}}
|
||||
perm:debitor:UPDATE{{debitor:UPDATE}}
|
||||
perm:debitor:SELECT{{debitor:SELECT}}
|
||||
end
|
||||
|
||||
subgraph debitorRel["`**debitorRel**`"]
|
||||
direction TB
|
||||
style debitorRel fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
subgraph debitorRel.anchorPerson["`**debitorRel.anchorPerson**`"]
|
||||
direction TB
|
||||
style debitorRel.anchorPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph debitorRel.anchorPerson:roles[ ]
|
||||
style debitorRel.anchorPerson:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:debitorRel.anchorPerson:owner[[debitorRel.anchorPerson:owner]]
|
||||
role:debitorRel.anchorPerson:admin[[debitorRel.anchorPerson:admin]]
|
||||
role:debitorRel.anchorPerson:referrer[[debitorRel.anchorPerson:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph debitorRel.holderPerson["`**debitorRel.holderPerson**`"]
|
||||
direction TB
|
||||
style debitorRel.holderPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph debitorRel.holderPerson:roles[ ]
|
||||
style debitorRel.holderPerson:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:debitorRel.holderPerson:owner[[debitorRel.holderPerson:owner]]
|
||||
role:debitorRel.holderPerson:admin[[debitorRel.holderPerson:admin]]
|
||||
role:debitorRel.holderPerson:referrer[[debitorRel.holderPerson:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph debitorRel.contact["`**debitorRel.contact**`"]
|
||||
direction TB
|
||||
style debitorRel.contact fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph debitorRel.contact:roles[ ]
|
||||
style debitorRel.contact:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:debitorRel.contact:owner[[debitorRel.contact:owner]]
|
||||
role:debitorRel.contact:admin[[debitorRel.contact:admin]]
|
||||
role:debitorRel.contact:referrer[[debitorRel.contact:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph debitorRel:roles[ ]
|
||||
style debitorRel:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:debitorRel:owner[[debitorRel:owner]]
|
||||
role:debitorRel:admin[[debitorRel:admin]]
|
||||
role:debitorRel:agent[[debitorRel:agent]]
|
||||
role:debitorRel:tenant[[debitorRel:tenant]]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
subgraph partnerRel["`**partnerRel**`"]
|
||||
direction TB
|
||||
style partnerRel fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph partnerRel.holderPerson["`**partnerRel.holderPerson**`"]
|
||||
direction TB
|
||||
style partnerRel.holderPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph partnerRel.holderPerson:roles[ ]
|
||||
style partnerRel.holderPerson:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:partnerRel.holderPerson:owner[[partnerRel.holderPerson:owner]]
|
||||
role:partnerRel.holderPerson:admin[[partnerRel.holderPerson:admin]]
|
||||
role:partnerRel.holderPerson:referrer[[partnerRel.holderPerson:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph partnerRel.contact["`**partnerRel.contact**`"]
|
||||
direction TB
|
||||
style partnerRel.contact fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph partnerRel.contact:roles[ ]
|
||||
style partnerRel.contact:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:partnerRel.contact:owner[[partnerRel.contact:owner]]
|
||||
role:partnerRel.contact:admin[[partnerRel.contact:admin]]
|
||||
role:partnerRel.contact:referrer[[partnerRel.contact:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph partnerRel.anchorPerson["`**partnerRel.anchorPerson**`"]
|
||||
direction TB
|
||||
style partnerRel.anchorPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph partnerRel.anchorPerson:roles[ ]
|
||||
style partnerRel.anchorPerson:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:partnerRel.anchorPerson:owner[[partnerRel.anchorPerson:owner]]
|
||||
role:partnerRel.anchorPerson:admin[[partnerRel.anchorPerson:admin]]
|
||||
role:partnerRel.anchorPerson:referrer[[partnerRel.anchorPerson:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph partnerRel:roles[ ]
|
||||
style partnerRel:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:partnerRel:owner[[partnerRel:owner]]
|
||||
role:partnerRel:admin[[partnerRel:admin]]
|
||||
role:partnerRel:agent[[partnerRel:agent]]
|
||||
role:partnerRel:tenant[[partnerRel:tenant]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph partnerRel.contact["`**partnerRel.contact**`"]
|
||||
direction TB
|
||||
style partnerRel.contact fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph partnerRel.contact:roles[ ]
|
||||
style partnerRel.contact:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:partnerRel.contact:owner[[partnerRel.contact:owner]]
|
||||
role:partnerRel.contact:admin[[partnerRel.contact:admin]]
|
||||
role:partnerRel.contact:referrer[[partnerRel.contact:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph debitorRel.contact["`**debitorRel.contact**`"]
|
||||
direction TB
|
||||
style debitorRel.contact fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph debitorRel.contact:roles[ ]
|
||||
style debitorRel.contact:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:debitorRel.contact:owner[[debitorRel.contact:owner]]
|
||||
role:debitorRel.contact:admin[[debitorRel.contact:admin]]
|
||||
role:debitorRel.contact:referrer[[debitorRel.contact:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph partnerRel.anchorPerson["`**partnerRel.anchorPerson**`"]
|
||||
direction TB
|
||||
style partnerRel.anchorPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph partnerRel.anchorPerson:roles[ ]
|
||||
style partnerRel.anchorPerson:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:partnerRel.anchorPerson:owner[[partnerRel.anchorPerson:owner]]
|
||||
role:partnerRel.anchorPerson:admin[[partnerRel.anchorPerson:admin]]
|
||||
role:partnerRel.anchorPerson:referrer[[partnerRel.anchorPerson:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph refundBankAccount["`**refundBankAccount**`"]
|
||||
direction TB
|
||||
style refundBankAccount fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph refundBankAccount:roles[ ]
|
||||
style refundBankAccount:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:refundBankAccount:owner[[refundBankAccount:owner]]
|
||||
role:refundBankAccount:admin[[refundBankAccount:admin]]
|
||||
role:refundBankAccount:referrer[[refundBankAccount:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
%% granting roles to roles
|
||||
role:global:admin -.-> role:debitorRel.anchorPerson:owner
|
||||
role:debitorRel.anchorPerson:owner -.-> role:debitorRel.anchorPerson:admin
|
||||
role:debitorRel.anchorPerson:admin -.-> role:debitorRel.anchorPerson:referrer
|
||||
role:global:admin -.-> role:debitorRel.holderPerson:owner
|
||||
role:debitorRel.holderPerson:owner -.-> role:debitorRel.holderPerson:admin
|
||||
role:debitorRel.holderPerson:admin -.-> role:debitorRel.holderPerson:referrer
|
||||
role:global:admin -.-> role:debitorRel.contact:owner
|
||||
role:debitorRel.contact:owner -.-> role:debitorRel.contact:admin
|
||||
role:debitorRel.contact:admin -.-> role:debitorRel.contact:referrer
|
||||
role:global:admin -.-> role:debitorRel:owner
|
||||
role:debitorRel:owner -.-> role:debitorRel:admin
|
||||
role:debitorRel.anchorPerson:admin -.-> role:debitorRel:admin
|
||||
role:debitorRel:admin -.-> role:debitorRel:agent
|
||||
role:debitorRel.holderPerson:admin -.-> role:debitorRel:agent
|
||||
role:debitorRel:agent -.-> role:debitorRel:tenant
|
||||
role:debitorRel.holderPerson:admin -.-> role:debitorRel:tenant
|
||||
role:debitorRel.contact:admin -.-> role:debitorRel:tenant
|
||||
role:debitorRel:tenant -.-> role:debitorRel.anchorPerson:referrer
|
||||
role:debitorRel:tenant -.-> role:debitorRel.holderPerson:referrer
|
||||
role:debitorRel:tenant -.-> role:debitorRel.contact:referrer
|
||||
role:global:admin -.-> role:refundBankAccount:owner
|
||||
role:refundBankAccount:owner -.-> role:refundBankAccount:admin
|
||||
role:refundBankAccount:admin -.-> role:refundBankAccount:referrer
|
||||
role:refundBankAccount:admin ==> role:debitorRel:agent
|
||||
role:debitorRel:agent ==> role:refundBankAccount:referrer
|
||||
role:global:admin -.-> role:partnerRel.anchorPerson:owner
|
||||
role:partnerRel.anchorPerson:owner -.-> role:partnerRel.anchorPerson:admin
|
||||
role:partnerRel.anchorPerson:admin -.-> role:partnerRel.anchorPerson:referrer
|
||||
role:global:admin -.-> role:partnerRel.holderPerson:owner
|
||||
role:partnerRel.holderPerson:owner -.-> role:partnerRel.holderPerson:admin
|
||||
role:partnerRel.holderPerson:admin -.-> role:partnerRel.holderPerson:referrer
|
||||
role:global:admin -.-> role:partnerRel.contact:owner
|
||||
role:partnerRel.contact:owner -.-> role:partnerRel.contact:admin
|
||||
role:partnerRel.contact:admin -.-> role:partnerRel.contact:referrer
|
||||
role:global:admin -.-> role:partnerRel:owner
|
||||
role:partnerRel:owner -.-> role:partnerRel:admin
|
||||
role:partnerRel.anchorPerson:admin -.-> role:partnerRel:admin
|
||||
role:partnerRel:admin -.-> role:partnerRel:agent
|
||||
role:partnerRel.holderPerson:admin -.-> role:partnerRel:agent
|
||||
role:partnerRel:agent -.-> role:partnerRel:tenant
|
||||
role:partnerRel.holderPerson:admin -.-> role:partnerRel:tenant
|
||||
role:partnerRel.contact:admin -.-> role:partnerRel:tenant
|
||||
role:partnerRel:tenant -.-> role:partnerRel.anchorPerson:referrer
|
||||
role:partnerRel:tenant -.-> role:partnerRel.holderPerson:referrer
|
||||
role:partnerRel:tenant -.-> role:partnerRel.contact:referrer
|
||||
role:partnerRel:admin ==> role:debitorRel:admin
|
||||
role:partnerRel:agent ==> role:debitorRel:agent
|
||||
role:debitorRel:agent ==> role:partnerRel:tenant
|
||||
|
||||
%% granting permissions to roles
|
||||
role:global:admin ==> perm:debitor:INSERT
|
||||
role:debitorRel:owner ==> perm:debitor:DELETE
|
||||
role:debitorRel:admin ==> perm:debitor:UPDATE
|
||||
role:debitorRel:tenant ==> perm:debitor:SELECT
|
||||
|
||||
```
|
@ -1,231 +0,0 @@
|
||||
--liquibase formatted sql
|
||||
-- This code generated was by RbacViewPostgresGenerator, do not amend manually.
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-debitor-rbac-OBJECT:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRelatedRbacObject('hs_office_debitor');
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-debitor-rbac-ROLE-DESCRIPTORS:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacRoleDescriptors('hsOfficeDebitor', 'hs_office_debitor');
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-debitor-rbac-insert-trigger:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
Creates the roles, grants and permission for the AFTER INSERT TRIGGER.
|
||||
*/
|
||||
|
||||
create or replace procedure buildRbacSystemForHsOfficeDebitor(
|
||||
NEW hs_office_debitor
|
||||
)
|
||||
language plpgsql as $$
|
||||
|
||||
declare
|
||||
newPartnerRel hs_office_relation;
|
||||
newDebitorRel hs_office_relation;
|
||||
newRefundBankAccount hs_office_bankaccount;
|
||||
|
||||
begin
|
||||
call enterTriggerForObjectUuid(NEW.uuid);
|
||||
|
||||
SELECT * FROM hs_office_relation WHERE uuid = NEW.partnerRelUuid INTO newPartnerRel;
|
||||
|
||||
SELECT * FROM hs_office_relation WHERE uuid = NEW.debitorRelUuid INTO newDebitorRel;
|
||||
assert newDebitorRel.uuid is not null, format('newDebitorRel must not be null for NEW.debitorRelUuid = %s', NEW.debitorRelUuid);
|
||||
|
||||
SELECT * FROM hs_office_bankaccount WHERE uuid = NEW.refundBankAccountUuid INTO newRefundBankAccount;
|
||||
|
||||
call grantRoleToRole(hsOfficeBankAccountReferrer(newRefundBankAccount), hsOfficeRelationAgent(newDebitorRel));
|
||||
call grantRoleToRole(hsOfficeRelationAdmin(newDebitorRel), hsOfficeRelationAdmin(newPartnerRel));
|
||||
call grantRoleToRole(hsOfficeRelationAgent(newDebitorRel), hsOfficeBankAccountAdmin(newRefundBankAccount));
|
||||
call grantRoleToRole(hsOfficeRelationAgent(newDebitorRel), hsOfficeRelationAgent(newPartnerRel));
|
||||
call grantRoleToRole(hsOfficeRelationTenant(newPartnerRel), hsOfficeRelationAgent(newDebitorRel));
|
||||
|
||||
call grantPermissionToRole(createPermission(NEW.uuid, 'DELETE'), hsOfficeRelationOwner(newDebitorRel));
|
||||
call grantPermissionToRole(createPermission(NEW.uuid, 'SELECT'), hsOfficeRelationTenant(newDebitorRel));
|
||||
call grantPermissionToRole(createPermission(NEW.uuid, 'UPDATE'), hsOfficeRelationAdmin(newDebitorRel));
|
||||
|
||||
call leaveTriggerForObjectUuid(NEW.uuid);
|
||||
end; $$;
|
||||
|
||||
/*
|
||||
AFTER INSERT TRIGGER to create the role+grant structure for a new hs_office_debitor row.
|
||||
*/
|
||||
|
||||
create or replace function insertTriggerForHsOfficeDebitor_tf()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
strict as $$
|
||||
begin
|
||||
call buildRbacSystemForHsOfficeDebitor(NEW);
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
create trigger insertTriggerForHsOfficeDebitor_tg
|
||||
after insert on hs_office_debitor
|
||||
for each row
|
||||
execute procedure insertTriggerForHsOfficeDebitor_tf();
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-debitor-rbac-update-trigger:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
Called from the AFTER UPDATE TRIGGER to re-wire the grants.
|
||||
*/
|
||||
|
||||
create or replace procedure updateRbacRulesForHsOfficeDebitor(
|
||||
OLD hs_office_debitor,
|
||||
NEW hs_office_debitor
|
||||
)
|
||||
language plpgsql as $$
|
||||
begin
|
||||
|
||||
if NEW.refundBankAccountUuid is distinct from OLD.refundBankAccountUuid then
|
||||
delete from rbacgrants g where g.grantedbytriggerof = OLD.uuid;
|
||||
call buildRbacSystemForHsOfficeDebitor(NEW);
|
||||
end if;
|
||||
end; $$;
|
||||
|
||||
/*
|
||||
AFTER INSERT TRIGGER to re-wire the grant structure for a new hs_office_debitor row.
|
||||
*/
|
||||
|
||||
create or replace function updateTriggerForHsOfficeDebitor_tf()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
strict as $$
|
||||
begin
|
||||
call updateRbacRulesForHsOfficeDebitor(OLD, NEW);
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
create trigger updateTriggerForHsOfficeDebitor_tg
|
||||
after update on hs_office_debitor
|
||||
for each row
|
||||
execute procedure updateTriggerForHsOfficeDebitor_tf();
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-debitor-rbac-INSERT:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
Creates INSERT INTO hs_office_debitor permissions for the related global rows.
|
||||
*/
|
||||
do language plpgsql $$
|
||||
declare
|
||||
row global;
|
||||
permissionUuid uuid;
|
||||
roleUuid uuid;
|
||||
begin
|
||||
call defineContext('create INSERT INTO hs_office_debitor permissions for the related global rows');
|
||||
|
||||
FOR row IN SELECT * FROM global
|
||||
LOOP
|
||||
roleUuid := findRoleId(globalAdmin());
|
||||
permissionUuid := createPermission(row.uuid, 'INSERT', 'hs_office_debitor');
|
||||
call grantPermissionToRole(permissionUuid, roleUuid);
|
||||
END LOOP;
|
||||
END;
|
||||
$$;
|
||||
|
||||
/**
|
||||
Adds hs_office_debitor INSERT permission to specified role of new global rows.
|
||||
*/
|
||||
create or replace function hs_office_debitor_global_insert_tf()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
strict as $$
|
||||
begin
|
||||
call grantPermissionToRole(
|
||||
createPermission(NEW.uuid, 'INSERT', 'hs_office_debitor'),
|
||||
globalAdmin());
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
-- z_... is to put it at the end of after insert triggers, to make sure the roles exist
|
||||
create trigger z_hs_office_debitor_global_insert_tg
|
||||
after insert on global
|
||||
for each row
|
||||
execute procedure hs_office_debitor_global_insert_tf();
|
||||
|
||||
/**
|
||||
Checks if the user or assumed roles are allowed to insert a row to hs_office_debitor,
|
||||
where only global-admin has that permission.
|
||||
*/
|
||||
create or replace function hs_office_debitor_insert_permission_missing_tf()
|
||||
returns trigger
|
||||
language plpgsql as $$
|
||||
begin
|
||||
raise exception '[403] insert into hs_office_debitor not allowed for current subjects % (%)',
|
||||
currentSubjects(), currentSubjectsUuids();
|
||||
end; $$;
|
||||
|
||||
create trigger hs_office_debitor_insert_permission_check_tg
|
||||
before insert on hs_office_debitor
|
||||
for each row
|
||||
when ( not isGlobalAdmin() )
|
||||
execute procedure hs_office_debitor_insert_permission_missing_tf();
|
||||
--//
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-debitor-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
call generateRbacIdentityViewFromQuery('hs_office_debitor',
|
||||
$idName$
|
||||
SELECT debitor.uuid,
|
||||
'D-' || (SELECT partner.partnerNumber
|
||||
FROM hs_office_partner partner
|
||||
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 partnerRel.type = 'DEBITOR'
|
||||
WHERE debitorRel.uuid = debitor.debitorRelUuid)
|
||||
|| to_char(debitorNumberSuffix, 'fm00')
|
||||
from hs_office_debitor as debitor
|
||||
$idName$);
|
||||
--//
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-debitor-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacRestrictedView('hs_office_debitor',
|
||||
$orderBy$
|
||||
SELECT debitor.uuid,
|
||||
'D-' || (SELECT partner.partnerNumber
|
||||
FROM hs_office_partner partner
|
||||
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 partnerRel.type = 'DEBITOR'
|
||||
WHERE debitorRel.uuid = debitor.debitorRelUuid)
|
||||
|| to_char(debitorNumberSuffix, 'fm00')
|
||||
from hs_office_debitor as debitor
|
||||
$orderBy$,
|
||||
$updates$
|
||||
debitorRel = new.debitorRel,
|
||||
billable = new.billable,
|
||||
debitorUuid = new.debitorUuid,
|
||||
refundBankAccountUuid = new.refundBankAccountUuid,
|
||||
vatId = new.vatId,
|
||||
vatCountryCode = new.vatCountryCode,
|
||||
vatBusiness = new.vatBusiness,
|
||||
vatReverseCharge = new.vatReverseCharge,
|
||||
defaultPrefix = new.defaultPrefix
|
||||
$updates$);
|
||||
--//
|
||||
|
@ -1,250 +1,275 @@
|
||||
### hs_office_debitor RBAC Roles
|
||||
### rbac debitor
|
||||
|
||||
This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually.
|
||||
|
||||
```mermaid
|
||||
%%{init:{'flowchart':{'htmlLabels':false}}}%%
|
||||
flowchart TB
|
||||
|
||||
subgraph global
|
||||
style global fill:#eee
|
||||
subgraph debitorRel.anchorPerson["`**debitorRel.anchorPerson**`"]
|
||||
direction TB
|
||||
style debitorRel.anchorPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
role:global.admin[global.admin]
|
||||
end
|
||||
subgraph debitorRel.anchorPerson:roles[ ]
|
||||
style debitorRel.anchorPerson:roles fill:#99bcdb,stroke:white
|
||||
|
||||
subgraph office
|
||||
style office fill:#eee
|
||||
|
||||
subgraph sepa
|
||||
|
||||
subgraph bankaccount
|
||||
style bankaccount fill: #e9f7ef
|
||||
|
||||
user:hsOfficeBankAccount.creator([bankaccount.creator])
|
||||
|
||||
role:hsOfficeBankAccount.owner[bankaccount.owner]
|
||||
%% permissions
|
||||
role:hsOfficeBankAccount.owner --> perm:hsOfficeBankAccount.*{{bankaccount.*}}
|
||||
%% incoming
|
||||
role:global.admin --> role:hsOfficeBankAccount.owner
|
||||
user:hsOfficeBankAccount.creator ---> role:hsOfficeBankAccount.owner
|
||||
|
||||
role:hsOfficeBankAccount.admin[bankaccount.admin]
|
||||
%% permissions
|
||||
role:hsOfficeBankAccount.admin --> perm:hsOfficeBankAccount.edit{{bankaccount.edit}}
|
||||
%% incoming
|
||||
role:hsOfficeBankAccount.owner ---> role:hsOfficeBankAccount.admin
|
||||
|
||||
role:hsOfficeBankAccount.tenant[bankaccount.tenant]
|
||||
%% incoming
|
||||
role:hsOfficeBankAccount.admin ---> role:hsOfficeBankAccount.tenant
|
||||
|
||||
role:hsOfficeBankAccount.guest[bankaccount.guest]
|
||||
%% permissions
|
||||
role:hsOfficeBankAccount.guest --> perm:hsOfficeBankAccount.view{{bankaccount.view}}
|
||||
%% incoming
|
||||
role:hsOfficeBankAccount.tenant ---> role:hsOfficeBankAccount.guest
|
||||
end
|
||||
|
||||
subgraph hsOfficeSepaMandate
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
subgraph contact
|
||||
style contact fill: #e9f7ef
|
||||
|
||||
user:hsOfficeContact.creator([contact.creator])
|
||||
|
||||
role:hsOfficeContact.owner[contact.owner]
|
||||
%% permissions
|
||||
role:hsOfficeContact.owner --> perm:hsOfficeContact.*{{contact.*}}
|
||||
%% incoming
|
||||
role:global.admin --> role:hsOfficeContact.owner
|
||||
user:hsOfficeContact.creator ---> role:hsOfficeContact.owner
|
||||
|
||||
role:hsOfficeContact.admin[contact.admin]
|
||||
%% permissions
|
||||
role:hsOfficeContact.admin ---> perm:hsOfficeContact.edit{{contact.edit}}
|
||||
%% incoming
|
||||
role:hsOfficeContact.owner ---> role:hsOfficeContact.admin
|
||||
|
||||
role:hsOfficeContact.tenant[contact.tenant]
|
||||
%% incoming
|
||||
role:hsOfficeContact.admin ----> role:hsOfficeContact.tenant
|
||||
|
||||
role:hsOfficeContact.guest[contact.guest]
|
||||
%% permissions
|
||||
role:hsOfficeContact.guest --> perm:hsOfficeContact.view{{contact.view}}
|
||||
%% incoming
|
||||
role:hsOfficeContact.tenant ---> role:hsOfficeContact.guest
|
||||
end
|
||||
|
||||
subgraph partner-person
|
||||
|
||||
subgraph person
|
||||
style person fill: #e9f7ef
|
||||
|
||||
user:hsOfficePerson.creator([personcreator])
|
||||
|
||||
role:hsOfficePerson.owner[person.owner]
|
||||
%% permissions
|
||||
role:hsOfficePerson.owner --> perm:hsOfficePerson.*{{person.*}}
|
||||
%% incoming
|
||||
user:hsOfficePerson.creator ---> role:hsOfficePerson.owner
|
||||
role:global.admin --> role:hsOfficePerson.owner
|
||||
|
||||
role:hsOfficePerson.admin[person.admin]
|
||||
%% permissions
|
||||
role:hsOfficePerson.admin --> perm:hsOfficePerson.edit{{person.edit}}
|
||||
%% incoming
|
||||
role:hsOfficePerson.owner ---> role:hsOfficePerson.admin
|
||||
|
||||
role:hsOfficePerson.tenant[person.tenant]
|
||||
%% incoming
|
||||
role:hsOfficePerson.admin -----> role:hsOfficePerson.tenant
|
||||
|
||||
role:hsOfficePerson.guest[person.guest]
|
||||
%% permissions
|
||||
role:hsOfficePerson.guest --> perm:hsOfficePerson.edit{{person.view}}
|
||||
%% incoming
|
||||
role:hsOfficePerson.tenant ---> role:hsOfficePerson.guest
|
||||
end
|
||||
|
||||
subgraph partner
|
||||
|
||||
role:hsOfficePartner.owner[partner.owner]
|
||||
%% permissions
|
||||
role:hsOfficePartner.owner --> perm:hsOfficePartner.*{{partner.*}}
|
||||
%% incoming
|
||||
role:global.admin ---> role:hsOfficePartner.owner
|
||||
|
||||
role:hsOfficePartner.admin[partner.admin]
|
||||
%% permissions
|
||||
role:hsOfficePartner.admin --> perm:hsOfficePartner.edit{{partner.edit}}
|
||||
%% incoming
|
||||
role:hsOfficePartner.owner ---> role:hsOfficePartner.admin
|
||||
%% outgoing
|
||||
role:hsOfficePartner.admin --> role:hsOfficePerson.tenant
|
||||
role:hsOfficePartner.admin --> role:hsOfficeContact.tenant
|
||||
|
||||
role:hsOfficePartner.agent[partner.agent]
|
||||
%% incoming
|
||||
role:hsOfficePartner.admin --> role:hsOfficePartner.agent
|
||||
role:hsOfficePerson.admin --> role:hsOfficePartner.agent
|
||||
role:hsOfficeContact.admin --> role:hsOfficePartner.agent
|
||||
|
||||
role:hsOfficePartner.tenant[partner.tenant]
|
||||
%% incoming
|
||||
role:hsOfficePartner.agent ---> role:hsOfficePartner.tenant
|
||||
%% outgoing
|
||||
role:hsOfficePartner.tenant --> role:hsOfficePerson.guest
|
||||
role:hsOfficePartner.tenant --> role:hsOfficeContact.guest
|
||||
|
||||
role:hsOfficePartner.guest[partner.guest]
|
||||
%% permissions
|
||||
role:hsOfficePartner.guest --> perm:hsOfficePartner.view{{partner.view}}
|
||||
%% incoming
|
||||
role:hsOfficePartner.tenant ---> role:hsOfficePartner.guest
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
subgraph debitor
|
||||
style debitor stroke-width:6px
|
||||
|
||||
user:hsOfficeDebitor.creator([debitor.creator])
|
||||
%% created by role
|
||||
user:hsOfficeDebitor.creator --> role:hsOfficePartner.agent
|
||||
|
||||
role:hsOfficeDebitor.owner[debitor.owner]
|
||||
%% permissions
|
||||
role:hsOfficeDebitor.owner --> perm:hsOfficeDebitor.*{{debitor.*}}
|
||||
%% incoming
|
||||
user:hsOfficeDebitor.creator --> role:hsOfficeDebitor.owner
|
||||
role:global.admin --> role:hsOfficeDebitor.owner
|
||||
|
||||
role:hsOfficeDebitor.admin[debitor.admin]
|
||||
%% permissions
|
||||
role:hsOfficeDebitor.admin --> perm:hsOfficeDebitor.edit{{debitor.edit}}
|
||||
%% incoming
|
||||
role:hsOfficeDebitor.owner ---> role:hsOfficeDebitor.admin
|
||||
|
||||
role:hsOfficeDebitor.agent[debitor.agent]
|
||||
%% incoming
|
||||
role:hsOfficeDebitor.admin ---> role:hsOfficeDebitor.agent
|
||||
role:hsOfficePartner.admin --> role:hsOfficeDebitor.agent
|
||||
%% outgoing
|
||||
role:hsOfficeDebitor.agent --> role:hsOfficeBankAccount.tenant
|
||||
|
||||
role:hsOfficeDebitor.tenant[debitor.tenant]
|
||||
%% incoming
|
||||
role:hsOfficeDebitor.agent ---> role:hsOfficeDebitor.tenant
|
||||
role:hsOfficePartner.agent --> role:hsOfficeDebitor.tenant
|
||||
role:hsOfficeBankAccount.admin --> role:hsOfficeDebitor.tenant
|
||||
%% outgoing
|
||||
role:hsOfficeDebitor.tenant --> role:hsOfficePartner.tenant
|
||||
role:hsOfficeDebitor.tenant --> role:hsOfficeContact.guest
|
||||
|
||||
role:hsOfficeDebitor.guest[debitor.guest]
|
||||
%% permissions
|
||||
role:hsOfficeDebitor.guest --> perm:hsOfficeDebitor.view{{debitor.view}}
|
||||
%% incoming
|
||||
role:hsOfficeDebitor.tenant --> role:hsOfficeDebitor.guest
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
subgraph hsOfficeSepaMandate
|
||||
|
||||
role:hsOfficeSepaMandate.owner[sepaMandate.owner]
|
||||
%% permissions
|
||||
role:hsOfficeSepaMandate.owner --> perm:hsOfficeSepaMandate.*{{sepaMandate.*}}
|
||||
%% incoming
|
||||
role:global.admin ---> role:hsOfficeSepaMandate.owner
|
||||
|
||||
role:hsOfficeSepaMandate.admin[sepaMandate.admin]
|
||||
%% permissions
|
||||
role:hsOfficeSepaMandate.admin --> perm:hsOfficeSepaMandate.edit{{sepaMandate.edit}}
|
||||
%% incoming
|
||||
role:hsOfficeSepaMandate.owner ---> role:hsOfficeSepaMandate.admin
|
||||
|
||||
role:hsOfficeSepaMandate.agent[sepaMandate.agent]
|
||||
%% incoming
|
||||
role:hsOfficeSepaMandate.admin ---> role:hsOfficeSepaMandate.agent
|
||||
role:hsOfficeDebitor.admin --> role:hsOfficeSepaMandate.agent
|
||||
role:hsOfficeBankAccount.admin --> role:hsOfficeSepaMandate.agent
|
||||
%% outgoing
|
||||
role:hsOfficeSepaMandate.agent --> role:hsOfficeDebitor.tenant
|
||||
role:hsOfficeSepaMandate.admin --> role:hsOfficeBankAccount.tenant
|
||||
|
||||
role:hsOfficeSepaMandate.tenant[sepaMandate.tenant]
|
||||
%% incoming
|
||||
role:hsOfficeSepaMandate.agent --> role:hsOfficeSepaMandate.tenant
|
||||
%% outgoing
|
||||
role:hsOfficeSepaMandate.tenant --> role:hsOfficeDebitor.guest
|
||||
role:hsOfficeSepaMandate.tenant --> role:hsOfficeBankAccount.guest
|
||||
|
||||
role:hsOfficeSepaMandate.guest[sepaMandate.guest]
|
||||
%% permissions
|
||||
role:hsOfficeSepaMandate.guest --> perm:hsOfficeSepaMandate.view{{sepaMandate.view}}
|
||||
%% incoming
|
||||
role:hsOfficeSepaMandate.tenant --> role:hsOfficeSepaMandate.guest
|
||||
end
|
||||
|
||||
subgraph hosting
|
||||
style hosting fill:#eee
|
||||
|
||||
subgraph package
|
||||
style package fill: #e9f7ef
|
||||
|
||||
role:package.owner[package.owner]
|
||||
--> role:package.admin[package.admin]
|
||||
--> role:package.tenant[package.tenant]
|
||||
|
||||
role:hsOfficeDebitor.agent --> role:package.owner
|
||||
role:package.admin --> role:hsOfficeDebitor.tenant
|
||||
role:hsOfficePartner.tenant --> role:hsOfficeDebitor.guest
|
||||
role:debitorRel.anchorPerson:owner[[debitorRel.anchorPerson:owner]]
|
||||
role:debitorRel.anchorPerson:admin[[debitorRel.anchorPerson:admin]]
|
||||
role:debitorRel.anchorPerson:referrer[[debitorRel.anchorPerson:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph debitorRel.holderPerson["`**debitorRel.holderPerson**`"]
|
||||
direction TB
|
||||
style debitorRel.holderPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph debitorRel.holderPerson:roles[ ]
|
||||
style debitorRel.holderPerson:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:debitorRel.holderPerson:owner[[debitorRel.holderPerson:owner]]
|
||||
role:debitorRel.holderPerson:admin[[debitorRel.holderPerson:admin]]
|
||||
role:debitorRel.holderPerson:referrer[[debitorRel.holderPerson:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph partnerRel.holderPerson["`**partnerRel.holderPerson**`"]
|
||||
direction TB
|
||||
style partnerRel.holderPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph partnerRel.holderPerson:roles[ ]
|
||||
style partnerRel.holderPerson:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:partnerRel.holderPerson:owner[[partnerRel.holderPerson:owner]]
|
||||
role:partnerRel.holderPerson:admin[[partnerRel.holderPerson:admin]]
|
||||
role:partnerRel.holderPerson:referrer[[partnerRel.holderPerson:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph debitor["`**debitor**`"]
|
||||
direction TB
|
||||
style debitor fill:#dd4901,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph debitor:permissions[ ]
|
||||
style debitor:permissions fill:#dd4901,stroke:white
|
||||
|
||||
perm:debitor:INSERT{{debitor:INSERT}}
|
||||
perm:debitor:DELETE{{debitor:DELETE}}
|
||||
perm:debitor:UPDATE{{debitor:UPDATE}}
|
||||
perm:debitor:SELECT{{debitor:SELECT}}
|
||||
end
|
||||
|
||||
subgraph debitorRel["`**debitorRel**`"]
|
||||
direction TB
|
||||
style debitorRel fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
subgraph debitorRel.anchorPerson["`**debitorRel.anchorPerson**`"]
|
||||
direction TB
|
||||
style debitorRel.anchorPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph debitorRel.anchorPerson:roles[ ]
|
||||
style debitorRel.anchorPerson:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:debitorRel.anchorPerson:owner[[debitorRel.anchorPerson:owner]]
|
||||
role:debitorRel.anchorPerson:admin[[debitorRel.anchorPerson:admin]]
|
||||
role:debitorRel.anchorPerson:referrer[[debitorRel.anchorPerson:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph debitorRel.holderPerson["`**debitorRel.holderPerson**`"]
|
||||
direction TB
|
||||
style debitorRel.holderPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph debitorRel.holderPerson:roles[ ]
|
||||
style debitorRel.holderPerson:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:debitorRel.holderPerson:owner[[debitorRel.holderPerson:owner]]
|
||||
role:debitorRel.holderPerson:admin[[debitorRel.holderPerson:admin]]
|
||||
role:debitorRel.holderPerson:referrer[[debitorRel.holderPerson:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph debitorRel.contact["`**debitorRel.contact**`"]
|
||||
direction TB
|
||||
style debitorRel.contact fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph debitorRel.contact:roles[ ]
|
||||
style debitorRel.contact:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:debitorRel.contact:owner[[debitorRel.contact:owner]]
|
||||
role:debitorRel.contact:admin[[debitorRel.contact:admin]]
|
||||
role:debitorRel.contact:referrer[[debitorRel.contact:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph debitorRel:roles[ ]
|
||||
style debitorRel:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:debitorRel:owner[[debitorRel:owner]]
|
||||
role:debitorRel:admin[[debitorRel:admin]]
|
||||
role:debitorRel:agent[[debitorRel:agent]]
|
||||
role:debitorRel:tenant[[debitorRel:tenant]]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
subgraph partnerRel["`**partnerRel**`"]
|
||||
direction TB
|
||||
style partnerRel fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph partnerRel.holderPerson["`**partnerRel.holderPerson**`"]
|
||||
direction TB
|
||||
style partnerRel.holderPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph partnerRel.holderPerson:roles[ ]
|
||||
style partnerRel.holderPerson:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:partnerRel.holderPerson:owner[[partnerRel.holderPerson:owner]]
|
||||
role:partnerRel.holderPerson:admin[[partnerRel.holderPerson:admin]]
|
||||
role:partnerRel.holderPerson:referrer[[partnerRel.holderPerson:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph partnerRel.contact["`**partnerRel.contact**`"]
|
||||
direction TB
|
||||
style partnerRel.contact fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph partnerRel.contact:roles[ ]
|
||||
style partnerRel.contact:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:partnerRel.contact:owner[[partnerRel.contact:owner]]
|
||||
role:partnerRel.contact:admin[[partnerRel.contact:admin]]
|
||||
role:partnerRel.contact:referrer[[partnerRel.contact:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph partnerRel.anchorPerson["`**partnerRel.anchorPerson**`"]
|
||||
direction TB
|
||||
style partnerRel.anchorPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph partnerRel.anchorPerson:roles[ ]
|
||||
style partnerRel.anchorPerson:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:partnerRel.anchorPerson:owner[[partnerRel.anchorPerson:owner]]
|
||||
role:partnerRel.anchorPerson:admin[[partnerRel.anchorPerson:admin]]
|
||||
role:partnerRel.anchorPerson:referrer[[partnerRel.anchorPerson:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph partnerRel:roles[ ]
|
||||
style partnerRel:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:partnerRel:owner[[partnerRel:owner]]
|
||||
role:partnerRel:admin[[partnerRel:admin]]
|
||||
role:partnerRel:agent[[partnerRel:agent]]
|
||||
role:partnerRel:tenant[[partnerRel:tenant]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph partnerRel.contact["`**partnerRel.contact**`"]
|
||||
direction TB
|
||||
style partnerRel.contact fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph partnerRel.contact:roles[ ]
|
||||
style partnerRel.contact:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:partnerRel.contact:owner[[partnerRel.contact:owner]]
|
||||
role:partnerRel.contact:admin[[partnerRel.contact:admin]]
|
||||
role:partnerRel.contact:referrer[[partnerRel.contact:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph debitorRel.contact["`**debitorRel.contact**`"]
|
||||
direction TB
|
||||
style debitorRel.contact fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph debitorRel.contact:roles[ ]
|
||||
style debitorRel.contact:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:debitorRel.contact:owner[[debitorRel.contact:owner]]
|
||||
role:debitorRel.contact:admin[[debitorRel.contact:admin]]
|
||||
role:debitorRel.contact:referrer[[debitorRel.contact:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph partnerRel.anchorPerson["`**partnerRel.anchorPerson**`"]
|
||||
direction TB
|
||||
style partnerRel.anchorPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph partnerRel.anchorPerson:roles[ ]
|
||||
style partnerRel.anchorPerson:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:partnerRel.anchorPerson:owner[[partnerRel.anchorPerson:owner]]
|
||||
role:partnerRel.anchorPerson:admin[[partnerRel.anchorPerson:admin]]
|
||||
role:partnerRel.anchorPerson:referrer[[partnerRel.anchorPerson:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph refundBankAccount["`**refundBankAccount**`"]
|
||||
direction TB
|
||||
style refundBankAccount fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph refundBankAccount:roles[ ]
|
||||
style refundBankAccount:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:refundBankAccount:owner[[refundBankAccount:owner]]
|
||||
role:refundBankAccount:admin[[refundBankAccount:admin]]
|
||||
role:refundBankAccount:referrer[[refundBankAccount:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
%% granting roles to roles
|
||||
role:global:admin -.-> role:debitorRel.anchorPerson:owner
|
||||
role:debitorRel.anchorPerson:owner -.-> role:debitorRel.anchorPerson:admin
|
||||
role:debitorRel.anchorPerson:admin -.-> role:debitorRel.anchorPerson:referrer
|
||||
role:global:admin -.-> role:debitorRel.holderPerson:owner
|
||||
role:debitorRel.holderPerson:owner -.-> role:debitorRel.holderPerson:admin
|
||||
role:debitorRel.holderPerson:admin -.-> role:debitorRel.holderPerson:referrer
|
||||
role:global:admin -.-> role:debitorRel.contact:owner
|
||||
role:debitorRel.contact:owner -.-> role:debitorRel.contact:admin
|
||||
role:debitorRel.contact:admin -.-> role:debitorRel.contact:referrer
|
||||
role:global:admin -.-> role:debitorRel:owner
|
||||
role:debitorRel:owner -.-> role:debitorRel:admin
|
||||
role:debitorRel.anchorPerson:admin -.-> role:debitorRel:admin
|
||||
role:debitorRel:admin -.-> role:debitorRel:agent
|
||||
role:debitorRel.holderPerson:admin -.-> role:debitorRel:agent
|
||||
role:debitorRel:agent -.-> role:debitorRel:tenant
|
||||
role:debitorRel.holderPerson:admin -.-> role:debitorRel:tenant
|
||||
role:debitorRel.contact:admin -.-> role:debitorRel:tenant
|
||||
role:debitorRel:tenant -.-> role:debitorRel.anchorPerson:referrer
|
||||
role:debitorRel:tenant -.-> role:debitorRel.holderPerson:referrer
|
||||
role:debitorRel:tenant -.-> role:debitorRel.contact:referrer
|
||||
role:global:admin -.-> role:refundBankAccount:owner
|
||||
role:refundBankAccount:owner -.-> role:refundBankAccount:admin
|
||||
role:refundBankAccount:admin -.-> role:refundBankAccount:referrer
|
||||
role:refundBankAccount:admin ==> role:debitorRel:agent
|
||||
role:debitorRel:agent ==> role:refundBankAccount:referrer
|
||||
role:global:admin -.-> role:partnerRel.anchorPerson:owner
|
||||
role:partnerRel.anchorPerson:owner -.-> role:partnerRel.anchorPerson:admin
|
||||
role:partnerRel.anchorPerson:admin -.-> role:partnerRel.anchorPerson:referrer
|
||||
role:global:admin -.-> role:partnerRel.holderPerson:owner
|
||||
role:partnerRel.holderPerson:owner -.-> role:partnerRel.holderPerson:admin
|
||||
role:partnerRel.holderPerson:admin -.-> role:partnerRel.holderPerson:referrer
|
||||
role:global:admin -.-> role:partnerRel.contact:owner
|
||||
role:partnerRel.contact:owner -.-> role:partnerRel.contact:admin
|
||||
role:partnerRel.contact:admin -.-> role:partnerRel.contact:referrer
|
||||
role:global:admin -.-> role:partnerRel:owner
|
||||
role:partnerRel:owner -.-> role:partnerRel:admin
|
||||
role:partnerRel.anchorPerson:admin -.-> role:partnerRel:admin
|
||||
role:partnerRel:admin -.-> role:partnerRel:agent
|
||||
role:partnerRel.holderPerson:admin -.-> role:partnerRel:agent
|
||||
role:partnerRel:agent -.-> role:partnerRel:tenant
|
||||
role:partnerRel.holderPerson:admin -.-> role:partnerRel:tenant
|
||||
role:partnerRel.contact:admin -.-> role:partnerRel:tenant
|
||||
role:partnerRel:tenant -.-> role:partnerRel.anchorPerson:referrer
|
||||
role:partnerRel:tenant -.-> role:partnerRel.holderPerson:referrer
|
||||
role:partnerRel:tenant -.-> role:partnerRel.contact:referrer
|
||||
role:partnerRel:admin ==> role:debitorRel:admin
|
||||
role:partnerRel:agent ==> role:debitorRel:agent
|
||||
role:debitorRel:agent ==> role:partnerRel:tenant
|
||||
|
||||
%% granting permissions to roles
|
||||
role:global:admin ==> perm:debitor:INSERT
|
||||
role:debitorRel:owner ==> perm:debitor:DELETE
|
||||
role:debitorRel:admin ==> perm:debitor:UPDATE
|
||||
role:debitorRel:tenant ==> perm:debitor:SELECT
|
||||
|
||||
```
|
||||
|
||||
|
@ -1,4 +1,6 @@
|
||||
--liquibase formatted sql
|
||||
-- This code generated was by RbacViewPostgresGenerator, do not amend manually.
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-debitor-rbac-OBJECT:1 endDelimiter:--//
|
||||
@ -15,233 +17,211 @@ call generateRbacRoleDescriptors('hsOfficeDebitor', 'hs_office_debitor');
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-debitor-rbac-ROLES-CREATION:1 endDelimiter:--//
|
||||
--changeset hs-office-debitor-rbac-insert-trigger:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
Creates and updates the roles and their assignments for debitor entities.
|
||||
Creates the roles, grants and permission for the AFTER INSERT TRIGGER.
|
||||
*/
|
||||
|
||||
create or replace function hsOfficeDebitorRbacRolesTrigger()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
strict as $$
|
||||
create or replace procedure buildRbacSystemForHsOfficeDebitor(
|
||||
NEW hs_office_debitor
|
||||
)
|
||||
language plpgsql as $$
|
||||
|
||||
declare
|
||||
hsOfficeDebitorTenant RbacRoleDescriptor;
|
||||
oldPartner hs_office_partner;
|
||||
newPartner hs_office_partner;
|
||||
newPerson hs_office_person;
|
||||
oldContact hs_office_contact;
|
||||
newContact hs_office_contact;
|
||||
newBankAccount hs_office_bankaccount;
|
||||
oldBankAccount hs_office_bankaccount;
|
||||
newPartnerRel hs_office_relation;
|
||||
newDebitorRel hs_office_relation;
|
||||
newRefundBankAccount hs_office_bankaccount;
|
||||
|
||||
begin
|
||||
call enterTriggerForObjectUuid(NEW.uuid);
|
||||
|
||||
hsOfficeDebitorTenant := hsOfficeDebitorTenant(NEW);
|
||||
SELECT partnerRel.*
|
||||
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 NEW.debitorRelUuid = debitorRel.uuid
|
||||
INTO newPartnerRel;
|
||||
assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.debitorRelUuid = %s', NEW.debitorRelUuid);
|
||||
|
||||
select * from hs_office_partner as p where p.uuid = NEW.partnerUuid into newPartner;
|
||||
select * from hs_office_person as p where p.uuid = newPartner.personUuid into newPerson;
|
||||
select * from hs_office_contact as c where c.uuid = NEW.billingContactUuid into newContact;
|
||||
select * from hs_office_bankaccount as b where b.uuid = NEW.refundBankAccountUuid into newBankAccount;
|
||||
if TG_OP = 'INSERT' then
|
||||
SELECT * FROM hs_office_relation WHERE uuid = NEW.debitorRelUuid INTO newDebitorRel;
|
||||
assert newDebitorRel.uuid is not null, format('newDebitorRel must not be null for NEW.debitorRelUuid = %s', NEW.debitorRelUuid);
|
||||
|
||||
SELECT * FROM hs_office_bankaccount WHERE uuid = NEW.refundBankAccountUuid INTO newRefundBankAccount;
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeDebitorOwner(NEW),
|
||||
permissions => array['DELETE'],
|
||||
incomingSuperRoles => array[globalAdmin()],
|
||||
userUuids => array[currentUserUuid()],
|
||||
grantedByRole => globalAdmin()
|
||||
);
|
||||
call grantRoleToRole(hsOfficeBankAccountReferrer(newRefundBankAccount), hsOfficeRelationAgent(newDebitorRel));
|
||||
call grantRoleToRole(hsOfficeRelationAdmin(newDebitorRel), hsOfficeRelationAdmin(newPartnerRel));
|
||||
call grantRoleToRole(hsOfficeRelationAgent(newDebitorRel), hsOfficeBankAccountAdmin(newRefundBankAccount));
|
||||
call grantRoleToRole(hsOfficeRelationAgent(newDebitorRel), hsOfficeRelationAgent(newPartnerRel));
|
||||
call grantRoleToRole(hsOfficeRelationTenant(newPartnerRel), hsOfficeRelationAgent(newDebitorRel));
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeDebitorAdmin(NEW),
|
||||
permissions => array['UPDATE'],
|
||||
incomingSuperRoles => array[hsOfficeDebitorOwner(NEW)]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeDebitorAgent(NEW),
|
||||
incomingSuperRoles => array[
|
||||
hsOfficeDebitorAdmin(NEW),
|
||||
hsOfficePartnerAdmin(newPartner),
|
||||
hsOfficeContactAdmin(newContact)],
|
||||
outgoingSubRoles => array[
|
||||
hsOfficeBankAccountTenant(newBankaccount)]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeDebitorTenant(NEW),
|
||||
incomingSuperRoles => array[
|
||||
hsOfficeDebitorAgent(NEW),
|
||||
hsOfficePartnerAgent(newPartner),
|
||||
hsOfficeBankAccountAdmin(newBankaccount)],
|
||||
outgoingSubRoles => array[
|
||||
hsOfficePartnerTenant(newPartner),
|
||||
hsOfficeContactGuest(newContact),
|
||||
hsOfficeBankAccountGuest(newBankaccount)]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeDebitorGuest(NEW),
|
||||
permissions => array['SELECT'],
|
||||
incomingSuperRoles => array[
|
||||
hsOfficeDebitorTenant(NEW)]
|
||||
);
|
||||
|
||||
elsif TG_OP = 'UPDATE' then
|
||||
|
||||
if OLD.partnerUuid <> NEW.partnerUuid then
|
||||
select * from hs_office_partner as p where p.uuid = OLD.partnerUuid into oldPartner;
|
||||
|
||||
call revokeRoleFromRole(hsOfficeDebitorAgent(OLD), hsOfficePartnerAdmin(oldPartner));
|
||||
call grantRoleToRole(hsOfficeDebitorAgent(NEW), hsOfficePartnerAdmin(newPartner));
|
||||
|
||||
call revokeRoleFromRole(hsOfficeDebitorTenant(OLD), hsOfficePartnerAgent(oldPartner));
|
||||
call grantRoleToRole(hsOfficeDebitorTenant(NEW), hsOfficePartnerAgent(newPartner));
|
||||
|
||||
call revokeRoleFromRole(hsOfficePartnerTenant(oldPartner), hsOfficeDebitorTenant(OLD));
|
||||
call grantRoleToRole(hsOfficePartnerTenant(newPartner), hsOfficeDebitorTenant(NEW));
|
||||
end if;
|
||||
|
||||
if OLD.billingContactUuid <> NEW.billingContactUuid then
|
||||
select * from hs_office_contact as c where c.uuid = OLD.billingContactUuid into oldContact;
|
||||
|
||||
call revokeRoleFromRole(hsOfficeDebitorAgent(OLD), hsOfficeContactAdmin(oldContact));
|
||||
call grantRoleToRole(hsOfficeDebitorAgent(NEW), hsOfficeContactAdmin(newContact));
|
||||
|
||||
call revokeRoleFromRole(hsOfficeContactGuest(oldContact), hsOfficeDebitorTenant(OLD));
|
||||
call grantRoleToRole(hsOfficeContactGuest(newContact), hsOfficeDebitorTenant(NEW));
|
||||
end if;
|
||||
|
||||
if (OLD.refundBankAccountUuid is not null or NEW.refundBankAccountUuid is not null) and
|
||||
( OLD.refundBankAccountUuid is null or NEW.refundBankAccountUuid is null or
|
||||
OLD.refundBankAccountUuid <> NEW.refundBankAccountUuid ) then
|
||||
|
||||
select * from hs_office_bankaccount as b where b.uuid = OLD.refundBankAccountUuid into oldBankAccount;
|
||||
|
||||
if oldBankAccount is not null then
|
||||
call revokeRoleFromRole(hsOfficeBankAccountTenant(oldBankaccount), hsOfficeDebitorAgent(OLD));
|
||||
end if;
|
||||
if newBankAccount is not null then
|
||||
call grantRoleToRole(hsOfficeBankAccountTenant(newBankaccount), hsOfficeDebitorAgent(NEW));
|
||||
end if;
|
||||
|
||||
if oldBankAccount is not null then
|
||||
call revokeRoleFromRole(hsOfficeDebitorTenant(OLD), hsOfficeBankAccountAdmin(oldBankaccount));
|
||||
end if;
|
||||
if newBankAccount is not null then
|
||||
call grantRoleToRole(hsOfficeDebitorTenant(NEW), hsOfficeBankAccountAdmin(newBankaccount));
|
||||
end if;
|
||||
|
||||
if oldBankAccount is not null then
|
||||
call revokeRoleFromRole(hsOfficeBankAccountGuest(oldBankaccount), hsOfficeDebitorTenant(OLD));
|
||||
end if;
|
||||
if newBankAccount is not null then
|
||||
call grantRoleToRole(hsOfficeBankAccountGuest(newBankaccount), hsOfficeDebitorTenant(NEW));
|
||||
end if;
|
||||
end if;
|
||||
else
|
||||
raise exception 'invalid usage of TRIGGER';
|
||||
end if;
|
||||
call grantPermissionToRole(createPermission(NEW.uuid, 'DELETE'), hsOfficeRelationOwner(newDebitorRel));
|
||||
call grantPermissionToRole(createPermission(NEW.uuid, 'SELECT'), hsOfficeRelationTenant(newDebitorRel));
|
||||
call grantPermissionToRole(createPermission(NEW.uuid, 'UPDATE'), hsOfficeRelationAdmin(newDebitorRel));
|
||||
|
||||
call leaveTriggerForObjectUuid(NEW.uuid);
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
/*
|
||||
An AFTER INSERT TRIGGER which creates the role structure for a new debitor.
|
||||
AFTER INSERT TRIGGER to create the role+grant structure for a new hs_office_debitor row.
|
||||
*/
|
||||
create trigger createRbacRolesForHsOfficeDebitor_Trigger
|
||||
after insert
|
||||
on hs_office_debitor
|
||||
for each row
|
||||
execute procedure hsOfficeDebitorRbacRolesTrigger();
|
||||
|
||||
/*
|
||||
An AFTER UPDATE TRIGGER which updates the role structure of a debitor.
|
||||
*/
|
||||
create trigger updateRbacRolesForHsOfficeDebitor_Trigger
|
||||
after update
|
||||
on hs_office_debitor
|
||||
create or replace function insertTriggerForHsOfficeDebitor_tf()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
strict as $$
|
||||
begin
|
||||
call buildRbacSystemForHsOfficeDebitor(NEW);
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
create trigger insertTriggerForHsOfficeDebitor_tg
|
||||
after insert on hs_office_debitor
|
||||
for each row
|
||||
execute procedure hsOfficeDebitorRbacRolesTrigger();
|
||||
execute procedure insertTriggerForHsOfficeDebitor_tf();
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-debitor-rbac-update-trigger:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
Called from the AFTER UPDATE TRIGGER to re-wire the grants.
|
||||
*/
|
||||
|
||||
create or replace procedure updateRbacRulesForHsOfficeDebitor(
|
||||
OLD hs_office_debitor,
|
||||
NEW hs_office_debitor
|
||||
)
|
||||
language plpgsql as $$
|
||||
begin
|
||||
|
||||
if NEW.debitorRelUuid is distinct from OLD.debitorRelUuid
|
||||
or NEW.refundBankAccountUuid is distinct from OLD.refundBankAccountUuid then
|
||||
delete from rbacgrants g where g.grantedbytriggerof = OLD.uuid;
|
||||
call buildRbacSystemForHsOfficeDebitor(NEW);
|
||||
end if;
|
||||
end; $$;
|
||||
|
||||
/*
|
||||
AFTER INSERT TRIGGER to re-wire the grant structure for a new hs_office_debitor row.
|
||||
*/
|
||||
|
||||
create or replace function updateTriggerForHsOfficeDebitor_tf()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
strict as $$
|
||||
begin
|
||||
call updateRbacRulesForHsOfficeDebitor(OLD, NEW);
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
create trigger updateTriggerForHsOfficeDebitor_tg
|
||||
after update on hs_office_debitor
|
||||
for each row
|
||||
execute procedure updateTriggerForHsOfficeDebitor_tf();
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-debitor-rbac-INSERT:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
Creates INSERT INTO hs_office_debitor permissions for the related global rows.
|
||||
*/
|
||||
do language plpgsql $$
|
||||
declare
|
||||
row global;
|
||||
begin
|
||||
call defineContext('create INSERT INTO hs_office_debitor permissions for the related global rows');
|
||||
|
||||
FOR row IN SELECT * FROM global
|
||||
LOOP
|
||||
call grantPermissionToRole(
|
||||
createPermission(row.uuid, 'INSERT', 'hs_office_debitor'),
|
||||
globalAdmin());
|
||||
END LOOP;
|
||||
END;
|
||||
$$;
|
||||
|
||||
/**
|
||||
Adds hs_office_debitor INSERT permission to specified role of new global rows.
|
||||
*/
|
||||
create or replace function hs_office_debitor_global_insert_tf()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
strict as $$
|
||||
begin
|
||||
call grantPermissionToRole(
|
||||
createPermission(NEW.uuid, 'INSERT', 'hs_office_debitor'),
|
||||
globalAdmin());
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
-- z_... is to put it at the end of after insert triggers, to make sure the roles exist
|
||||
create trigger z_hs_office_debitor_global_insert_tg
|
||||
after insert on global
|
||||
for each row
|
||||
execute procedure hs_office_debitor_global_insert_tf();
|
||||
|
||||
/**
|
||||
Checks if the user or assumed roles are allowed to insert a row to hs_office_debitor,
|
||||
where only global-admin has that permission.
|
||||
*/
|
||||
create or replace function hs_office_debitor_insert_permission_missing_tf()
|
||||
returns trigger
|
||||
language plpgsql as $$
|
||||
begin
|
||||
raise exception '[403] insert into hs_office_debitor not allowed for current subjects % (%)',
|
||||
currentSubjects(), currentSubjectsUuids();
|
||||
end; $$;
|
||||
|
||||
create trigger hs_office_debitor_insert_permission_check_tg
|
||||
before insert on hs_office_debitor
|
||||
for each row
|
||||
when ( not isGlobalAdmin() )
|
||||
execute procedure hs_office_debitor_insert_permission_missing_tf();
|
||||
--//
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-debitor-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacIdentityViewFromProjection('hs_office_debitor', $idName$
|
||||
'#' ||
|
||||
(select partnerNumber from hs_office_partner p where p.uuid = target.partnerUuid) ||
|
||||
to_char(debitorNumberSuffix, 'fm00') ||
|
||||
':' || (select split_part(idName, ':', 2) from hs_office_partner_iv pi where pi.uuid = target.partnerUuid)
|
||||
$idName$);
|
||||
--//
|
||||
|
||||
call generateRbacIdentityViewFromQuery('hs_office_debitor',
|
||||
$idName$
|
||||
SELECT debitor.uuid AS uuid,
|
||||
'D-' || (SELECT partner.partnerNumber
|
||||
FROM hs_office_partner partner
|
||||
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
|
||||
$idName$);
|
||||
--//
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-debitor-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacRestrictedView('hs_office_debitor', 'target.debitorNumberSuffix',
|
||||
call generateRbacRestrictedView('hs_office_debitor',
|
||||
$orderBy$
|
||||
defaultPrefix
|
||||
$orderBy$,
|
||||
$updates$
|
||||
partnerUuid = new.partnerUuid, -- TODO: remove? should never do anything
|
||||
debitorRelUuid = new.debitorRelUuid,
|
||||
billable = new.billable,
|
||||
billingContactUuid = new.billingContactUuid,
|
||||
debitorNumberSuffix = new.debitorNumberSuffix, -- TODO: Should it be allowed to updated this value?
|
||||
refundBankAccountUuid = new.refundBankAccountUuid,
|
||||
vatId = new.vatId,
|
||||
vatCountryCode = new.vatCountryCode,
|
||||
vatBusiness = new.vatBusiness,
|
||||
vatreversecharge = new.vatreversecharge,
|
||||
defaultPrefix = new.defaultPrefix -- TODO: Should it be allowed to updated this value?
|
||||
vatReverseCharge = new.vatReverseCharge,
|
||||
defaultPrefix = new.defaultPrefix
|
||||
$updates$);
|
||||
--//
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-debitor-rbac-NEW-DEBITOR:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
/*
|
||||
Creates a global permission for new-debitor and assigns it to the hostsharing admins role.
|
||||
*/
|
||||
do language plpgsql $$
|
||||
declare
|
||||
addDebitorPermissions uuid[];
|
||||
globalObjectUuid uuid;
|
||||
globalAdminRoleUuid uuid ;
|
||||
begin
|
||||
call defineContext('granting global new-debitor permission to global admin role', null, null, null);
|
||||
|
||||
globalAdminRoleUuid := findRoleId(globalAdmin());
|
||||
globalObjectUuid := (select uuid from global);
|
||||
addDebitorPermissions := createPermissions(globalObjectUuid, array ['new-debitor']);
|
||||
call grantPermissionsToRole(globalAdminRoleUuid, addDebitorPermissions);
|
||||
end;
|
||||
$$;
|
||||
|
||||
/**
|
||||
Used by the trigger to prevent the add-debitor to current user respectively assumed roles.
|
||||
*/
|
||||
create or replace function addHsOfficeDebitorNotAllowedForCurrentSubjects()
|
||||
returns trigger
|
||||
language PLPGSQL
|
||||
as $$
|
||||
begin
|
||||
raise exception '[403] new-debitor not permitted for %',
|
||||
array_to_string(currentSubjects(), ';', 'null');
|
||||
end; $$;
|
||||
|
||||
/**
|
||||
Checks if the user or assumed roles are allowed to create a new debitor.
|
||||
*/
|
||||
create trigger hs_office_debitor_insert_trigger
|
||||
before insert
|
||||
on hs_office_debitor
|
||||
for each row
|
||||
-- TODO.spec: who is allowed to create new debitors
|
||||
when ( not hasAssumedRole() )
|
||||
execute procedure addHsOfficeDebitorNotAllowedForCurrentSubjects();
|
||||
--//
|
||||
|
||||
|
@ -9,36 +9,41 @@
|
||||
Creates a single debitor test record.
|
||||
*/
|
||||
create or replace procedure createHsOfficeDebitorTestData(
|
||||
debitorNumberSuffix numeric(5),
|
||||
partnerTradeName varchar,
|
||||
billingContactLabel varchar,
|
||||
defaultPrefix varchar
|
||||
withDebitorNumberSuffix numeric(5),
|
||||
forPartnerPersonName varchar,
|
||||
forBillingContactLabel varchar,
|
||||
withDefaultPrefix varchar
|
||||
)
|
||||
language plpgsql as $$
|
||||
declare
|
||||
currentTask varchar;
|
||||
idName varchar;
|
||||
relatedPartner hs_office_partner;
|
||||
relatedContact hs_office_contact;
|
||||
relatedDebitorRelUuid uuid;
|
||||
relatedBankAccountUuid uuid;
|
||||
begin
|
||||
idName := cleanIdentifier( partnerTradeName|| '-' || billingContactLabel);
|
||||
idName := cleanIdentifier( forPartnerPersonName|| '-' || forBillingContactLabel);
|
||||
currentTask := 'creating debitor test-data ' || idName;
|
||||
call defineContext(currentTask, null, 'superuser-alex@hostsharing.net', 'global#global.admin');
|
||||
execute format('set local hsadminng.currentTask to %L', currentTask);
|
||||
|
||||
select partner.* from hs_office_partner partner
|
||||
join hs_office_person person on person.uuid = partner.personUuid
|
||||
where person.tradeName = partnerTradeName into relatedPartner;
|
||||
select c.* from hs_office_contact c where c.label = billingContactLabel into relatedContact;
|
||||
select b.uuid from hs_office_bankaccount b where b.holder = partnerTradeName into relatedBankAccountUuid;
|
||||
select debitorRel.uuid
|
||||
into relatedDebitorRelUuid
|
||||
from hs_office_relation debitorRel
|
||||
join hs_office_person person on person.uuid = debitorRel.holderUuid
|
||||
and (person.tradeName = forPartnerPersonName or person.familyName = forPartnerPersonName)
|
||||
where debitorRel.type = 'DEBITOR';
|
||||
|
||||
raise notice 'creating test debitor: % (#%)', idName, debitorNumberSuffix;
|
||||
raise notice '- using partner (%): %', relatedPartner.uuid, relatedPartner;
|
||||
raise notice '- using billingContact (%): %', relatedContact.uuid, relatedContact;
|
||||
select b.uuid
|
||||
into relatedBankAccountUuid
|
||||
from hs_office_bankaccount b
|
||||
where b.holder = forPartnerPersonName;
|
||||
|
||||
raise notice 'creating test debitor: % (#%)', idName, withDebitorNumberSuffix;
|
||||
-- raise exception 'creating test debitor: (uuid=%, debitorRelUuid=%, debitornumbersuffix=%, billable=%, vatbusiness=%, vatreversecharge=%, refundbankaccountuuid=%, defaultprefix=%)',
|
||||
-- uuid_generate_v4(), relatedDebitorRelUuid, withDebitorNumberSuffix, true, true, false, relatedBankAccountUuid, withDefaultPrefix;
|
||||
insert
|
||||
into hs_office_debitor (uuid, partneruuid, debitornumbersuffix, billable, billingcontactuuid, vatbusiness, vatreversecharge, refundbankaccountuuid, defaultprefix)
|
||||
values (uuid_generate_v4(), relatedPartner.uuid, debitorNumberSuffix, true, relatedContact.uuid, true, false, relatedBankAccountUuid, defaultPrefix);
|
||||
into hs_office_debitor (uuid, debitorRelUuid, debitornumbersuffix, billable, vatbusiness, vatreversecharge, refundbankaccountuuid, defaultprefix)
|
||||
values (uuid_generate_v4(), relatedDebitorRelUuid, withDebitorNumberSuffix, true, true, false, relatedBankAccountUuid, withDefaultPrefix);
|
||||
end; $$;
|
||||
--//
|
||||
|
||||
|
@ -12,7 +12,6 @@ create table if not exists hs_office_membership
|
||||
(
|
||||
uuid uuid unique references RbacObject (uuid) initially deferred,
|
||||
partnerUuid uuid not null references hs_office_partner(uuid),
|
||||
mainDebitorUuid uuid not null references hs_office_debitor(uuid),
|
||||
memberNumberSuffix char(2) not null check (
|
||||
memberNumberSuffix::text ~ '^[0-9][0-9]$'),
|
||||
validity daterange not null,
|
||||
|
@ -1,75 +1,159 @@
|
||||
### hs_office_membership RBAC
|
||||
### rbac membership
|
||||
|
||||
This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually.
|
||||
|
||||
```mermaid
|
||||
%%{init:{'flowchart':{'htmlLabels':false}}}%%
|
||||
flowchart TB
|
||||
|
||||
subgraph global
|
||||
style global fill:#eee
|
||||
|
||||
role:global.admin[global.admin]
|
||||
end
|
||||
|
||||
subgraph hsOfficeDebitor
|
||||
subgraph partnerRel["`**partnerRel**`"]
|
||||
direction TB
|
||||
style hsOfficeDebitor fill:#eee
|
||||
style partnerRel fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
role:hsOfficeDebitor.owner[debitor.owner]
|
||||
--> role:hsOfficeDebitor.admin[debitor.admin]
|
||||
--> role:hsOfficeDebitor.tenant[debitor.tenant]
|
||||
--> role:hsOfficeDebitor.guest[debitor.guest]
|
||||
subgraph partnerRel.contact["`**partnerRel.contact**`"]
|
||||
direction TB
|
||||
style partnerRel.contact fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph partnerRel.contact:roles[ ]
|
||||
style partnerRel.contact:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:partnerRel.contact:owner[[partnerRel.contact:owner]]
|
||||
role:partnerRel.contact:admin[[partnerRel.contact:admin]]
|
||||
role:partnerRel.contact:referrer[[partnerRel.contact:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph partnerRel.anchorPerson["`**partnerRel.anchorPerson**`"]
|
||||
direction TB
|
||||
style partnerRel.anchorPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph partnerRel.anchorPerson:roles[ ]
|
||||
style partnerRel.anchorPerson:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:partnerRel.anchorPerson:owner[[partnerRel.anchorPerson:owner]]
|
||||
role:partnerRel.anchorPerson:admin[[partnerRel.anchorPerson:admin]]
|
||||
role:partnerRel.anchorPerson:referrer[[partnerRel.anchorPerson:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph partnerRel.holderPerson["`**partnerRel.holderPerson**`"]
|
||||
direction TB
|
||||
style partnerRel.holderPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph partnerRel.holderPerson:roles[ ]
|
||||
style partnerRel.holderPerson:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:partnerRel.holderPerson:owner[[partnerRel.holderPerson:owner]]
|
||||
role:partnerRel.holderPerson:admin[[partnerRel.holderPerson:admin]]
|
||||
role:partnerRel.holderPerson:referrer[[partnerRel.holderPerson:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph partnerRel:roles[ ]
|
||||
style partnerRel:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:partnerRel:owner[[partnerRel:owner]]
|
||||
role:partnerRel:admin[[partnerRel:admin]]
|
||||
role:partnerRel:agent[[partnerRel:agent]]
|
||||
role:partnerRel:tenant[[partnerRel:tenant]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph hsOfficePartner
|
||||
subgraph partnerRel.contact["`**partnerRel.contact**`"]
|
||||
direction TB
|
||||
style hsOfficePartner fill:#eee
|
||||
style partnerRel.contact fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
role:hsOfficePartner.owner[partner.admin]
|
||||
--> role:hsOfficePartner.admin[partner.admin]
|
||||
--> role:hsOfficePartner.agent[partner.agent]
|
||||
--> role:hsOfficePartner.tenant[partner.tenant]
|
||||
--> role:hsOfficePartner.guest[partner.guest]
|
||||
subgraph partnerRel.contact:roles[ ]
|
||||
style partnerRel.contact:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:partnerRel.contact:owner[[partnerRel.contact:owner]]
|
||||
role:partnerRel.contact:admin[[partnerRel.contact:admin]]
|
||||
role:partnerRel.contact:referrer[[partnerRel.contact:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph hsOfficeMembership
|
||||
subgraph membership["`**membership**`"]
|
||||
direction TB
|
||||
style membership fill:#dd4901,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
role:hsOfficeMembership.owner[membership.owner]
|
||||
%% permissions
|
||||
role:hsOfficeMembership.owner --> perm:hsOfficeMembership.*{{membership.*}}
|
||||
%% incoming
|
||||
role:global.admin ---> role:hsOfficeMembership.owner
|
||||
subgraph membership:roles[ ]
|
||||
style membership:roles fill:#dd4901,stroke:white
|
||||
|
||||
role:hsOfficeMembership.admin[membership.admin]
|
||||
%% permissions
|
||||
role:hsOfficeMembership.admin --> perm:hsOfficeMembership.edit{{membership.edit}}
|
||||
%% incoming
|
||||
role:hsOfficeMembership.owner ---> role:hsOfficeMembership.admin
|
||||
role:membership:owner[[membership:owner]]
|
||||
role:membership:admin[[membership:admin]]
|
||||
role:membership:referrer[[membership:referrer]]
|
||||
end
|
||||
|
||||
role:hsOfficeMembership.agent[membership.agent]
|
||||
%% incoming
|
||||
role:hsOfficeMembership.admin ---> role:hsOfficeMembership.agent
|
||||
role:hsOfficePartner.admin --> role:hsOfficeMembership.agent
|
||||
role:hsOfficeDebitor.admin --> role:hsOfficeMembership.agent
|
||||
%% outgoing
|
||||
role:hsOfficeMembership.agent --> role:hsOfficePartner.tenant
|
||||
role:hsOfficeMembership.agent --> role:hsOfficeDebitor.tenant
|
||||
subgraph membership:permissions[ ]
|
||||
style membership:permissions fill:#dd4901,stroke:white
|
||||
|
||||
role:hsOfficeMembership.tenant[membership.tenant]
|
||||
%% incoming
|
||||
role:hsOfficeMembership.agent --> role:hsOfficeMembership.tenant
|
||||
role:hsOfficePartner.agent --> role:hsOfficeMembership.tenant
|
||||
role:hsOfficeDebitor.agent --> role:hsOfficeMembership.tenant
|
||||
%% outgoing
|
||||
role:hsOfficeMembership.tenant --> role:hsOfficePartner.guest
|
||||
role:hsOfficeMembership.tenant --> role:hsOfficeDebitor.guest
|
||||
|
||||
role:hsOfficeMembership.guest[membership.guest]
|
||||
%% permissions
|
||||
role:hsOfficeMembership.guest --> perm:hsOfficeMembership.view{{membership.view}}
|
||||
%% incoming
|
||||
role:hsOfficeMembership.tenant --> role:hsOfficeMembership.guest
|
||||
role:hsOfficePartner.tenant --> role:hsOfficeMembership.guest
|
||||
role:hsOfficeDebitor.tenant --> role:hsOfficeMembership.guest
|
||||
perm:membership:INSERT{{membership:INSERT}}
|
||||
perm:membership:DELETE{{membership:DELETE}}
|
||||
perm:membership:UPDATE{{membership:UPDATE}}
|
||||
perm:membership:SELECT{{membership:SELECT}}
|
||||
end
|
||||
end
|
||||
|
||||
subgraph partnerRel.anchorPerson["`**partnerRel.anchorPerson**`"]
|
||||
direction TB
|
||||
style partnerRel.anchorPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph partnerRel.anchorPerson:roles[ ]
|
||||
style partnerRel.anchorPerson:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:partnerRel.anchorPerson:owner[[partnerRel.anchorPerson:owner]]
|
||||
role:partnerRel.anchorPerson:admin[[partnerRel.anchorPerson:admin]]
|
||||
role:partnerRel.anchorPerson:referrer[[partnerRel.anchorPerson:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph partnerRel.holderPerson["`**partnerRel.holderPerson**`"]
|
||||
direction TB
|
||||
style partnerRel.holderPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph partnerRel.holderPerson:roles[ ]
|
||||
style partnerRel.holderPerson:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:partnerRel.holderPerson:owner[[partnerRel.holderPerson:owner]]
|
||||
role:partnerRel.holderPerson:admin[[partnerRel.holderPerson:admin]]
|
||||
role:partnerRel.holderPerson:referrer[[partnerRel.holderPerson:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
%% granting roles to users
|
||||
user:creator ==> role:membership:owner
|
||||
|
||||
%% granting roles to roles
|
||||
role:global:admin -.-> role:partnerRel.anchorPerson:owner
|
||||
role:partnerRel.anchorPerson:owner -.-> role:partnerRel.anchorPerson:admin
|
||||
role:partnerRel.anchorPerson:admin -.-> role:partnerRel.anchorPerson:referrer
|
||||
role:global:admin -.-> role:partnerRel.holderPerson:owner
|
||||
role:partnerRel.holderPerson:owner -.-> role:partnerRel.holderPerson:admin
|
||||
role:partnerRel.holderPerson:admin -.-> role:partnerRel.holderPerson:referrer
|
||||
role:global:admin -.-> role:partnerRel.contact:owner
|
||||
role:partnerRel.contact:owner -.-> role:partnerRel.contact:admin
|
||||
role:partnerRel.contact:admin -.-> role:partnerRel.contact:referrer
|
||||
role:global:admin -.-> role:partnerRel:owner
|
||||
role:partnerRel:owner -.-> role:partnerRel:admin
|
||||
role:partnerRel.anchorPerson:admin -.-> role:partnerRel:admin
|
||||
role:partnerRel:admin -.-> role:partnerRel:agent
|
||||
role:partnerRel.holderPerson:admin -.-> role:partnerRel:agent
|
||||
role:partnerRel:agent -.-> role:partnerRel:tenant
|
||||
role:partnerRel.holderPerson:admin -.-> role:partnerRel:tenant
|
||||
role:partnerRel.contact:admin -.-> role:partnerRel:tenant
|
||||
role:partnerRel:tenant -.-> role:partnerRel.anchorPerson:referrer
|
||||
role:partnerRel:tenant -.-> role:partnerRel.holderPerson:referrer
|
||||
role:partnerRel:tenant -.-> role:partnerRel.contact:referrer
|
||||
role:partnerRel:admin ==> role:membership:owner
|
||||
role:membership:owner ==> role:membership:admin
|
||||
role:partnerRel:agent ==> role:membership:admin
|
||||
role:membership:admin ==> role:membership:referrer
|
||||
role:membership:referrer ==> role:partnerRel:tenant
|
||||
|
||||
%% granting permissions to roles
|
||||
role:global:admin ==> perm:membership:INSERT
|
||||
role:membership:owner ==> perm:membership:DELETE
|
||||
role:membership:admin ==> perm:membership:UPDATE
|
||||
role:membership:referrer ==> perm:membership:SELECT
|
||||
|
||||
```
|
||||
|
@ -1,4 +1,6 @@
|
||||
--liquibase formatted sql
|
||||
-- This code generated was by RbacViewPostgresGenerator, do not amend manually.
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-membership-rbac-OBJECT:1 endDelimiter:--//
|
||||
@ -15,148 +17,162 @@ call generateRbacRoleDescriptors('hsOfficeMembership', 'hs_office_membership');
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-membership-rbac-ROLES-CREATION:1 endDelimiter:--//
|
||||
--changeset hs-office-membership-rbac-insert-trigger:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
Creates and updates the roles and their assignments for membership entities.
|
||||
Creates the roles, grants and permission for the AFTER INSERT TRIGGER.
|
||||
*/
|
||||
|
||||
create or replace function hsOfficeMembershipRbacRolesTrigger()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
strict as $$
|
||||
create or replace procedure buildRbacSystemForHsOfficeMembership(
|
||||
NEW hs_office_membership
|
||||
)
|
||||
language plpgsql as $$
|
||||
|
||||
declare
|
||||
newHsOfficePartner hs_office_partner;
|
||||
newHsOfficeDebitor hs_office_debitor;
|
||||
newPartnerRel hs_office_relation;
|
||||
|
||||
begin
|
||||
call enterTriggerForObjectUuid(NEW.uuid);
|
||||
|
||||
select * from hs_office_partner as p where p.uuid = NEW.partnerUuid into newHsOfficePartner;
|
||||
select * from hs_office_debitor as c where c.uuid = NEW.mainDebitorUuid into newHsOfficeDebitor;
|
||||
SELECT partnerRel.*
|
||||
FROM hs_office_partner AS partner
|
||||
JOIN hs_office_relation AS partnerRel ON partnerRel.uuid = partner.partnerRelUuid
|
||||
WHERE partner.uuid = NEW.partnerUuid
|
||||
INTO newPartnerRel;
|
||||
assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.partnerUuid = %s', NEW.partnerUuid);
|
||||
|
||||
if TG_OP = 'INSERT' then
|
||||
|
||||
-- === ATTENTION: code generated from related Mermaid flowchart: ===
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeMembershipOwner(NEW),
|
||||
permissions => array['DELETE'],
|
||||
incomingSuperRoles => array[hsOfficeRelationAdmin(newPartnerRel)],
|
||||
userUuids => array[currentUserUuid()]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeMembershipOwner(NEW),
|
||||
permissions => array['DELETE'],
|
||||
incomingSuperRoles => array[globalAdmin()]
|
||||
);
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeMembershipAdmin(NEW),
|
||||
permissions => array['UPDATE'],
|
||||
incomingSuperRoles => array[
|
||||
hsOfficeMembershipOwner(NEW),
|
||||
hsOfficeRelationAgent(newPartnerRel)]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeMembershipAdmin(NEW),
|
||||
permissions => array['UPDATE'],
|
||||
incomingSuperRoles => array[hsOfficeMembershipOwner(NEW)]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeMembershipAgent(NEW),
|
||||
incomingSuperRoles => array[hsOfficeMembershipAdmin(NEW), hsOfficePartnerAdmin(newHsOfficePartner), hsOfficeDebitorAdmin(newHsOfficeDebitor)],
|
||||
outgoingSubRoles => array[hsOfficePartnerTenant(newHsOfficePartner), hsOfficeDebitorTenant(newHsOfficeDebitor)]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeMembershipTenant(NEW),
|
||||
incomingSuperRoles => array[hsOfficeMembershipAgent(NEW), hsOfficePartnerAgent(newHsOfficePartner), hsOfficeDebitorAgent(newHsOfficeDebitor)],
|
||||
outgoingSubRoles => array[hsOfficePartnerGuest(newHsOfficePartner), hsOfficeDebitorGuest(newHsOfficeDebitor)]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeMembershipGuest(NEW),
|
||||
permissions => array['SELECT'],
|
||||
incomingSuperRoles => array[hsOfficeMembershipTenant(NEW), hsOfficePartnerTenant(newHsOfficePartner), hsOfficeDebitorTenant(newHsOfficeDebitor)]
|
||||
);
|
||||
|
||||
-- === END of code generated from Mermaid flowchart. ===
|
||||
|
||||
else
|
||||
raise exception 'invalid usage of TRIGGER';
|
||||
end if;
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeMembershipReferrer(NEW),
|
||||
permissions => array['SELECT'],
|
||||
incomingSuperRoles => array[hsOfficeMembershipAdmin(NEW)],
|
||||
outgoingSubRoles => array[hsOfficeRelationTenant(newPartnerRel)]
|
||||
);
|
||||
|
||||
call leaveTriggerForObjectUuid(NEW.uuid);
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
/*
|
||||
An AFTER INSERT TRIGGER which creates the role structure for a new customer.
|
||||
AFTER INSERT TRIGGER to create the role+grant structure for a new hs_office_membership row.
|
||||
*/
|
||||
create trigger createRbacRolesForHsOfficeMembership_Trigger
|
||||
after insert
|
||||
on hs_office_membership
|
||||
|
||||
create or replace function insertTriggerForHsOfficeMembership_tf()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
strict as $$
|
||||
begin
|
||||
call buildRbacSystemForHsOfficeMembership(NEW);
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
create trigger insertTriggerForHsOfficeMembership_tg
|
||||
after insert on hs_office_membership
|
||||
for each row
|
||||
execute procedure hsOfficeMembershipRbacRolesTrigger();
|
||||
execute procedure insertTriggerForHsOfficeMembership_tf();
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-membership-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
||||
--changeset hs-office-membership-rbac-INSERT:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacIdentityViewFromProjection('hs_office_membership', $idName$
|
||||
'#' ||
|
||||
(select partnerNumber from hs_office_partner p where p.uuid = target.partnerUuid) ||
|
||||
memberNumberSuffix ||
|
||||
':' || (select split_part(idName, ':', 2) from hs_office_partner_iv p where p.uuid = target.partnerUuid)
|
||||
$idName$);
|
||||
|
||||
/*
|
||||
Creates INSERT INTO hs_office_membership permissions for the related global rows.
|
||||
*/
|
||||
do language plpgsql $$
|
||||
declare
|
||||
row global;
|
||||
begin
|
||||
call defineContext('create INSERT INTO hs_office_membership permissions for the related global rows');
|
||||
|
||||
FOR row IN SELECT * FROM global
|
||||
LOOP
|
||||
call grantPermissionToRole(
|
||||
createPermission(row.uuid, 'INSERT', 'hs_office_membership'),
|
||||
globalAdmin());
|
||||
END LOOP;
|
||||
END;
|
||||
$$;
|
||||
|
||||
/**
|
||||
Adds hs_office_membership INSERT permission to specified role of new global rows.
|
||||
*/
|
||||
create or replace function hs_office_membership_global_insert_tf()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
strict as $$
|
||||
begin
|
||||
call grantPermissionToRole(
|
||||
createPermission(NEW.uuid, 'INSERT', 'hs_office_membership'),
|
||||
globalAdmin());
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
-- z_... is to put it at the end of after insert triggers, to make sure the roles exist
|
||||
create trigger z_hs_office_membership_global_insert_tg
|
||||
after insert on global
|
||||
for each row
|
||||
execute procedure hs_office_membership_global_insert_tf();
|
||||
|
||||
/**
|
||||
Checks if the user or assumed roles are allowed to insert a row to hs_office_membership,
|
||||
where only global-admin has that permission.
|
||||
*/
|
||||
create or replace function hs_office_membership_insert_permission_missing_tf()
|
||||
returns trigger
|
||||
language plpgsql as $$
|
||||
begin
|
||||
raise exception '[403] insert into hs_office_membership not allowed for current subjects % (%)',
|
||||
currentSubjects(), currentSubjectsUuids();
|
||||
end; $$;
|
||||
|
||||
create trigger hs_office_membership_insert_permission_check_tg
|
||||
before insert on hs_office_membership
|
||||
for each row
|
||||
when ( not isGlobalAdmin() )
|
||||
execute procedure hs_office_membership_insert_permission_missing_tf();
|
||||
--//
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-membership-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
call generateRbacIdentityViewFromQuery('hs_office_membership',
|
||||
$idName$
|
||||
SELECT m.uuid AS uuid,
|
||||
'M-' || p.partnerNumber || m.memberNumberSuffix as idName
|
||||
FROM hs_office_membership AS m
|
||||
JOIN hs_office_partner AS p ON p.uuid = m.partnerUuid
|
||||
$idName$);
|
||||
--//
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-membership-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacRestrictedView('hs_office_membership',
|
||||
orderby => 'target.memberNumberSuffix',
|
||||
columnUpdates => $updates$
|
||||
$orderBy$
|
||||
validity
|
||||
$orderBy$,
|
||||
$updates$
|
||||
validity = new.validity,
|
||||
reasonForTermination = new.reasonForTermination,
|
||||
membershipFeeBillable = new.membershipFeeBillable
|
||||
membershipFeeBillable = new.membershipFeeBillable,
|
||||
reasonForTermination = new.reasonForTermination
|
||||
$updates$);
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-membership-rbac-NEW-Membership:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
/*
|
||||
Creates a global permission for new-membership and assigns it to the hostsharing admins role.
|
||||
*/
|
||||
do language plpgsql $$
|
||||
declare
|
||||
addCustomerPermissions uuid[];
|
||||
globalObjectUuid uuid;
|
||||
globalAdminRoleUuid uuid ;
|
||||
begin
|
||||
call defineContext('granting global new-membership permission to global admin role', null, null, null);
|
||||
|
||||
globalAdminRoleUuid := findRoleId(globalAdmin());
|
||||
globalObjectUuid := (select uuid from global);
|
||||
addCustomerPermissions := createPermissions(globalObjectUuid, array ['new-membership']);
|
||||
call grantPermissionsToRole(globalAdminRoleUuid, addCustomerPermissions);
|
||||
end;
|
||||
$$;
|
||||
|
||||
/**
|
||||
Used by the trigger to prevent the add-customer to current user respectively assumed roles.
|
||||
*/
|
||||
create or replace function addHsOfficeMembershipNotAllowedForCurrentSubjects()
|
||||
returns trigger
|
||||
language PLPGSQL
|
||||
as $$
|
||||
begin
|
||||
raise exception '[403] new-membership not permitted for %',
|
||||
array_to_string(currentSubjects(), ';', 'null');
|
||||
end; $$;
|
||||
|
||||
/**
|
||||
Checks if the user or assumed roles are allowed to create a new customer.
|
||||
*/
|
||||
create trigger hs_office_membership_insert_trigger
|
||||
before insert
|
||||
on hs_office_membership
|
||||
for each row
|
||||
-- TODO.spec: who is allowed to create new memberships
|
||||
when ( not hasAssumedRole() )
|
||||
execute procedure addHsOfficeMembershipNotAllowedForCurrentSubjects();
|
||||
--//
|
||||
|
||||
|
@ -9,35 +9,27 @@
|
||||
Creates a single membership test record.
|
||||
*/
|
||||
create or replace procedure createHsOfficeMembershipTestData(
|
||||
forPartnerTradeName varchar,
|
||||
forMainDebitorNumberSuffix numeric,
|
||||
forPartnerNumber numeric(5),
|
||||
newMemberNumberSuffix char(2) )
|
||||
language plpgsql as $$
|
||||
declare
|
||||
currentTask varchar;
|
||||
idName varchar;
|
||||
relatedPartner hs_office_partner;
|
||||
relatedDebitor hs_office_debitor;
|
||||
begin
|
||||
idName := cleanIdentifier( forPartnerTradeName || '#' || forMainDebitorNumberSuffix);
|
||||
currentTask := 'creating Membership test-data ' || idName;
|
||||
currentTask := 'creating Membership test-data ' ||
|
||||
'P-' || forPartnerNumber::text ||
|
||||
'M-...' || newMemberNumberSuffix;
|
||||
call defineContext(currentTask, null, 'superuser-alex@hostsharing.net', 'global#global.admin');
|
||||
execute format('set local hsadminng.currentTask to %L', currentTask);
|
||||
|
||||
select partner.* from hs_office_partner partner
|
||||
join hs_office_person person on person.uuid = partner.personUuid
|
||||
where person.tradeName = forPartnerTradeName into relatedPartner;
|
||||
select d.* from hs_office_debitor d
|
||||
where d.partneruuid = relatedPartner.uuid
|
||||
and d.debitorNumberSuffix = forMainDebitorNumberSuffix
|
||||
into relatedDebitor;
|
||||
where partner.partnerNumber = forPartnerNumber into relatedPartner;
|
||||
|
||||
raise notice 'creating test Membership: %', idName;
|
||||
raise notice 'creating test Membership: M-% %', forPartnerNumber, newMemberNumberSuffix;
|
||||
raise notice '- using partner (%): %', relatedPartner.uuid, relatedPartner;
|
||||
raise notice '- using debitor (%): %', relatedDebitor.uuid, relatedDebitor;
|
||||
insert
|
||||
into hs_office_membership (uuid, partneruuid, maindebitoruuid, memberNumberSuffix, validity, reasonfortermination)
|
||||
values (uuid_generate_v4(), relatedPartner.uuid, relatedDebitor.uuid, newMemberNumberSuffix, daterange('20221001' , null, '[]'), 'NONE');
|
||||
into hs_office_membership (uuid, partneruuid, memberNumberSuffix, validity, reasonfortermination)
|
||||
values (uuid_generate_v4(), relatedPartner.uuid, newMemberNumberSuffix, daterange('20221001' , null, '[]'), 'NONE');
|
||||
end; $$;
|
||||
--//
|
||||
|
||||
@ -48,9 +40,9 @@ end; $$;
|
||||
|
||||
do language plpgsql $$
|
||||
begin
|
||||
call createHsOfficeMembershipTestData('First GmbH', 11, '01');
|
||||
call createHsOfficeMembershipTestData('Second e.K.', 12, '02');
|
||||
call createHsOfficeMembershipTestData('Third OHG', 13, '03');
|
||||
call createHsOfficeMembershipTestData(10001, '01');
|
||||
call createHsOfficeMembershipTestData(10002, '02');
|
||||
call createHsOfficeMembershipTestData(10003, '03');
|
||||
end;
|
||||
$$;
|
||||
--//
|
||||
|
@ -42,7 +42,7 @@ begin
|
||||
|
||||
-- coopsharestransactions cannot be edited nor deleted, just created+viewed
|
||||
call grantPermissionsToRole(
|
||||
getRoleId(hsOfficeMembershipTenant(newHsOfficeMembership)),
|
||||
getRoleId(hsOfficeMembershipReferrer(newHsOfficeMembership)),
|
||||
createPermissions(NEW.uuid, array ['SELECT'])
|
||||
);
|
||||
|
||||
|
@ -42,7 +42,7 @@ begin
|
||||
|
||||
-- coopassetstransactions cannot be edited nor deleted, just created+viewed
|
||||
call grantPermissionsToRole(
|
||||
getRoleId(hsOfficeMembershipTenant(newHsOfficeMembership)),
|
||||
getRoleId(hsOfficeMembershipReferrer(newHsOfficeMembership)),
|
||||
createPermissions(NEW.uuid, array ['SELECT'])
|
||||
);
|
||||
|
||||
|
@ -129,7 +129,8 @@ public class ArchitectureTest {
|
||||
public static final ArchRule hsOfficeBankAccountPackageRule = classes()
|
||||
.that().resideInAPackage("..hs.office.bankaccount..")
|
||||
.should().onlyBeAccessed().byClassesThat()
|
||||
.resideInAnyPackage("..hs.office.bankaccount..",
|
||||
.resideInAnyPackage(
|
||||
"..hs.office.bankaccount..",
|
||||
"..hs.office.sepamandate..",
|
||||
"..hs.office.debitor..",
|
||||
"..hs.office.migration..");
|
||||
@ -139,7 +140,8 @@ public class ArchitectureTest {
|
||||
public static final ArchRule hsOfficeSepaMandatePackageRule = classes()
|
||||
.that().resideInAPackage("..hs.office.sepamandate..")
|
||||
.should().onlyBeAccessed().byClassesThat()
|
||||
.resideInAnyPackage("..hs.office.sepamandate..",
|
||||
.resideInAnyPackage(
|
||||
"..hs.office.sepamandate..",
|
||||
"..hs.office.debitor..",
|
||||
"..hs.office.migration..");
|
||||
|
||||
@ -148,7 +150,9 @@ public class ArchitectureTest {
|
||||
public static final ArchRule hsOfficeContactPackageRule = classes()
|
||||
.that().resideInAPackage("..hs.office.contact..")
|
||||
.should().onlyBeAccessed().byClassesThat()
|
||||
.resideInAnyPackage("..hs.office.contact..", "..hs.office.relation..",
|
||||
.resideInAnyPackage(
|
||||
"..hs.office.contact..",
|
||||
"..hs.office.relation..",
|
||||
"..hs.office.partner..",
|
||||
"..hs.office.debitor..",
|
||||
"..hs.office.membership..",
|
||||
@ -159,37 +163,46 @@ public class ArchitectureTest {
|
||||
public static final ArchRule hsOfficePersonPackageRule = classes()
|
||||
.that().resideInAPackage("..hs.office.person..")
|
||||
.should().onlyBeAccessed().byClassesThat()
|
||||
.resideInAnyPackage("..hs.office.person..", "..hs.office.relation..",
|
||||
.resideInAnyPackage(
|
||||
"..hs.office.person..",
|
||||
"..hs.office.relation..",
|
||||
"..hs.office.partner..",
|
||||
"..hs.office.debitor..",
|
||||
"..hs.office.membership..",
|
||||
"..hs.office.migration..");
|
||||
"..hs.office.migration..")
|
||||
.orShould().haveNameNotMatching(".*Test$");
|
||||
|
||||
|
||||
@ArchTest
|
||||
@SuppressWarnings("unused")
|
||||
public static final ArchRule hsOfficeRelationPackageRule = classes()
|
||||
.that().resideInAPackage("..hs.office.relation..")
|
||||
.should().onlyBeAccessed().byClassesThat()
|
||||
.resideInAnyPackage("..hs.office.relation..",
|
||||
.resideInAnyPackage(
|
||||
"..hs.office.relation..",
|
||||
"..hs.office.partner..",
|
||||
"..hs.office.migration..");
|
||||
"..hs.office.migration..")
|
||||
.orShould().haveNameNotMatching(".*Test$");
|
||||
|
||||
@ArchTest
|
||||
@SuppressWarnings("unused")
|
||||
public static final ArchRule hsOfficePartnerPackageRule = classes()
|
||||
.that().resideInAPackage("..hs.office.partner..")
|
||||
.should().onlyBeAccessed().byClassesThat()
|
||||
.resideInAnyPackage("..hs.office.partner..",
|
||||
.resideInAnyPackage(
|
||||
"..hs.office.partner..",
|
||||
"..hs.office.debitor..",
|
||||
"..hs.office.membership..",
|
||||
"..hs.office.migration..");
|
||||
"..hs.office.migration..")
|
||||
.orShould().haveNameNotMatching(".*Test$");
|
||||
|
||||
@ArchTest
|
||||
@SuppressWarnings("unused")
|
||||
public static final ArchRule hsOfficeMembershipPackageRule = classes()
|
||||
.that().resideInAPackage("..hs.office.membership..")
|
||||
.should().onlyBeAccessed().byClassesThat()
|
||||
.resideInAnyPackage("..hs.office.membership..",
|
||||
.resideInAnyPackage(
|
||||
"..hs.office.membership..",
|
||||
"..hs.office.coopassets..",
|
||||
"..hs.office.coopshares..",
|
||||
"..hs.office.migration..");
|
||||
|
@ -19,7 +19,7 @@ class HsOfficeBankAccountEntityUnitTest {
|
||||
.iban("DE02370502990000684712")
|
||||
.bic("COKSDE33")
|
||||
.build();
|
||||
assertThat("" + givenBankAccount).isEqualTo("bankAccount(holder='given holder', iban='DE02370502990000684712', bic='COKSDE33')");
|
||||
assertThat(givenBankAccount.toString()).isEqualTo("bankAccount(DE02370502990000684712: holder='given holder', bic='COKSDE33')");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -102,23 +102,21 @@ class HsOfficeBankAccountRepositoryIntegrationTest extends ContextBasedTestWithC
|
||||
final var roles = rawRoleRepo.findAll();
|
||||
assertThat(distinctRoleNamesOf(roles)).containsExactlyInAnyOrder(Array.from(
|
||||
initialRoleNames,
|
||||
"hs_office_bankaccount#sometempaccC.owner",
|
||||
"hs_office_bankaccount#sometempaccC.admin",
|
||||
"hs_office_bankaccount#sometempaccC.tenant",
|
||||
"hs_office_bankaccount#sometempaccC.guest"
|
||||
"hs_office_bankaccount#DE25500105176934832579.owner",
|
||||
"hs_office_bankaccount#DE25500105176934832579.admin",
|
||||
"hs_office_bankaccount#DE25500105176934832579.referrer"
|
||||
));
|
||||
assertThat(distinctGrantDisplaysOf(rawGrantRepo.findAll())).containsExactlyInAnyOrder(Array.fromFormatted(
|
||||
initialGrantNames,
|
||||
"{ grant perm DELETE on hs_office_bankaccount#sometempaccC to role hs_office_bankaccount#sometempaccC.owner by system and assume }",
|
||||
"{ grant role hs_office_bankaccount#sometempaccC.owner to role global#global.admin by system and assume }",
|
||||
"{ grant role hs_office_bankaccount#sometempaccC.owner to user selfregistered-user-drew@hostsharing.org by global#global.admin and assume }",
|
||||
"{ grant perm DELETE on hs_office_bankaccount#DE25500105176934832579 to role hs_office_bankaccount#DE25500105176934832579.owner by system and assume }",
|
||||
"{ grant role hs_office_bankaccount#DE25500105176934832579.owner to role global#global.admin by system and assume }",
|
||||
"{ grant role hs_office_bankaccount#DE25500105176934832579.owner to user selfregistered-user-drew@hostsharing.org by hs_office_bankaccount#DE25500105176934832579.owner and assume }",
|
||||
|
||||
"{ grant role hs_office_bankaccount#sometempaccC.admin to role hs_office_bankaccount#sometempaccC.owner by system and assume }",
|
||||
"{ grant role hs_office_bankaccount#DE25500105176934832579.admin to role hs_office_bankaccount#DE25500105176934832579.owner by system and assume }",
|
||||
"{ grant perm UPDATE on hs_office_bankaccount#DE25500105176934832579 to role hs_office_bankaccount#DE25500105176934832579.admin by system and assume }",
|
||||
|
||||
"{ grant role hs_office_bankaccount#sometempaccC.tenant to role hs_office_bankaccount#sometempaccC.admin by system and assume }",
|
||||
|
||||
"{ grant perm SELECT on hs_office_bankaccount#sometempaccC to role hs_office_bankaccount#sometempaccC.guest by system and assume }",
|
||||
"{ grant role hs_office_bankaccount#sometempaccC.guest to role hs_office_bankaccount#sometempaccC.tenant by system and assume }",
|
||||
"{ grant perm SELECT on hs_office_bankaccount#DE25500105176934832579 to role hs_office_bankaccount#DE25500105176934832579.referrer by system and assume }",
|
||||
"{ grant role hs_office_bankaccount#DE25500105176934832579.referrer to role hs_office_bankaccount#DE25500105176934832579.admin by system and assume }",
|
||||
null
|
||||
));
|
||||
}
|
||||
@ -241,10 +239,6 @@ class HsOfficeBankAccountRepositoryIntegrationTest extends ContextBasedTestWithC
|
||||
final var initialRoleNames = distinctRoleNamesOf(rawRoleRepo.findAll());
|
||||
final var initialGrantNames = distinctGrantDisplaysOf(rawGrantRepo.findAll());
|
||||
final var givenBankAccount = givenSomeTemporaryBankAccount("selfregistered-user-drew@hostsharing.org");
|
||||
assertThat(distinctRoleNamesOf(rawRoleRepo.findAll()).size()).as("unexpected number of roles created")
|
||||
.isEqualTo(initialRoleNames.size() + 4);
|
||||
assertThat(distinctGrantDisplaysOf(rawGrantRepo.findAll()).size()).as("unexpected number of grants created")
|
||||
.isEqualTo(initialGrantNames.size() + 7);
|
||||
|
||||
// when
|
||||
final var result = jpaAttempt.transacted(() -> {
|
||||
|
@ -105,19 +105,18 @@ class HsOfficeContactRepositoryIntegrationTest extends ContextBasedTestWithClean
|
||||
initialRoleNames,
|
||||
"hs_office_contact#anothernewcontact.owner",
|
||||
"hs_office_contact#anothernewcontact.admin",
|
||||
"hs_office_contact#anothernewcontact.tenant",
|
||||
"hs_office_contact#anothernewcontact.guest"
|
||||
"hs_office_contact#anothernewcontact.referrer"
|
||||
));
|
||||
assertThat(distinctGrantDisplaysOf(rawGrantRepo.findAll())).containsExactlyInAnyOrder(Array.from(
|
||||
assertThat(distinctGrantDisplaysOf(rawGrantRepo.findAll())).containsExactlyInAnyOrder(Array.fromFormatted(
|
||||
initialGrantNames,
|
||||
"{ grant role hs_office_contact#anothernewcontact.owner to role global#global.admin by system and assume }",
|
||||
"{ grant perm UPDATE on hs_office_contact#anothernewcontact to role hs_office_contact#anothernewcontact.admin by system and assume }",
|
||||
"{ grant role hs_office_contact#anothernewcontact.tenant to role hs_office_contact#anothernewcontact.admin by system and assume }",
|
||||
"{ grant perm DELETE on hs_office_contact#anothernewcontact to role hs_office_contact#anothernewcontact.owner by system and assume }",
|
||||
"{ grant role hs_office_contact#anothernewcontact.admin to role hs_office_contact#anothernewcontact.owner by system and assume }",
|
||||
"{ grant perm SELECT on hs_office_contact#anothernewcontact to role hs_office_contact#anothernewcontact.guest by system and assume }",
|
||||
"{ grant role hs_office_contact#anothernewcontact.guest to role hs_office_contact#anothernewcontact.tenant by system and assume }",
|
||||
"{ grant role hs_office_contact#anothernewcontact.owner to user selfregistered-user-drew@hostsharing.org by global#global.admin and assume }"
|
||||
"{ grant role hs_office_contact#anothernewcontact.owner to role global#global.admin by system and assume }",
|
||||
"{ grant perm UPDATE on hs_office_contact#anothernewcontact to role hs_office_contact#anothernewcontact.admin by system and assume }",
|
||||
"{ grant role hs_office_contact#anothernewcontact.owner to user selfregistered-user-drew@hostsharing.org by hs_office_contact#anothernewcontact.owner and assume }",
|
||||
"{ grant perm DELETE on hs_office_contact#anothernewcontact to role hs_office_contact#anothernewcontact.owner by system and assume }",
|
||||
"{ grant role hs_office_contact#anothernewcontact.admin to role hs_office_contact#anothernewcontact.owner by system and assume }",
|
||||
|
||||
"{ grant perm SELECT on hs_office_contact#anothernewcontact to role hs_office_contact#anothernewcontact.referrer by system and assume }",
|
||||
"{ grant role hs_office_contact#anothernewcontact.referrer to role hs_office_contact#anothernewcontact.admin by system and assume }"
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -276,7 +276,7 @@ class HsOfficeCoopAssetsTransactionControllerAcceptanceTest extends ContextBased
|
||||
|
||||
@Test
|
||||
@Accepts({ "CoopAssetTransaction:X(Access Control)" })
|
||||
void contactAdminUser_canGetRelatedCoopAssetTransaction() {
|
||||
void partnerPersonUser_canGetRelatedCoopAssetTransaction() {
|
||||
context.define("superuser-alex@hostsharing.net");
|
||||
final var givenCoopAssetTransactionUuid = coopAssetsTransactionRepo.findCoopAssetsTransactionByOptionalMembershipUuidAndDateRange(
|
||||
null,
|
||||
@ -285,7 +285,7 @@ class HsOfficeCoopAssetsTransactionControllerAcceptanceTest extends ContextBased
|
||||
|
||||
RestAssured // @formatter:off
|
||||
.given()
|
||||
.header("current-user", "contact-admin@firstcontact.example.com")
|
||||
.header("current-user", "person-FirstGmbH@example.com")
|
||||
.port(port)
|
||||
.when()
|
||||
.get("http://localhost/api/hs/office/coopassetstransactions/" + givenCoopAssetTransactionUuid)
|
||||
|
@ -23,27 +23,27 @@ class HsOfficeCoopAssetsTransactionEntityUnitTest {
|
||||
void toStringContainsAlmostAllPropertiesAccount() {
|
||||
final var result = givenCoopAssetTransaction.toString();
|
||||
|
||||
assertThat(result).isEqualTo("CoopAssetsTransaction(1000101, 2020-01-01, DEPOSIT, 128.00, some-ref)");
|
||||
assertThat(result).isEqualTo("CoopAssetsTransaction(M-1000101: 2020-01-01, DEPOSIT, 128.00, some-ref)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void toShortStringContainsOnlyMemberNumberSuffixAndSharesCountOnly() {
|
||||
final var result = givenCoopAssetTransaction.toShortString();
|
||||
|
||||
assertThat(result).isEqualTo("1000101+128.00");
|
||||
assertThat(result).isEqualTo("M-1000101:+128.00");
|
||||
}
|
||||
|
||||
@Test
|
||||
void toStringWithEmptyTransactionDoesNotThrowException() {
|
||||
final var result = givenEmptyCoopAssetsTransaction.toString();
|
||||
|
||||
assertThat(result).isEqualTo("CoopAssetsTransaction()");
|
||||
assertThat(result).isEqualTo("CoopAssetsTransaction(M-?????: )");
|
||||
}
|
||||
|
||||
@Test
|
||||
void toShortStringEmptyTransactionDoesNotThrowException() {
|
||||
final var result = givenEmptyCoopAssetsTransaction.toShortString();
|
||||
|
||||
assertThat(result).isEqualTo("nullnu");
|
||||
assertThat(result).isEqualTo("M-?????:+0.00");
|
||||
}
|
||||
}
|
||||
|
@ -114,7 +114,7 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase
|
||||
.map(s -> s.replace("hs_office_", ""))
|
||||
.containsExactlyInAnyOrder(Array.fromFormatted(
|
||||
initialGrantNames,
|
||||
"{ grant perm SELECT on coopassetstransaction#temprefB to role membership#1000101:....tenant by system and assume }",
|
||||
"{ grant perm SELECT on coopassetstransaction#temprefB to role membership#M-1000101.referrer by system and assume }",
|
||||
null));
|
||||
}
|
||||
|
||||
@ -141,17 +141,17 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase
|
||||
// then
|
||||
allTheseCoopAssetsTransactionsAreReturned(
|
||||
result,
|
||||
"CoopAssetsTransaction(1000101, 2010-03-15, DEPOSIT, 320.00, ref 1000101-1, initial deposit)",
|
||||
"CoopAssetsTransaction(1000101, 2021-09-01, DISBURSAL, -128.00, ref 1000101-2, partial disbursal)",
|
||||
"CoopAssetsTransaction(1000101, 2022-10-20, ADJUSTMENT, 128.00, ref 1000101-3, some adjustment)",
|
||||
"CoopAssetsTransaction(M-1000101: 2010-03-15, DEPOSIT, 320.00, ref 1000101-1, initial deposit)",
|
||||
"CoopAssetsTransaction(M-1000101: 2021-09-01, DISBURSAL, -128.00, ref 1000101-2, partial disbursal)",
|
||||
"CoopAssetsTransaction(M-1000101: 2022-10-20, ADJUSTMENT, 128.00, ref 1000101-3, some adjustment)",
|
||||
|
||||
"CoopAssetsTransaction(1000202, 2010-03-15, DEPOSIT, 320.00, ref 1000202-1, initial deposit)",
|
||||
"CoopAssetsTransaction(1000202, 2021-09-01, DISBURSAL, -128.00, ref 1000202-2, partial disbursal)",
|
||||
"CoopAssetsTransaction(1000202, 2022-10-20, ADJUSTMENT, 128.00, ref 1000202-3, some adjustment)",
|
||||
"CoopAssetsTransaction(M-1000202: 2010-03-15, DEPOSIT, 320.00, ref 1000202-1, initial deposit)",
|
||||
"CoopAssetsTransaction(M-1000202: 2021-09-01, DISBURSAL, -128.00, ref 1000202-2, partial disbursal)",
|
||||
"CoopAssetsTransaction(M-1000202: 2022-10-20, ADJUSTMENT, 128.00, ref 1000202-3, some adjustment)",
|
||||
|
||||
"CoopAssetsTransaction(1000303, 2010-03-15, DEPOSIT, 320.00, ref 1000303-1, initial deposit)",
|
||||
"CoopAssetsTransaction(1000303, 2021-09-01, DISBURSAL, -128.00, ref 1000303-2, partial disbursal)",
|
||||
"CoopAssetsTransaction(1000303, 2022-10-20, ADJUSTMENT, 128.00, ref 1000303-3, some adjustment)");
|
||||
"CoopAssetsTransaction(M-1000303: 2010-03-15, DEPOSIT, 320.00, ref 1000303-1, initial deposit)",
|
||||
"CoopAssetsTransaction(M-1000303: 2021-09-01, DISBURSAL, -128.00, ref 1000303-2, partial disbursal)",
|
||||
"CoopAssetsTransaction(M-1000303: 2022-10-20, ADJUSTMENT, 128.00, ref 1000303-3, some adjustment)");
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -169,9 +169,9 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase
|
||||
// then
|
||||
allTheseCoopAssetsTransactionsAreReturned(
|
||||
result,
|
||||
"CoopAssetsTransaction(1000202, 2010-03-15, DEPOSIT, 320.00, ref 1000202-1, initial deposit)",
|
||||
"CoopAssetsTransaction(1000202, 2021-09-01, DISBURSAL, -128.00, ref 1000202-2, partial disbursal)",
|
||||
"CoopAssetsTransaction(1000202, 2022-10-20, ADJUSTMENT, 128.00, ref 1000202-3, some adjustment)");
|
||||
"CoopAssetsTransaction(M-1000202: 2010-03-15, DEPOSIT, 320.00, ref 1000202-1, initial deposit)",
|
||||
"CoopAssetsTransaction(M-1000202: 2021-09-01, DISBURSAL, -128.00, ref 1000202-2, partial disbursal)",
|
||||
"CoopAssetsTransaction(M-1000202: 2022-10-20, ADJUSTMENT, 128.00, ref 1000202-3, some adjustment)");
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -189,13 +189,13 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase
|
||||
// then
|
||||
allTheseCoopAssetsTransactionsAreReturned(
|
||||
result,
|
||||
"CoopAssetsTransaction(1000202, 2021-09-01, DISBURSAL, -128.00, ref 1000202-2, partial disbursal)");
|
||||
"CoopAssetsTransaction(M-1000202: 2021-09-01, DISBURSAL, -128.00, ref 1000202-2, partial disbursal)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void normalUser_canViewOnlyRelatedCoopAssetsTransactions() {
|
||||
public void partnerPersonAdmin_canViewRelatedCoopAssetsTransactions() {
|
||||
// given:
|
||||
context("superuser-alex@hostsharing.net", "hs_office_partner#10001:FirstGmbH-firstcontact.admin");
|
||||
context("superuser-alex@hostsharing.net", "hs_office_person#FirstGmbH.admin");
|
||||
|
||||
// when:
|
||||
final var result = coopAssetsTransactionRepo.findCoopAssetsTransactionByOptionalMembershipUuidAndDateRange(
|
||||
@ -206,9 +206,9 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase
|
||||
// then:
|
||||
exactlyTheseCoopAssetsTransactionsAreReturned(
|
||||
result,
|
||||
"CoopAssetsTransaction(1000101, 2010-03-15, DEPOSIT, 320.00, ref 1000101-1, initial deposit)",
|
||||
"CoopAssetsTransaction(1000101, 2021-09-01, DISBURSAL, -128.00, ref 1000101-2, partial disbursal)",
|
||||
"CoopAssetsTransaction(1000101, 2022-10-20, ADJUSTMENT, 128.00, ref 1000101-3, some adjustment)");
|
||||
"CoopAssetsTransaction(M-1000101: 2010-03-15, DEPOSIT, 320.00, ref 1000101-1, initial deposit)",
|
||||
"CoopAssetsTransaction(M-1000101: 2021-09-01, DISBURSAL, -128.00, ref 1000101-2, partial disbursal)",
|
||||
"CoopAssetsTransaction(M-1000101: 2022-10-20, ADJUSTMENT, 128.00, ref 1000101-3, some adjustment)");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -218,17 +218,27 @@ class HsOfficeCoopSharesTransactionControllerAcceptanceTest extends ContextBased
|
||||
|
||||
@Test
|
||||
@Accepts({"CoopShareTransaction:X(Access Control)"})
|
||||
void contactAdminUser_canGetRelatedCoopShareTransaction() {
|
||||
void partnerPersonUser_canGetRelatedCoopShareTransaction() {
|
||||
context.define("superuser-alex@hostsharing.net");
|
||||
final var givenCoopShareTransactionUuid = coopSharesTransactionRepo.findCoopSharesTransactionByOptionalMembershipUuidAndDateRange(null, LocalDate.of(2010, 3, 15), LocalDate.of(2010, 3, 15)).get(0).getUuid();
|
||||
|
||||
RestAssured // @formatter:off
|
||||
.given().header("current-user", "contact-admin@firstcontact.example.com").port(port).when().get("http://localhost/api/hs/office/coopsharestransactions/" + givenCoopShareTransactionUuid).then().log().body().assertThat().statusCode(200).contentType("application/json").body("", lenientlyEquals("""
|
||||
{
|
||||
"transactionType": "SUBSCRIPTION",
|
||||
"shareCount": 4
|
||||
}
|
||||
""")); // @formatter:on
|
||||
.given()
|
||||
.header("current-user", "person-FirstGmbH@example.com")
|
||||
.port(port)
|
||||
.when()
|
||||
.get("http://localhost/api/hs/office/coopsharestransactions/" + givenCoopShareTransactionUuid)
|
||||
.then()
|
||||
.log().body()
|
||||
.assertThat()
|
||||
.statusCode(200)
|
||||
.contentType("application/json")
|
||||
.body("", lenientlyEquals("""
|
||||
{
|
||||
"transactionType": "SUBSCRIPTION",
|
||||
"shareCount": 4
|
||||
}
|
||||
""")); // @formatter:on
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -88,7 +88,6 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase
|
||||
context("superuser-alex@hostsharing.net");
|
||||
final var initialRoleNames = distinctRoleNamesOf(rawRoleRepo.findAll());
|
||||
final var initialGrantNames = distinctGrantDisplaysOf(rawGrantRepo.findAll()).stream()
|
||||
.map(s -> s.replace("FirstGmbH-firstcontact", "..."))
|
||||
.map(s -> s.replace("hs_office_", ""))
|
||||
.toList();
|
||||
|
||||
@ -109,11 +108,10 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase
|
||||
final var all = rawRoleRepo.findAll();
|
||||
assertThat(distinctRoleNamesOf(all)).containsExactlyInAnyOrder(Array.from(initialRoleNames)); // no new roles created
|
||||
assertThat(distinctGrantDisplaysOf(rawGrantRepo.findAll()))
|
||||
.map(s -> s.replace("FirstGmbH-firstcontact", "..."))
|
||||
.map(s -> s.replace("hs_office_", ""))
|
||||
.containsExactlyInAnyOrder(Array.fromFormatted(
|
||||
initialGrantNames,
|
||||
"{ grant perm SELECT on coopsharestransaction#temprefB to role membership#1000101:....tenant by system and assume }",
|
||||
"{ grant perm SELECT on coopsharestransaction#temprefB to role membership#M-1000101.referrer by system and assume }",
|
||||
null));
|
||||
}
|
||||
|
||||
@ -194,7 +192,7 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase
|
||||
@Test
|
||||
public void normalUser_canViewOnlyRelatedCoopSharesTransactions() {
|
||||
// given:
|
||||
context("superuser-alex@hostsharing.net", "hs_office_partner#10001:FirstGmbH-firstcontact.admin");
|
||||
context("superuser-alex@hostsharing.net", "hs_office_membership#M-1000101.admin");
|
||||
|
||||
// when:
|
||||
final var result = coopSharesTransactionRepo.findCoopSharesTransactionByOptionalMembershipUuidAndDateRange(
|
||||
|
@ -7,6 +7,9 @@ import net.hostsharing.hsadminng.context.Context;
|
||||
import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountRepository;
|
||||
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRepository;
|
||||
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerRepository;
|
||||
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonRepository;
|
||||
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
|
||||
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRepository;
|
||||
import net.hostsharing.hsadminng.hs.office.test.ContextBasedTestWithCleanup;
|
||||
import net.hostsharing.test.Accepts;
|
||||
import net.hostsharing.test.JpaAttempt;
|
||||
@ -24,6 +27,7 @@ import jakarta.persistence.EntityManager;
|
||||
import jakarta.persistence.PersistenceContext;
|
||||
import java.util.UUID;
|
||||
|
||||
import static net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType.DEBITOR;
|
||||
import static net.hostsharing.test.IsValidUuidMatcher.isUuidValid;
|
||||
import static net.hostsharing.test.JsonMatcher.lenientlyEquals;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@ -57,6 +61,12 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
|
||||
@Autowired
|
||||
HsOfficeBankAccountRepository bankAccountRepo;
|
||||
|
||||
@Autowired
|
||||
HsOfficePersonRepository personRepo;
|
||||
|
||||
@Autowired
|
||||
HsOfficeRelationRepository relRepo;
|
||||
|
||||
@Autowired
|
||||
JpaAttempt jpaAttempt;
|
||||
|
||||
@ -81,37 +91,135 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
|
||||
.contentType("application/json")
|
||||
.body("", lenientlyEquals("""
|
||||
[
|
||||
{
|
||||
"debitorNumber": 1000111,
|
||||
"debitorNumberSuffix": 11,
|
||||
"partner": { "person": { "personType": "LEGAL_PERSON" } },
|
||||
"billingContact": { "label": "first contact" },
|
||||
"vatId": null,
|
||||
"vatCountryCode": null,
|
||||
"vatBusiness": true,
|
||||
"refundBankAccount": { "holder": "First GmbH" }
|
||||
},
|
||||
{
|
||||
"debitorNumber": 1000212,
|
||||
"debitorNumberSuffix": 12,
|
||||
"partner": { "person": { "tradeName": "Second e.K." } },
|
||||
"billingContact": { "label": "second contact" },
|
||||
"vatId": null,
|
||||
"vatCountryCode": null,
|
||||
"vatBusiness": true,
|
||||
"refundBankAccount": { "holder": "Second e.K." }
|
||||
},
|
||||
{
|
||||
"debitorNumber": 1000313,
|
||||
"debitorNumberSuffix": 13,
|
||||
"partner": { "person": { "tradeName": "Third OHG" } },
|
||||
"billingContact": { "label": "third contact" },
|
||||
"vatId": null,
|
||||
"vatCountryCode": null,
|
||||
"vatBusiness": true,
|
||||
"refundBankAccount": { "holder": "Third OHG" }
|
||||
}
|
||||
]
|
||||
{
|
||||
"debitorRel": {
|
||||
"anchor": {
|
||||
"personType": "LEGAL_PERSON",
|
||||
"tradeName": "First GmbH",
|
||||
"givenName": null,
|
||||
"familyName": null
|
||||
},
|
||||
"holder": {
|
||||
"personType": "LEGAL_PERSON",
|
||||
"tradeName": "First GmbH",
|
||||
"givenName": null,
|
||||
"familyName": null
|
||||
},
|
||||
"type": "DEBITOR",
|
||||
"mark": null,
|
||||
"contact": {
|
||||
"label": "first contact",
|
||||
"emailAddresses": "contact-admin@firstcontact.example.com",
|
||||
"phoneNumbers": "+49 123 1234567"
|
||||
}
|
||||
},
|
||||
"debitorNumber": 1000111,
|
||||
"debitorNumberSuffix": 11,
|
||||
"partner": {
|
||||
"partnerNumber": 10001,
|
||||
"partnerRel": {
|
||||
"anchor": {
|
||||
"personType": "LEGAL_PERSON",
|
||||
"tradeName": "Hostsharing eG",
|
||||
"givenName": null,
|
||||
"familyName": null
|
||||
},
|
||||
"holder": {
|
||||
"personType": "LEGAL_PERSON",
|
||||
"tradeName": "First GmbH",
|
||||
"givenName": null,
|
||||
"familyName": null
|
||||
},
|
||||
"type": "PARTNER",
|
||||
"mark": null,
|
||||
"contact": {
|
||||
"label": "first contact",
|
||||
"emailAddresses": "contact-admin@firstcontact.example.com",
|
||||
"phoneNumbers": "+49 123 1234567"
|
||||
}
|
||||
},
|
||||
"details": {
|
||||
"registrationOffice": "Hamburg",
|
||||
"registrationNumber": "RegNo123456789",
|
||||
"birthName": null,
|
||||
"birthPlace": null,
|
||||
"birthday": null,
|
||||
"dateOfDeath": null
|
||||
}
|
||||
},
|
||||
"billable": true,
|
||||
"vatId": null,
|
||||
"vatCountryCode": null,
|
||||
"vatBusiness": true,
|
||||
"vatReverseCharge": false,
|
||||
"refundBankAccount": {
|
||||
"holder": "First GmbH",
|
||||
"iban": "DE02120300000000202051",
|
||||
"bic": "BYLADEM1001"
|
||||
},
|
||||
"defaultPrefix": "fir"
|
||||
},
|
||||
{
|
||||
"debitorRel": {
|
||||
"anchor": {"tradeName": "Second e.K."},
|
||||
"holder": {"tradeName": "Second e.K."},
|
||||
"type": "DEBITOR",
|
||||
"contact": {"emailAddresses": "contact-admin@secondcontact.example.com"}
|
||||
},
|
||||
"debitorNumber": 1000212,
|
||||
"debitorNumberSuffix": 12,
|
||||
"partner": {
|
||||
"partnerNumber": 10002,
|
||||
"partnerRel": {
|
||||
"anchor": {"tradeName": "Hostsharing eG"},
|
||||
"holder": {"tradeName": "Second e.K."},
|
||||
"type": "PARTNER",
|
||||
"contact": {"emailAddresses": "contact-admin@secondcontact.example.com"}
|
||||
},
|
||||
"details": {
|
||||
"registrationOffice": "Hamburg",
|
||||
"registrationNumber": "RegNo123456789"
|
||||
}
|
||||
},
|
||||
"billable": true,
|
||||
"vatId": null,
|
||||
"vatCountryCode": null,
|
||||
"vatBusiness": true,
|
||||
"vatReverseCharge": false,
|
||||
"refundBankAccount": {"iban": "DE02100500000054540402"},
|
||||
"defaultPrefix": "sec"
|
||||
},
|
||||
{
|
||||
"debitorRel": {
|
||||
"anchor": {"tradeName": "Third OHG"},
|
||||
"holder": {"tradeName": "Third OHG"},
|
||||
"type": "DEBITOR",
|
||||
"contact": {"emailAddresses": "contact-admin@thirdcontact.example.com"}
|
||||
},
|
||||
"debitorNumber": 1000313,
|
||||
"debitorNumberSuffix": 13,
|
||||
"partner": {
|
||||
"partnerNumber": 10003,
|
||||
"partnerRel": {
|
||||
"anchor": {"tradeName": "Hostsharing eG"},
|
||||
"holder": {"tradeName": "Third OHG"},
|
||||
"type": "PARTNER",
|
||||
"contact": {"emailAddresses": "contact-admin@thirdcontact.example.com"}
|
||||
},
|
||||
"details": {
|
||||
"registrationOffice": "Hamburg",
|
||||
"registrationNumber": "RegNo123456789"
|
||||
}
|
||||
},
|
||||
"billable": true,
|
||||
"vatId": null,
|
||||
"vatCountryCode": null,
|
||||
"vatBusiness": true,
|
||||
"vatReverseCharge": false,
|
||||
"refundBankAccount": {"iban": "DE02300209000106531065"},
|
||||
"defaultPrefix": "thi"
|
||||
}
|
||||
]
|
||||
"""));
|
||||
// @formatter:on
|
||||
}
|
||||
@ -132,8 +240,10 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
|
||||
[
|
||||
{
|
||||
"debitorNumber": 1000212,
|
||||
"partner": { "person": { "tradeName": "Second e.K." } },
|
||||
"billingContact": { "label": "second contact" },
|
||||
"partner": { "partnerNumber": 10002 },
|
||||
"debitorRel": {
|
||||
"contact": { "label": "second contact" }
|
||||
},
|
||||
"vatId": null,
|
||||
"vatCountryCode": null,
|
||||
"vatBusiness": true
|
||||
@ -154,6 +264,17 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
|
||||
final var givenPartner = partnerRepo.findPartnerByOptionalNameLike("Third").get(0);
|
||||
final var givenContact = contactRepo.findContactByOptionalLabelLike("fourth").get(0);
|
||||
final var givenBankAccount = bankAccountRepo.findByOptionalHolderLike("Fourth").get(0);
|
||||
final var givenBillingPerson = personRepo.findPersonByOptionalNameLike("Fourth").get(0);
|
||||
|
||||
final var givenDebitorRelUUid = jpaAttempt.transacted(() -> {
|
||||
context.define("superuser-alex@hostsharing.net");
|
||||
return relRepo.save(HsOfficeRelationEntity.builder()
|
||||
.type(DEBITOR)
|
||||
.anchor(givenPartner.getPartnerRel().getHolder())
|
||||
.holder(givenBillingPerson)
|
||||
.contact(givenContact)
|
||||
.build()).getUuid();
|
||||
}).assertSuccessful().returnedValue();
|
||||
|
||||
final var location = RestAssured // @formatter:off
|
||||
.given()
|
||||
@ -161,8 +282,7 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
|
||||
.contentType(ContentType.JSON)
|
||||
.body("""
|
||||
{
|
||||
"partnerUuid": "%s",
|
||||
"billingContactUuid": "%s",
|
||||
"debitorRelUuid": "%s",
|
||||
"debitorNumberSuffix": "%s",
|
||||
"billable": "true",
|
||||
"vatId": "VAT123456",
|
||||
@ -172,7 +292,7 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
|
||||
"refundBankAccountUuid": "%s",
|
||||
"defaultPrefix": "for"
|
||||
}
|
||||
""".formatted( givenPartner.getUuid(), givenContact.getUuid(), ++nextDebitorSuffix, givenBankAccount.getUuid()))
|
||||
""".formatted( givenDebitorRelUUid, ++nextDebitorSuffix, givenBankAccount.getUuid()))
|
||||
.port(port)
|
||||
.when()
|
||||
.post("http://localhost/api/hs/office/debitors")
|
||||
@ -182,8 +302,8 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
|
||||
.body("uuid", isUuidValid())
|
||||
.body("vatId", is("VAT123456"))
|
||||
.body("defaultPrefix", is("for"))
|
||||
.body("billingContact.label", is(givenContact.getLabel()))
|
||||
.body("partner.person.tradeName", is(givenPartner.getPerson().getTradeName()))
|
||||
.body("debitorRel.contact.label", is(givenContact.getLabel()))
|
||||
.body("debitorRel.holder.tradeName", is(givenBillingPerson.getTradeName()))
|
||||
.body("refundBankAccount.holder", is(givenBankAccount.getHolder()))
|
||||
.header("Location", startsWith("http://localhost"))
|
||||
.extract().header("Location"); // @formatter:on
|
||||
@ -206,15 +326,23 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
|
||||
.header("current-user", "superuser-alex@hostsharing.net")
|
||||
.contentType(ContentType.JSON)
|
||||
.body("""
|
||||
{
|
||||
"partnerUuid": "%s",
|
||||
"billingContactUuid": "%s",
|
||||
"debitorNumberSuffix": "%s",
|
||||
"defaultPrefix": "for",
|
||||
"billable": "true",
|
||||
"vatReverseCharge": "false"
|
||||
}
|
||||
""".formatted( givenPartner.getUuid(), givenContact.getUuid(), ++nextDebitorSuffix))
|
||||
{
|
||||
"debitorRel": {
|
||||
"type": "DEBITOR",
|
||||
"anchorUuid": "%s",
|
||||
"holderUuid": "%s",
|
||||
"contactUuid": "%s"
|
||||
},
|
||||
"debitorNumberSuffix": "%s",
|
||||
"defaultPrefix": "for",
|
||||
"billable": "true",
|
||||
"vatReverseCharge": "false"
|
||||
}
|
||||
""".formatted(
|
||||
givenPartner.getPartnerRel().getHolder().getUuid(),
|
||||
givenPartner.getPartnerRel().getHolder().getUuid(),
|
||||
givenContact.getUuid(),
|
||||
++nextDebitorSuffix))
|
||||
.port(port)
|
||||
.when()
|
||||
.post("http://localhost/api/hs/office/debitors")
|
||||
@ -222,8 +350,8 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
|
||||
.statusCode(201)
|
||||
.contentType(ContentType.JSON)
|
||||
.body("uuid", isUuidValid())
|
||||
.body("billingContact.label", is(givenContact.getLabel()))
|
||||
.body("partner.person.tradeName", is(givenPartner.getPerson().getTradeName()))
|
||||
.body("debitorRel.contact.label", is(givenContact.getLabel()))
|
||||
.body("partner.partnerRel.holder.tradeName", is(givenPartner.getPartnerRel().getHolder().getTradeName()))
|
||||
.body("vatId", equalTo(null))
|
||||
.body("vatCountryCode", equalTo(null))
|
||||
.body("vatBusiness", equalTo(false))
|
||||
@ -250,19 +378,22 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
|
||||
.header("current-user", "superuser-alex@hostsharing.net")
|
||||
.contentType(ContentType.JSON)
|
||||
.body("""
|
||||
{
|
||||
"partnerUuid": "%s",
|
||||
"billingContactUuid": "%s",
|
||||
"debitorNumberSuffix": "%s",
|
||||
"billable": "true",
|
||||
"vatId": "VAT123456",
|
||||
"vatCountryCode": "DE",
|
||||
"vatBusiness": true,
|
||||
"vatReverseCharge": "false",
|
||||
"defaultPrefix": "thi"
|
||||
}
|
||||
"""
|
||||
.formatted( givenPartner.getUuid(), givenContactUuid, ++nextDebitorSuffix))
|
||||
{
|
||||
"debitorRel": {
|
||||
"type": "DEBITOR",
|
||||
"anchorUuid": "%s",
|
||||
"holderUuid": "%s",
|
||||
"contactUuid": "%s"
|
||||
},
|
||||
"debitorNumberSuffix": "%s",
|
||||
"defaultPrefix": "for",
|
||||
"billable": "true",
|
||||
"vatReverseCharge": "false"
|
||||
}
|
||||
""".formatted(
|
||||
givenPartner.getPartnerRel().getAnchor().getUuid(),
|
||||
givenPartner.getPartnerRel().getAnchor().getUuid(),
|
||||
givenContactUuid, ++nextDebitorSuffix))
|
||||
.port(port)
|
||||
.when()
|
||||
.post("http://localhost/api/hs/office/debitors")
|
||||
@ -273,10 +404,10 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
|
||||
}
|
||||
|
||||
@Test
|
||||
void globalAdmin_canNotAddDebitor_ifPartnerDoesNotExist() {
|
||||
void globalAdmin_canNotAddDebitor_ifDebitorRelDoesNotExist() {
|
||||
|
||||
context.define("superuser-alex@hostsharing.net");
|
||||
final var givenPartnerUuid = UUID.fromString("00000000-0000-0000-0000-000000000000");
|
||||
final var givenDebitorRelUuid = UUID.fromString("00000000-0000-0000-0000-000000000000");
|
||||
final var givenContact = contactRepo.findContactByOptionalLabelLike("fourth").get(0);
|
||||
|
||||
final var location = RestAssured // @formatter:off
|
||||
@ -284,24 +415,20 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
|
||||
.header("current-user", "superuser-alex@hostsharing.net")
|
||||
.contentType(ContentType.JSON)
|
||||
.body("""
|
||||
{
|
||||
"partnerUuid": "%s",
|
||||
"billingContactUuid": "%s",
|
||||
"debitorNumberSuffix": "%s",
|
||||
"billable": "true",
|
||||
"vatId": "VAT123456",
|
||||
"vatCountryCode": "DE",
|
||||
"vatBusiness": true,
|
||||
"vatReverseCharge": "false",
|
||||
"defaultPrefix": "for"
|
||||
}
|
||||
""".formatted( givenPartnerUuid, givenContact.getUuid(), ++nextDebitorSuffix))
|
||||
{
|
||||
"debitorRelUuid": "%s",
|
||||
"debitorNumberSuffix": "%s",
|
||||
"defaultPrefix": "for",
|
||||
"billable": "true",
|
||||
"vatReverseCharge": "false"
|
||||
}
|
||||
""".formatted(givenDebitorRelUuid, ++nextDebitorSuffix))
|
||||
.port(port)
|
||||
.when()
|
||||
.post("http://localhost/api/hs/office/debitors")
|
||||
.then().log().all().assertThat()
|
||||
.statusCode(400)
|
||||
.body("message", is("Unable to find Partner with uuid 00000000-0000-0000-0000-000000000000"));
|
||||
.body("message", is("Unable to find HsOfficeRelationEntity with uuid 00000000-0000-0000-0000-000000000000"));
|
||||
// @formatter:on
|
||||
}
|
||||
}
|
||||
@ -321,14 +448,53 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
|
||||
.port(port)
|
||||
.when()
|
||||
.get("http://localhost/api/hs/office/debitors/" + givenDebitorUuid)
|
||||
.then().log().body().assertThat()
|
||||
.then().log().all().assertThat()
|
||||
.statusCode(200)
|
||||
.contentType("application/json")
|
||||
.body("", lenientlyEquals("""
|
||||
{
|
||||
"partner": { person: { "tradeName": "First GmbH" } },
|
||||
"billingContact": { "label": "first contact" }
|
||||
}
|
||||
"debitorRel": {
|
||||
"anchor": { "personType": "LEGAL_PERSON", "tradeName": "First GmbH"},
|
||||
"holder": { "personType": "LEGAL_PERSON", "tradeName": "First GmbH"},
|
||||
"type": "DEBITOR",
|
||||
"contact": {
|
||||
"label": "first contact",
|
||||
"postalAddress": "\\nVorname Nachname\\nStraße Hnr\\nPLZ Stadt\\n",
|
||||
"emailAddresses": "contact-admin@firstcontact.example.com",
|
||||
"phoneNumbers": "+49 123 1234567"
|
||||
}
|
||||
},
|
||||
"debitorNumber": 1000111,
|
||||
"debitorNumberSuffix": 11,
|
||||
"partner": {
|
||||
"partnerNumber": 10001,
|
||||
"partnerRel": {
|
||||
"anchor": { "personType": "LEGAL_PERSON", "tradeName": "Hostsharing eG"},
|
||||
"holder": { "personType": "LEGAL_PERSON", "tradeName": "First GmbH"},
|
||||
"type": "PARTNER",
|
||||
"mark": null,
|
||||
"contact": {
|
||||
"label": "first contact",
|
||||
"postalAddress": "\\nVorname Nachname\\nStraße Hnr\\nPLZ Stadt\\n",
|
||||
"emailAddresses": "contact-admin@firstcontact.example.com",
|
||||
"phoneNumbers": "+49 123 1234567"
|
||||
}
|
||||
},
|
||||
"details": {
|
||||
"registrationOffice": "Hamburg",
|
||||
"registrationNumber": "RegNo123456789"
|
||||
}
|
||||
},
|
||||
"billable": true,
|
||||
"vatBusiness": true,
|
||||
"vatReverseCharge": false,
|
||||
"refundBankAccount": {
|
||||
"holder": "First GmbH",
|
||||
"iban": "DE02120300000000202051",
|
||||
"bic": "BYLADEM1001"
|
||||
},
|
||||
"defaultPrefix": "fir"
|
||||
}
|
||||
""")); // @formatter:on
|
||||
}
|
||||
|
||||
@ -350,7 +516,7 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
|
||||
|
||||
@Test
|
||||
@Accepts({ "Debitor:X(Access Control)" })
|
||||
void contactAdminUser_canGetRelatedDebitor() {
|
||||
void contactAdminUser_canGetRelatedDebitorExceptRefundBankAccount() {
|
||||
context.define("superuser-alex@hostsharing.net");
|
||||
final var givenDebitorUuid = debitorRepo.findDebitorByOptionalNameLike("first contact").get(0).getUuid();
|
||||
|
||||
@ -365,9 +531,10 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
|
||||
.contentType("application/json")
|
||||
.body("", lenientlyEquals("""
|
||||
{
|
||||
"partner": { person: { "tradeName": "First GmbH" } },
|
||||
"billingContact": { "label": "first contact" },
|
||||
"refundBankAccount": { "holder": "First GmbH" }
|
||||
"debitorNumber": 1000111,
|
||||
"partner": { "partnerNumber": 10001 },
|
||||
"debitorRel": { "contact": { "label": "first contact" } },
|
||||
"refundBankAccount": null
|
||||
}
|
||||
""")); // @formatter:on
|
||||
}
|
||||
@ -378,7 +545,7 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
|
||||
class PatchDebitor {
|
||||
|
||||
@Test
|
||||
void globalAdmin_withoutAssumedRole_canPatchAllPropertiesOfArbitraryDebitor() {
|
||||
void globalAdmin_withoutAssumedRole_canPatchArbitraryDebitor() {
|
||||
|
||||
context.define("superuser-alex@hostsharing.net");
|
||||
final var givenDebitor = givenSomeTemporaryDebitor();
|
||||
@ -400,77 +567,90 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
|
||||
.port(port)
|
||||
.when()
|
||||
.patch("http://localhost/api/hs/office/debitors/" + givenDebitor.getUuid())
|
||||
.then().assertThat()
|
||||
.then().log().all().assertThat()
|
||||
.statusCode(200)
|
||||
.contentType(ContentType.JSON)
|
||||
.body("uuid", isUuidValid())
|
||||
.body("vatId", is("VAT222222"))
|
||||
.body("vatCountryCode", is("AA"))
|
||||
.body("vatBusiness", is(true))
|
||||
.body("defaultPrefix", is("for"))
|
||||
.body("billingContact.label", is(givenContact.getLabel()))
|
||||
.body("partner.person.tradeName", is(givenDebitor.getPartner().getPerson().getTradeName()));
|
||||
.body("", lenientlyEquals("""
|
||||
{
|
||||
"debitorRel": {
|
||||
"anchor": { "tradeName": "Fourth eG" },
|
||||
"holder": { "tradeName": "Fourth eG" },
|
||||
"type": "DEBITOR",
|
||||
"mark": null,
|
||||
"contact": { "label": "fourth contact" }
|
||||
},
|
||||
"debitorNumber": 10004${debitorNumberSuffix},
|
||||
"debitorNumberSuffix": ${debitorNumberSuffix},
|
||||
"partner": {
|
||||
"partnerNumber": 10004,
|
||||
"partnerRel": {
|
||||
"anchor": { "tradeName": "Hostsharing eG" },
|
||||
"holder": { "tradeName": "Fourth eG" },
|
||||
"type": "PARTNER",
|
||||
"mark": null,
|
||||
"contact": { "label": "fourth contact" }
|
||||
},
|
||||
"details": {
|
||||
"registrationOffice": "Hamburg",
|
||||
"registrationNumber": "RegNo123456789",
|
||||
"birthName": null,
|
||||
"birthPlace": null,
|
||||
"birthday": null,
|
||||
"dateOfDeath": null
|
||||
}
|
||||
},
|
||||
"billable": true,
|
||||
"vatId": "VAT222222",
|
||||
"vatCountryCode": "AA",
|
||||
"vatBusiness": true,
|
||||
"vatReverseCharge": false,
|
||||
"defaultPrefix": "for"
|
||||
}
|
||||
"""
|
||||
.replace("${debitorNumberSuffix}", givenDebitor.getDebitorNumberSuffix().toString()))
|
||||
);
|
||||
// @formatter:on
|
||||
|
||||
// finally, the debitor is actually updated
|
||||
context.define("superuser-alex@hostsharing.net");
|
||||
assertThat(debitorRepo.findByUuid(givenDebitor.getUuid())).isPresent().get()
|
||||
.matches(partner -> {
|
||||
assertThat(partner.getPartner().getPerson().getTradeName()).isEqualTo(givenDebitor.getPartner()
|
||||
.getPerson()
|
||||
.getTradeName());
|
||||
assertThat(partner.getBillingContact().getLabel()).isEqualTo("fourth contact");
|
||||
assertThat(partner.getVatId()).isEqualTo("VAT222222");
|
||||
assertThat(partner.getVatCountryCode()).isEqualTo("AA");
|
||||
assertThat(partner.isVatBusiness()).isEqualTo(true);
|
||||
.matches(debitor -> {
|
||||
assertThat(debitor.getDebitorRel().getHolder().getTradeName())
|
||||
.isEqualTo(givenDebitor.getDebitorRel().getHolder().getTradeName());
|
||||
assertThat(debitor.getDebitorRel().getContact().getLabel()).isEqualTo("fourth contact");
|
||||
assertThat(debitor.getVatId()).isEqualTo("VAT222222");
|
||||
assertThat(debitor.getVatCountryCode()).isEqualTo("AA");
|
||||
assertThat(debitor.isVatBusiness()).isEqualTo(true);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void globalAdmin_withoutAssumedRole_canPatchPartialPropertiesOfArbitraryDebitor() {
|
||||
void theContactOwner_canNotPatchARelatedDebitor() {
|
||||
|
||||
context.define("superuser-alex@hostsharing.net");
|
||||
final var givenDebitor = givenSomeTemporaryDebitor();
|
||||
final var newBillingContact = contactRepo.findContactByOptionalLabelLike("sixth").get(0);
|
||||
|
||||
final var location = RestAssured // @formatter:off
|
||||
.given()
|
||||
// @formatter:on
|
||||
RestAssured // @formatter:off
|
||||
.given()
|
||||
.header("current-user", "superuser-alex@hostsharing.net")
|
||||
.header("assumed-roles", "hs_office_contact#fourthcontact.admin")
|
||||
.contentType(ContentType.JSON)
|
||||
.body("""
|
||||
{
|
||||
"billingContactUuid": "%s",
|
||||
"vatId": "VAT999999"
|
||||
}
|
||||
""".formatted(newBillingContact.getUuid()))
|
||||
{
|
||||
"vatId": "VAT999999"
|
||||
}
|
||||
""")
|
||||
.port(port)
|
||||
.when()
|
||||
.when()
|
||||
.patch("http://localhost/api/hs/office/debitors/" + givenDebitor.getUuid())
|
||||
.then().assertThat()
|
||||
.statusCode(200)
|
||||
.contentType(ContentType.JSON)
|
||||
.body("uuid", isUuidValid())
|
||||
.body("billingContact.label", is("sixth contact"))
|
||||
.body("vatId", is("VAT999999"))
|
||||
.body("vatCountryCode", is(givenDebitor.getVatCountryCode()))
|
||||
.body("vatBusiness", is(givenDebitor.isVatBusiness()));
|
||||
// @formatter:on
|
||||
.then().log().all().assertThat()
|
||||
.statusCode(403)
|
||||
.body("message", containsString("ERROR: [403] Subject"))
|
||||
.body("message", containsString("is not allowed to update hs_office_debitor uuid "));
|
||||
|
||||
// finally, the debitor is actually updated
|
||||
assertThat(debitorRepo.findByUuid(givenDebitor.getUuid())).isPresent().get()
|
||||
.matches(partner -> {
|
||||
assertThat(partner.getPartner().getPerson().getTradeName()).isEqualTo(givenDebitor.getPartner()
|
||||
.getPerson()
|
||||
.getTradeName());
|
||||
assertThat(partner.getBillingContact().getLabel()).isEqualTo("sixth contact");
|
||||
assertThat(partner.getVatId()).isEqualTo("VAT999999");
|
||||
assertThat(partner.getVatCountryCode()).isEqualTo(givenDebitor.getVatCountryCode());
|
||||
assertThat(partner.isVatBusiness()).isEqualTo(givenDebitor.isVatBusiness());
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Nested
|
||||
@ -500,7 +680,7 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
|
||||
void contactAdminUser_canNotDeleteRelatedDebitor() {
|
||||
context.define("superuser-alex@hostsharing.net");
|
||||
final var givenDebitor = givenSomeTemporaryDebitor();
|
||||
assertThat(givenDebitor.getBillingContact().getLabel()).isEqualTo("fourth contact");
|
||||
assertThat(givenDebitor.getDebitorRel().getContact().getLabel()).isEqualTo("fourth contact");
|
||||
|
||||
RestAssured // @formatter:off
|
||||
.given()
|
||||
@ -520,7 +700,7 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
|
||||
void normalUser_canNotDeleteUnrelatedDebitor() {
|
||||
context.define("superuser-alex@hostsharing.net");
|
||||
final var givenDebitor = givenSomeTemporaryDebitor();
|
||||
assertThat(givenDebitor.getBillingContact().getLabel()).isEqualTo("fourth contact");
|
||||
assertThat(givenDebitor.getDebitorRel().getContact().getLabel()).isEqualTo("fourth contact");
|
||||
|
||||
RestAssured // @formatter:off
|
||||
.given()
|
||||
@ -544,8 +724,14 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
|
||||
final var newDebitor = HsOfficeDebitorEntity.builder()
|
||||
.debitorNumberSuffix(++nextDebitorSuffix)
|
||||
.billable(true)
|
||||
.partner(givenPartner)
|
||||
.billingContact(givenContact)
|
||||
.debitorRel(
|
||||
HsOfficeRelationEntity.builder()
|
||||
.type(DEBITOR)
|
||||
.anchor(givenPartner.getPartnerRel().getHolder())
|
||||
.holder(givenPartner.getPartnerRel().getHolder())
|
||||
.contact(givenContact)
|
||||
.build()
|
||||
)
|
||||
.defaultPrefix("abc")
|
||||
.vatReverseCharge(false)
|
||||
.build();
|
||||
|
@ -1,9 +1,8 @@
|
||||
package net.hostsharing.hsadminng.hs.office.debitor;
|
||||
|
||||
import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity;
|
||||
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
|
||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeDebitorPatchResource;
|
||||
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity;
|
||||
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
|
||||
import net.hostsharing.test.PatchUnitTestBase;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.TestInstance;
|
||||
@ -28,9 +27,8 @@ class HsOfficeDebitorEntityPatcherUnitTest extends PatchUnitTestBase<
|
||||
> {
|
||||
|
||||
private static final UUID INITIAL_DEBITOR_UUID = UUID.randomUUID();
|
||||
private static final UUID INITIAL_PARTNER_UUID = UUID.randomUUID();
|
||||
private static final UUID INITIAL_CONTACT_UUID = UUID.randomUUID();
|
||||
private static final UUID PATCHED_CONTACT_UUID = UUID.randomUUID();
|
||||
private static final UUID INITIAL_DEBITOR_REL_UUID = UUID.randomUUID();
|
||||
private static final UUID PATCHED_DEBITOR_REL_UUID = UUID.randomUUID();
|
||||
|
||||
private static final String PATCHED_DEFAULT_PREFIX = "xyz";
|
||||
private static final String PATCHED_VAT_COUNTRY_CODE = "ZZ";
|
||||
@ -46,12 +44,8 @@ class HsOfficeDebitorEntityPatcherUnitTest extends PatchUnitTestBase<
|
||||
private static final UUID INITIAL_REFUND_BANK_ACCOUNT_UUID = UUID.randomUUID();
|
||||
private static final UUID PATCHED_REFUND_BANK_ACCOUNT_UUID = UUID.randomUUID();
|
||||
|
||||
private final HsOfficePartnerEntity givenInitialPartner = HsOfficePartnerEntity.builder()
|
||||
.uuid(INITIAL_PARTNER_UUID)
|
||||
.build();
|
||||
|
||||
private final HsOfficeContactEntity givenInitialContact = HsOfficeContactEntity.builder()
|
||||
.uuid(INITIAL_CONTACT_UUID)
|
||||
private final HsOfficeRelationEntity givenInitialDebitorRel = HsOfficeRelationEntity.builder()
|
||||
.uuid(INITIAL_DEBITOR_REL_UUID)
|
||||
.build();
|
||||
|
||||
private final HsOfficeBankAccountEntity givenInitialBankAccount = HsOfficeBankAccountEntity.builder()
|
||||
@ -62,8 +56,8 @@ class HsOfficeDebitorEntityPatcherUnitTest extends PatchUnitTestBase<
|
||||
|
||||
@BeforeEach
|
||||
void initMocks() {
|
||||
lenient().when(em.getReference(eq(HsOfficeContactEntity.class), any())).thenAnswer(invocation ->
|
||||
HsOfficeContactEntity.builder().uuid(invocation.getArgument(1)).build());
|
||||
lenient().when(em.getReference(eq(HsOfficeRelationEntity.class), any())).thenAnswer(invocation ->
|
||||
HsOfficeRelationEntity.builder().uuid(invocation.getArgument(1)).build());
|
||||
lenient().when(em.getReference(eq(HsOfficeBankAccountEntity.class), any())).thenAnswer(invocation ->
|
||||
HsOfficeBankAccountEntity.builder().uuid(invocation.getArgument(1)).build());
|
||||
}
|
||||
@ -72,8 +66,7 @@ class HsOfficeDebitorEntityPatcherUnitTest extends PatchUnitTestBase<
|
||||
protected HsOfficeDebitorEntity newInitialEntity() {
|
||||
final var entity = new HsOfficeDebitorEntity();
|
||||
entity.setUuid(INITIAL_DEBITOR_UUID);
|
||||
entity.setPartner(givenInitialPartner);
|
||||
entity.setBillingContact(givenInitialContact);
|
||||
entity.setDebitorRel(givenInitialDebitorRel);
|
||||
entity.setBillable(INITIAL_BILLABLE);
|
||||
entity.setVatId("initial VAT-ID");
|
||||
entity.setVatCountryCode("AA");
|
||||
@ -98,11 +91,11 @@ class HsOfficeDebitorEntityPatcherUnitTest extends PatchUnitTestBase<
|
||||
protected Stream<Property> propertyTestDescriptors() {
|
||||
return Stream.of(
|
||||
new JsonNullableProperty<>(
|
||||
"billingContact",
|
||||
HsOfficeDebitorPatchResource::setBillingContactUuid,
|
||||
PATCHED_CONTACT_UUID,
|
||||
HsOfficeDebitorEntity::setBillingContact,
|
||||
newBillingContact(PATCHED_CONTACT_UUID))
|
||||
"debitorRel",
|
||||
HsOfficeDebitorPatchResource::setDebitorRelUuid,
|
||||
PATCHED_DEBITOR_REL_UUID,
|
||||
HsOfficeDebitorEntity::setDebitorRel,
|
||||
newDebitorRel(PATCHED_DEBITOR_REL_UUID))
|
||||
.notNullable(),
|
||||
new SimpleProperty<>(
|
||||
"billable",
|
||||
@ -129,7 +122,7 @@ class HsOfficeDebitorEntityPatcherUnitTest extends PatchUnitTestBase<
|
||||
new SimpleProperty<>(
|
||||
"vatReverseCharge",
|
||||
HsOfficeDebitorPatchResource::setVatReverseCharge,
|
||||
PATCHED_BILLABLE,
|
||||
PATCHED_VAT_REVERSE_CHARGE,
|
||||
HsOfficeDebitorEntity::setVatReverseCharge)
|
||||
.notNullable(),
|
||||
new JsonNullableProperty<>(
|
||||
@ -148,15 +141,15 @@ class HsOfficeDebitorEntityPatcherUnitTest extends PatchUnitTestBase<
|
||||
);
|
||||
}
|
||||
|
||||
private HsOfficeContactEntity newBillingContact(final UUID uuid) {
|
||||
final var newContact = new HsOfficeContactEntity();
|
||||
newContact.setUuid(uuid);
|
||||
return newContact;
|
||||
private HsOfficeRelationEntity newDebitorRel(final UUID uuid) {
|
||||
return HsOfficeRelationEntity.builder()
|
||||
.uuid(uuid)
|
||||
.build();
|
||||
}
|
||||
|
||||
private HsOfficeBankAccountEntity newBankAccount(final UUID uuid) {
|
||||
final var newBankAccount = new HsOfficeBankAccountEntity();
|
||||
newBankAccount.setUuid(uuid);
|
||||
return newBankAccount;
|
||||
return HsOfficeBankAccountEntity.builder()
|
||||
.uuid(uuid)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
@ -1,61 +1,52 @@
|
||||
package net.hostsharing.hsadminng.hs.office.debitor;
|
||||
|
||||
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
|
||||
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerDetailsEntity;
|
||||
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity;
|
||||
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
|
||||
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonType;
|
||||
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class HsOfficeDebitorEntityUnitTest {
|
||||
|
||||
private HsOfficeRelationEntity givenDebitorRel = HsOfficeRelationEntity.builder()
|
||||
.anchor(HsOfficePersonEntity.builder()
|
||||
.personType(HsOfficePersonType.LEGAL_PERSON)
|
||||
.tradeName("some partner trade name")
|
||||
.build())
|
||||
.holder(HsOfficePersonEntity.builder()
|
||||
.personType(HsOfficePersonType.LEGAL_PERSON)
|
||||
.tradeName("some billing trade name")
|
||||
.build())
|
||||
.contact(HsOfficeContactEntity.builder().label("some label").build())
|
||||
.build();
|
||||
|
||||
@Test
|
||||
void toStringContainsPartnerAndContact() {
|
||||
final var given = HsOfficeDebitorEntity.builder()
|
||||
.debitorNumberSuffix((byte)67)
|
||||
.partner(HsOfficePartnerEntity.builder()
|
||||
.person(HsOfficePersonEntity.builder()
|
||||
.personType(HsOfficePersonType.LEGAL_PERSON)
|
||||
.tradeName("some trade name")
|
||||
.build())
|
||||
.details(HsOfficePartnerDetailsEntity.builder().birthName("some birth name").build())
|
||||
.partnerNumber(12345)
|
||||
.build())
|
||||
.billingContact(HsOfficeContactEntity.builder().label("some label").build())
|
||||
.debitorRel(givenDebitorRel)
|
||||
.defaultPrefix("som")
|
||||
.build();
|
||||
|
||||
final var result = given.toString();
|
||||
|
||||
assertThat(result).isEqualTo("debitor(D-1234567: LP some trade name: som)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void toStringWithoutPersonContainsDebitorNumber() {
|
||||
final var given = HsOfficeDebitorEntity.builder()
|
||||
.debitorNumberSuffix((byte)67)
|
||||
.partner(HsOfficePartnerEntity.builder()
|
||||
.person(null)
|
||||
.details(HsOfficePartnerDetailsEntity.builder().birthName("some birth name").build())
|
||||
.partnerNumber(12345)
|
||||
.build())
|
||||
.billingContact(HsOfficeContactEntity.builder().label("some label").build())
|
||||
.build();
|
||||
|
||||
final var result = given.toString();
|
||||
|
||||
assertThat(result).isEqualTo("debitor(D-1234567: <person=null>)");
|
||||
assertThat(result).isEqualTo("debitor(D-1234567: rel(anchor='LP some partner trade name', holder='LP some billing trade name'), som)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void toShortStringContainsDebitorNumber() {
|
||||
final var given = HsOfficeDebitorEntity.builder()
|
||||
.debitorRel(givenDebitorRel)
|
||||
.debitorNumberSuffix((byte)67)
|
||||
.partner(HsOfficePartnerEntity.builder()
|
||||
.partnerNumber(12345)
|
||||
.build())
|
||||
.debitorNumberSuffix((byte)67)
|
||||
.build();
|
||||
|
||||
final var result = given.toShortString();
|
||||
@ -66,10 +57,11 @@ class HsOfficeDebitorEntityUnitTest {
|
||||
@Test
|
||||
void getDebitorNumberWithPartnerNumberAndDebitorNumberSuffix() {
|
||||
final var given = HsOfficeDebitorEntity.builder()
|
||||
.debitorRel(givenDebitorRel)
|
||||
.debitorNumberSuffix((byte)67)
|
||||
.partner(HsOfficePartnerEntity.builder()
|
||||
.partnerNumber(12345)
|
||||
.build())
|
||||
.debitorNumberSuffix((byte)67)
|
||||
.build();
|
||||
|
||||
final var result = given.getDebitorNumber();
|
||||
@ -80,8 +72,9 @@ class HsOfficeDebitorEntityUnitTest {
|
||||
@Test
|
||||
void getDebitorNumberWithoutPartnerReturnsNull() {
|
||||
final var given = HsOfficeDebitorEntity.builder()
|
||||
.partner(null)
|
||||
.debitorRel(givenDebitorRel)
|
||||
.debitorNumberSuffix((byte)67)
|
||||
.partner(null)
|
||||
.build();
|
||||
|
||||
final var result = given.getDebitorNumber();
|
||||
@ -92,10 +85,9 @@ class HsOfficeDebitorEntityUnitTest {
|
||||
@Test
|
||||
void getDebitorNumberWithoutPartnerNumberReturnsNull() {
|
||||
final var given = HsOfficeDebitorEntity.builder()
|
||||
.partner(HsOfficePartnerEntity.builder()
|
||||
.partnerNumber(null)
|
||||
.build())
|
||||
.debitorRel(givenDebitorRel)
|
||||
.debitorNumberSuffix((byte)67)
|
||||
.partner(HsOfficePartnerEntity.builder().build())
|
||||
.build();
|
||||
|
||||
final var result = given.getDebitorNumber();
|
||||
@ -106,10 +98,11 @@ class HsOfficeDebitorEntityUnitTest {
|
||||
@Test
|
||||
void getDebitorNumberWithoutDebitorNumberSuffixReturnsNull() {
|
||||
final var given = HsOfficeDebitorEntity.builder()
|
||||
.debitorRel(givenDebitorRel)
|
||||
.debitorNumberSuffix(null)
|
||||
.partner(HsOfficePartnerEntity.builder()
|
||||
.partnerNumber(12345)
|
||||
.build())
|
||||
.debitorNumberSuffix(null)
|
||||
.build();
|
||||
|
||||
final var result = given.getDebitorNumber();
|
||||
|
@ -4,11 +4,16 @@ import net.hostsharing.hsadminng.context.Context;
|
||||
import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountRepository;
|
||||
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRepository;
|
||||
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerRepository;
|
||||
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonRepository;
|
||||
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
|
||||
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType;
|
||||
import net.hostsharing.hsadminng.hs.office.test.ContextBasedTestWithCleanup;
|
||||
import net.hostsharing.hsadminng.rbac.rbacgrant.RawRbacGrantRepository;
|
||||
import net.hostsharing.hsadminng.rbac.rbacgrant.RbacGrantsDiagramService;
|
||||
import net.hostsharing.hsadminng.rbac.rbacrole.RawRbacRoleRepository;
|
||||
import net.hostsharing.test.Array;
|
||||
import net.hostsharing.test.JpaAttempt;
|
||||
import org.hibernate.Hibernate;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@ -18,6 +23,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.orm.jpa.JpaObjectRetrievalFailureException;
|
||||
import org.springframework.orm.jpa.JpaSystemException;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@ -27,13 +33,14 @@ import jakarta.servlet.http.HttpServletRequest;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static net.hostsharing.hsadminng.hs.office.test.EntityList.one;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacgrant.RawRbacGrantEntity.distinctGrantDisplaysOf;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacrole.RawRbacRoleEntity.distinctRoleNamesOf;
|
||||
import static net.hostsharing.test.JpaAttempt.attempt;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@DataJpaTest
|
||||
@Import( { Context.class, JpaAttempt.class })
|
||||
@Import( { Context.class, JpaAttempt.class, RbacGrantsDiagramService.class })
|
||||
class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithCleanup {
|
||||
|
||||
@Autowired
|
||||
@ -45,6 +52,9 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
|
||||
@Autowired
|
||||
HsOfficeContactRepository contactRepo;
|
||||
|
||||
@Autowired
|
||||
HsOfficePersonRepository personRepo;
|
||||
|
||||
@Autowired
|
||||
HsOfficeBankAccountRepository bankAccountRepo;
|
||||
|
||||
@ -60,9 +70,11 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
|
||||
@Autowired
|
||||
JpaAttempt jpaAttempt;
|
||||
|
||||
@Autowired
|
||||
RbacGrantsDiagramService mermaidService;
|
||||
|
||||
@MockBean
|
||||
HttpServletRequest request;
|
||||
|
||||
@Nested
|
||||
class CreateDebitor {
|
||||
|
||||
@ -71,15 +83,19 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
|
||||
// given
|
||||
context("superuser-alex@hostsharing.net");
|
||||
final var count = debitorRepo.count();
|
||||
final var givenPartner = partnerRepo.findPartnerByOptionalNameLike("First GmbH").get(0);
|
||||
final var givenContact = contactRepo.findContactByOptionalLabelLike("first contact").get(0);
|
||||
final var givenPartnerPerson = one(personRepo.findPersonByOptionalNameLike("First GmbH"));
|
||||
final var givenContact = one(contactRepo.findContactByOptionalLabelLike("first contact"));
|
||||
|
||||
// when
|
||||
final var result = attempt(em, () -> {
|
||||
final var newDebitor = HsOfficeDebitorEntity.builder()
|
||||
.debitorNumberSuffix((byte)21)
|
||||
.partner(givenPartner)
|
||||
.billingContact(givenContact)
|
||||
.debitorRel(HsOfficeRelationEntity.builder()
|
||||
.type(HsOfficeRelationType.DEBITOR)
|
||||
.anchor(givenPartnerPerson)
|
||||
.holder(givenPartnerPerson)
|
||||
.contact(givenContact)
|
||||
.build())
|
||||
.defaultPrefix("abc")
|
||||
.billable(false)
|
||||
.build();
|
||||
@ -99,16 +115,19 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
|
||||
public void canNotCreateNewDebitorWithInvalidDefaultPrefix(final String givenPrefix) {
|
||||
// given
|
||||
context("superuser-alex@hostsharing.net");
|
||||
final var count = debitorRepo.count();
|
||||
final var givenPartner = partnerRepo.findPartnerByOptionalNameLike("First GmbH").get(0);
|
||||
final var givenContact = contactRepo.findContactByOptionalLabelLike("first contact").get(0);
|
||||
final var givenPartnerPerson = one(personRepo.findPersonByOptionalNameLike("First GmbH"));
|
||||
final var givenContact = one(contactRepo.findContactByOptionalLabelLike("first contact"));
|
||||
|
||||
// when
|
||||
final var result = attempt(em, () -> {
|
||||
final var newDebitor = HsOfficeDebitorEntity.builder()
|
||||
.debitorNumberSuffix((byte)21)
|
||||
.partner(givenPartner)
|
||||
.billingContact(givenContact)
|
||||
.debitorRel(HsOfficeRelationEntity.builder()
|
||||
.type(HsOfficeRelationType.DEBITOR)
|
||||
.anchor(givenPartnerPerson)
|
||||
.holder(givenPartnerPerson)
|
||||
.contact(givenContact)
|
||||
.build())
|
||||
.billable(true)
|
||||
.vatReverseCharge(false)
|
||||
.vatBusiness(false)
|
||||
@ -128,21 +147,22 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
|
||||
final var initialRoleNames = distinctRoleNamesOf(rawRoleRepo.findAll());
|
||||
final var initialGrantNames = distinctGrantDisplaysOf(rawGrantRepo.findAll()).stream()
|
||||
// some search+replace to make the output fit into the screen width
|
||||
.map(s -> s.replace("superuser-alex@hostsharing.net", "superuser-alex"))
|
||||
.map(s -> s.replace("22FourtheG-fourthcontact", "FeG"))
|
||||
.map(s -> s.replace("FourtheG-fourthcontact", "FeG"))
|
||||
.map(s -> s.replace("fourthcontact", "4th"))
|
||||
.map(s -> s.replace("hs_office_", ""))
|
||||
.toList();
|
||||
|
||||
// when
|
||||
attempt(em, () -> {
|
||||
final var givenPartner = partnerRepo.findPartnerByOptionalNameLike("Fourth").get(0);
|
||||
final var givenContact = contactRepo.findContactByOptionalLabelLike("fourth contact").get(0);
|
||||
final var givenPartnerPerson = one(personRepo.findPersonByOptionalNameLike("First GmbH"));
|
||||
final var givenDebitorPerson = one(personRepo.findPersonByOptionalNameLike("Fourth eG"));
|
||||
final var givenContact = one(contactRepo.findContactByOptionalLabelLike("fourth contact"));
|
||||
final var newDebitor = HsOfficeDebitorEntity.builder()
|
||||
.debitorNumberSuffix((byte)22)
|
||||
.partner(givenPartner)
|
||||
.billingContact(givenContact)
|
||||
.debitorRel(HsOfficeRelationEntity.builder()
|
||||
.type(HsOfficeRelationType.DEBITOR)
|
||||
.anchor(givenPartnerPerson)
|
||||
.holder(givenDebitorPerson)
|
||||
.contact(givenContact)
|
||||
.build())
|
||||
.defaultPrefix("abc")
|
||||
.billable(false)
|
||||
.build();
|
||||
@ -152,49 +172,52 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
|
||||
// then
|
||||
assertThat(distinctRoleNamesOf(rawRoleRepo.findAll())).containsExactlyInAnyOrder(Array.from(
|
||||
initialRoleNames,
|
||||
"hs_office_debitor#1000422:FourtheG-fourthcontact.owner",
|
||||
"hs_office_debitor#1000422:FourtheG-fourthcontact.admin",
|
||||
"hs_office_debitor#1000422:FourtheG-fourthcontact.agent",
|
||||
"hs_office_debitor#1000422:FourtheG-fourthcontact.tenant",
|
||||
"hs_office_debitor#1000422:FourtheG-fourthcontact.guest"));
|
||||
"hs_office_relation#FirstGmbH-with-DEBITOR-FourtheG.owner",
|
||||
"hs_office_relation#FirstGmbH-with-DEBITOR-FourtheG.admin",
|
||||
"hs_office_relation#FirstGmbH-with-DEBITOR-FourtheG.agent",
|
||||
"hs_office_relation#FirstGmbH-with-DEBITOR-FourtheG.tenant"));
|
||||
assertThat(distinctGrantDisplaysOf(rawGrantRepo.findAll()))
|
||||
.map(s -> s.replace("superuser-alex@hostsharing.net", "superuser-alex"))
|
||||
.map(s -> s.replace("22FourtheG-fourthcontact", "FeG"))
|
||||
.map(s -> s.replace("FourtheG-fourthcontact", "FeG"))
|
||||
.map(s -> s.replace("fourthcontact", "4th"))
|
||||
.map(s -> s.replace("hs_office_", ""))
|
||||
.containsExactlyInAnyOrder(Array.fromFormatted(
|
||||
initialGrantNames,
|
||||
// owner
|
||||
"{ grant perm DELETE on debitor#1000422:FeG to role debitor#1000422:FeG.owner by system and assume }",
|
||||
"{ grant role debitor#1000422:FeG.owner to role global#global.admin by system and assume }",
|
||||
"{ grant role debitor#1000422:FeG.owner to user superuser-alex by global#global.admin and assume }",
|
||||
.map(s -> s.replace("hs_office_", ""))
|
||||
.containsExactlyInAnyOrder(Array.fromFormatted(
|
||||
initialGrantNames,
|
||||
"{ grant perm INSERT into sepamandate with relation#FirstGmbH-with-DEBITOR-FourtheG to role relation#FirstGmbH-with-DEBITOR-FourtheG.admin by system and assume }",
|
||||
|
||||
// admin
|
||||
"{ grant perm UPDATE on debitor#1000422:FeG to role debitor#1000422:FeG.admin by system and assume }",
|
||||
"{ grant role debitor#1000422:FeG.admin to role debitor#1000422:FeG.owner by system and assume }",
|
||||
// owner
|
||||
"{ grant perm DELETE on debitor#D-1000122 to role relation#FirstGmbH-with-DEBITOR-FourtheG.owner by system and assume }",
|
||||
"{ grant perm DELETE on relation#FirstGmbH-with-DEBITOR-FourtheG to role relation#FirstGmbH-with-DEBITOR-FourtheG.owner by system and assume }",
|
||||
"{ grant role relation#FirstGmbH-with-DEBITOR-FourtheG.owner to role global#global.admin by system and assume }",
|
||||
"{ grant role relation#FirstGmbH-with-DEBITOR-FourtheG.owner to user superuser-alex@hostsharing.net by relation#FirstGmbH-with-DEBITOR-FourtheG.owner and assume }",
|
||||
|
||||
// agent
|
||||
"{ grant role debitor#1000422:FeG.agent to role debitor#1000422:FeG.admin by system and assume }",
|
||||
"{ grant role debitor#1000422:FeG.agent to role contact#4th.admin by system and assume }",
|
||||
"{ grant role debitor#1000422:FeG.agent to role partner#10004:FeG.admin by system and assume }",
|
||||
// admin
|
||||
"{ grant perm UPDATE on debitor#D-1000122 to role relation#FirstGmbH-with-DEBITOR-FourtheG.admin by system and assume }",
|
||||
"{ grant perm UPDATE on relation#FirstGmbH-with-DEBITOR-FourtheG to role relation#FirstGmbH-with-DEBITOR-FourtheG.admin by system and assume }",
|
||||
"{ grant role relation#FirstGmbH-with-DEBITOR-FourtheG.admin to role relation#FirstGmbH-with-DEBITOR-FourtheG.owner by system and assume }",
|
||||
"{ grant role relation#FirstGmbH-with-DEBITOR-FourtheG.admin to role person#FirstGmbH.admin by system and assume }",
|
||||
"{ grant role relation#FirstGmbH-with-DEBITOR-FourtheG.admin to role relation#HostsharingeG-with-PARTNER-FirstGmbH.admin by system and assume }",
|
||||
|
||||
// tenant
|
||||
"{ grant role contact#4th.guest to role debitor#1000422:FeG.tenant by system and assume }",
|
||||
"{ grant role debitor#1000422:FeG.tenant to role debitor#1000422:FeG.agent by system and assume }",
|
||||
"{ grant role debitor#1000422:FeG.tenant to role partner#10004:FeG.agent by system and assume }",
|
||||
"{ grant role partner#10004:FeG.tenant to role debitor#1000422:FeG.tenant by system and assume }",
|
||||
// agent
|
||||
"{ grant role relation#FirstGmbH-with-DEBITOR-FourtheG.agent to role person#FourtheG.admin by system and assume }",
|
||||
"{ grant role relation#FirstGmbH-with-DEBITOR-FourtheG.agent to role relation#FirstGmbH-with-DEBITOR-FourtheG.admin by system and assume }",
|
||||
"{ grant role relation#FirstGmbH-with-DEBITOR-FourtheG.agent to role relation#HostsharingeG-with-PARTNER-FirstGmbH.agent by system and assume }",
|
||||
|
||||
// guest
|
||||
"{ grant perm SELECT on debitor#1000422:FeG to role debitor#1000422:FeG.guest by system and assume }",
|
||||
"{ grant role debitor#1000422:FeG.guest to role debitor#1000422:FeG.tenant by system and assume }",
|
||||
// tenant
|
||||
"{ grant perm SELECT on debitor#D-1000122 to role relation#FirstGmbH-with-DEBITOR-FourtheG.tenant by system and assume }",
|
||||
"{ grant perm SELECT on relation#FirstGmbH-with-DEBITOR-FourtheG to role relation#FirstGmbH-with-DEBITOR-FourtheG.tenant by system and assume }",
|
||||
"{ grant role relation#HostsharingeG-with-PARTNER-FirstGmbH.tenant to role relation#FirstGmbH-with-DEBITOR-FourtheG.agent by system and assume }",
|
||||
"{ grant role contact#fourthcontact.referrer to role relation#FirstGmbH-with-DEBITOR-FourtheG.tenant by system and assume }",
|
||||
"{ grant role person#FirstGmbH.referrer to role relation#FirstGmbH-with-DEBITOR-FourtheG.tenant by system and assume }",
|
||||
"{ grant role person#FourtheG.referrer to role relation#FirstGmbH-with-DEBITOR-FourtheG.tenant by system and assume }",
|
||||
"{ grant role relation#FirstGmbH-with-DEBITOR-FourtheG.tenant to role contact#fourthcontact.admin by system and assume }",
|
||||
"{ grant role relation#FirstGmbH-with-DEBITOR-FourtheG.tenant to role person#FourtheG.admin by system and assume }",
|
||||
"{ grant role relation#FirstGmbH-with-DEBITOR-FourtheG.tenant to role relation#FirstGmbH-with-DEBITOR-FourtheG.agent by system and assume }",
|
||||
|
||||
null));
|
||||
null));
|
||||
}
|
||||
|
||||
private void assertThatDebitorIsPersisted(final HsOfficeDebitorEntity saved) {
|
||||
final var savedRefreshed = refresh(saved);
|
||||
final var found = debitorRepo.findByUuid(saved.getUuid());
|
||||
assertThat(found).isNotEmpty().get().usingRecursiveComparison().isEqualTo(saved);
|
||||
assertThat(found).isNotEmpty().get().usingRecursiveComparison().isEqualTo(savedRefreshed);
|
||||
}
|
||||
}
|
||||
|
||||
@ -212,9 +235,9 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
|
||||
// then
|
||||
allTheseDebitorsAreReturned(
|
||||
result,
|
||||
"debitor(D-1000111: LP First GmbH: fir)",
|
||||
"debitor(D-1000212: LP Second e.K.: sec)",
|
||||
"debitor(D-1000313: IF Third OHG: thi)");
|
||||
"debitor(D-1000111: rel(anchor='LP First GmbH', type='DEBITOR', holder='LP First GmbH'), fir)",
|
||||
"debitor(D-1000212: rel(anchor='LP Second e.K.', type='DEBITOR', holder='LP Second e.K.'), sec)",
|
||||
"debitor(D-1000313: rel(anchor='IF Third OHG', type='DEBITOR', holder='IF Third OHG'), thi)");
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ -233,8 +256,8 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
|
||||
|
||||
// then:
|
||||
exactlyTheseDebitorsAreReturned(result,
|
||||
"debitor(D-1000111: LP First GmbH: fir)",
|
||||
"debitor(D-1000120: LP First GmbH: fif)");
|
||||
"debitor(D-1000111: P-10001, fir)",
|
||||
"debitor(D-1000120: P-10001, fif)");
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -262,7 +285,8 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
|
||||
final var result = debitorRepo.findDebitorByDebitorNumber(1000313);
|
||||
|
||||
// then
|
||||
exactlyTheseDebitorsAreReturned(result, "debitor(D-1000313: IF Third OHG: thi)");
|
||||
exactlyTheseDebitorsAreReturned(result,
|
||||
"debitor(D-1000313: rel(anchor='IF Third OHG', type='DEBITOR', holder='IF Third OHG'), thi)");
|
||||
}
|
||||
}
|
||||
|
||||
@ -278,7 +302,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
|
||||
final var result = debitorRepo.findDebitorByOptionalNameLike("third contact");
|
||||
|
||||
// then
|
||||
exactlyTheseDebitorsAreReturned(result, "debitor(D-1000313: IF Third OHG: thi)");
|
||||
exactlyTheseDebitorsAreReturned(result, "debitor(D-1000313: rel(anchor='IF Third OHG', type='DEBITOR', holder='IF Third OHG'), thi)");
|
||||
}
|
||||
}
|
||||
|
||||
@ -290,13 +314,14 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
|
||||
// given
|
||||
context("superuser-alex@hostsharing.net");
|
||||
final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "fifth contact", "Fourth", "fif");
|
||||
|
||||
assertThatDebitorIsVisibleForUserWithRole(
|
||||
givenDebitor,
|
||||
"hs_office_partner#10004:FourtheG-fourthcontact.admin");
|
||||
assertThatDebitorActuallyInDatabase(givenDebitor);
|
||||
final var givenNewPartner = partnerRepo.findPartnerByOptionalNameLike("First").get(0);
|
||||
final var givenNewContact = contactRepo.findContactByOptionalLabelLike("sixth contact").get(0);
|
||||
final var givenNewBankAccount = bankAccountRepo.findByOptionalHolderLike("first").get(0);
|
||||
"hs_office_relation#FourtheG-with-DEBITOR-FourtheG.admin", true);
|
||||
final var givenNewPartnerPerson = one(personRepo.findPersonByOptionalNameLike("First"));
|
||||
final var givenNewBillingPerson = one(personRepo.findPersonByOptionalNameLike("Firby"));
|
||||
final var givenNewContact = one(contactRepo.findContactByOptionalLabelLike("sixth contact"));
|
||||
final var givenNewBankAccount = one(bankAccountRepo.findByOptionalHolderLike("first"));
|
||||
final String givenNewVatId = "NEW-VAT-ID";
|
||||
final String givenNewVatCountryCode = "NC";
|
||||
final boolean givenNewVatBusiness = !givenDebitor.isVatBusiness();
|
||||
@ -304,8 +329,12 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
|
||||
// when
|
||||
final var result = jpaAttempt.transacted(() -> {
|
||||
context("superuser-alex@hostsharing.net");
|
||||
givenDebitor.setPartner(givenNewPartner);
|
||||
givenDebitor.setBillingContact(givenNewContact);
|
||||
givenDebitor.setDebitorRel(HsOfficeRelationEntity.builder()
|
||||
.type(HsOfficeRelationType.DEBITOR)
|
||||
.anchor(givenNewPartnerPerson)
|
||||
.holder(givenNewBillingPerson)
|
||||
.contact(givenNewContact)
|
||||
.build());
|
||||
givenDebitor.setRefundBankAccount(givenNewBankAccount);
|
||||
givenDebitor.setVatId(givenNewVatId);
|
||||
givenDebitor.setVatCountryCode(givenNewVatCountryCode);
|
||||
@ -317,15 +346,15 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
|
||||
result.assertSuccessful();
|
||||
assertThatDebitorIsVisibleForUserWithRole(
|
||||
result.returnedValue(),
|
||||
"global#global.admin");
|
||||
"global#global.admin", true);
|
||||
|
||||
// ... partner role was reassigned:
|
||||
assertThatDebitorIsNotVisibleForUserWithRole(
|
||||
result.returnedValue(),
|
||||
"hs_office_partner#10004:FourtheG-fourthcontact.agent");
|
||||
"hs_office_relation#FourtheG-with-DEBITOR-FourtheG.admin");
|
||||
assertThatDebitorIsVisibleForUserWithRole(
|
||||
result.returnedValue(),
|
||||
"hs_office_partner#10001:FirstGmbH-firstcontact.agent");
|
||||
"hs_office_relation#FirstGmbH-with-DEBITOR-FirbySusan.agent", true);
|
||||
|
||||
// ... contact role was reassigned:
|
||||
assertThatDebitorIsNotVisibleForUserWithRole(
|
||||
@ -333,15 +362,15 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
|
||||
"hs_office_contact#fifthcontact.admin");
|
||||
assertThatDebitorIsVisibleForUserWithRole(
|
||||
result.returnedValue(),
|
||||
"hs_office_contact#sixthcontact.admin");
|
||||
"hs_office_contact#sixthcontact.admin", false);
|
||||
|
||||
// ... bank-account role was reassigned:
|
||||
assertThatDebitorIsNotVisibleForUserWithRole(
|
||||
result.returnedValue(),
|
||||
"hs_office_bankaccount#FourtheG.admin");
|
||||
"hs_office_bankaccount#DE02200505501015871393.admin");
|
||||
assertThatDebitorIsVisibleForUserWithRole(
|
||||
result.returnedValue(),
|
||||
"hs_office_bankaccount#FirstGmbH.admin");
|
||||
"hs_office_bankaccount#DE02120300000000202051.admin", true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -351,9 +380,9 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
|
||||
final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "fifth contact", null, "fig");
|
||||
assertThatDebitorIsVisibleForUserWithRole(
|
||||
givenDebitor,
|
||||
"hs_office_partner#10004:FourtheG-fourthcontact.admin");
|
||||
assertThatDebitorActuallyInDatabase(givenDebitor);
|
||||
final var givenNewBankAccount = bankAccountRepo.findByOptionalHolderLike("first").get(0);
|
||||
"hs_office_relation#FourtheG-with-DEBITOR-FourtheG.admin", true);
|
||||
assertThatDebitorActuallyInDatabase(givenDebitor, true);
|
||||
final var givenNewBankAccount = one(bankAccountRepo.findByOptionalHolderLike("first"));
|
||||
|
||||
// when
|
||||
final var result = jpaAttempt.transacted(() -> {
|
||||
@ -366,12 +395,12 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
|
||||
result.assertSuccessful();
|
||||
assertThatDebitorIsVisibleForUserWithRole(
|
||||
result.returnedValue(),
|
||||
"global#global.admin");
|
||||
"global#global.admin", true);
|
||||
|
||||
// ... bank-account role was assigned:
|
||||
assertThatDebitorIsVisibleForUserWithRole(
|
||||
result.returnedValue(),
|
||||
"hs_office_bankaccount#FirstGmbH.admin");
|
||||
"hs_office_bankaccount#DE02120300000000202051.admin", true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -381,8 +410,8 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
|
||||
final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "fifth contact", "Fourth", "fih");
|
||||
assertThatDebitorIsVisibleForUserWithRole(
|
||||
givenDebitor,
|
||||
"hs_office_partner#10004:FourtheG-fourthcontact.admin");
|
||||
assertThatDebitorActuallyInDatabase(givenDebitor);
|
||||
"hs_office_relation#HostsharingeG-with-PARTNER-FourtheG.agent", true);
|
||||
assertThatDebitorActuallyInDatabase(givenDebitor, true);
|
||||
|
||||
// when
|
||||
final var result = jpaAttempt.transacted(() -> {
|
||||
@ -395,34 +424,34 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
|
||||
result.assertSuccessful();
|
||||
assertThatDebitorIsVisibleForUserWithRole(
|
||||
result.returnedValue(),
|
||||
"global#global.admin");
|
||||
"global#global.admin", true);
|
||||
|
||||
// ... bank-account role was removed from previous bank-account admin:
|
||||
assertThatDebitorIsNotVisibleForUserWithRole(
|
||||
result.returnedValue(),
|
||||
"hs_office_bankaccount#FourtheG.admin");
|
||||
"hs_office_bankaccount#DE02200505501015871393.admin");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void partnerAdmin_canNotUpdateRelatedDebitor() {
|
||||
public void partnerAgent_canNotUpdateRelatedDebitor() {
|
||||
// given
|
||||
context("superuser-alex@hostsharing.net");
|
||||
final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "eighth", "Fourth", "eig");
|
||||
assertThatDebitorIsVisibleForUserWithRole(
|
||||
givenDebitor,
|
||||
"hs_office_partner#10004:FourtheG-fourthcontact.admin");
|
||||
assertThatDebitorActuallyInDatabase(givenDebitor);
|
||||
"hs_office_relation#HostsharingeG-with-PARTNER-FourtheG.agent", true);
|
||||
assertThatDebitorActuallyInDatabase(givenDebitor, true);
|
||||
|
||||
// when
|
||||
final var result = jpaAttempt.transacted(() -> {
|
||||
context("superuser-alex@hostsharing.net", "hs_office_partner#10004:FourtheG-fourthcontact.admin");
|
||||
context("superuser-alex@hostsharing.net", "hs_office_relation#HostsharingeG-with-PARTNER-FourtheG.agent");
|
||||
givenDebitor.setVatId("NEW-VAT-ID");
|
||||
return toCleanup(debitorRepo.save(givenDebitor));
|
||||
});
|
||||
|
||||
// then
|
||||
result.assertExceptionWithRootCauseMessage(JpaSystemException.class,
|
||||
"[403] Subject ", " is not allowed to update hs_office_debitor uuid");
|
||||
result.assertExceptionWithRootCauseMessage(JpaSystemException.class,
|
||||
"[403] Subject ", " is not allowed to update hs_office_debitor uuid");
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -430,10 +459,10 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
|
||||
// given
|
||||
context("superuser-alex@hostsharing.net");
|
||||
final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "ninth", "Fourth", "nin");
|
||||
assertThatDebitorActuallyInDatabase(givenDebitor, true);
|
||||
assertThatDebitorIsVisibleForUserWithRole(
|
||||
givenDebitor,
|
||||
"hs_office_contact#ninthcontact.admin");
|
||||
assertThatDebitorActuallyInDatabase(givenDebitor);
|
||||
"hs_office_contact#ninthcontact.admin", false);
|
||||
|
||||
// when
|
||||
final var result = jpaAttempt.transacted(() -> {
|
||||
@ -443,22 +472,34 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
|
||||
});
|
||||
|
||||
// then
|
||||
result.assertExceptionWithRootCauseMessage(JpaSystemException.class,
|
||||
"[403] Subject ", " is not allowed to update hs_office_debitor uuid");
|
||||
result.assertExceptionWithRootCauseMessage(
|
||||
JpaObjectRetrievalFailureException.class,
|
||||
// this technical error message gets translated to a [403] error at the controller level
|
||||
"Unable to find net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity with id ");
|
||||
}
|
||||
|
||||
private void assertThatDebitorActuallyInDatabase(final HsOfficeDebitorEntity saved) {
|
||||
private void assertThatDebitorActuallyInDatabase(final HsOfficeDebitorEntity saved, final boolean withPartner) {
|
||||
final var found = debitorRepo.findByUuid(saved.getUuid());
|
||||
assertThat(found).isNotEmpty().get().isNotSameAs(saved)
|
||||
.extracting(Object::toString).isEqualTo(saved.toString());
|
||||
assertThat(found).isNotEmpty();
|
||||
found.ifPresent(foundEntity -> {
|
||||
em.refresh(foundEntity);
|
||||
Hibernate.initialize(foundEntity);
|
||||
assertThat(foundEntity).isNotSameAs(saved);
|
||||
if (withPartner) {
|
||||
assertThat(foundEntity.getPartner()).isNotNull();
|
||||
}
|
||||
assertThat(foundEntity.getDebitorRel()).extracting(HsOfficeRelationEntity::toString)
|
||||
.isEqualTo(saved.getDebitorRel().toString());
|
||||
});
|
||||
}
|
||||
|
||||
private void assertThatDebitorIsVisibleForUserWithRole(
|
||||
final HsOfficeDebitorEntity entity,
|
||||
final String assumedRoles) {
|
||||
final String assumedRoles,
|
||||
final boolean withPartner) {
|
||||
jpaAttempt.transacted(() -> {
|
||||
context("superuser-alex@hostsharing.net", assumedRoles);
|
||||
assertThatDebitorActuallyInDatabase(entity);
|
||||
assertThatDebitorActuallyInDatabase(entity, withPartner);
|
||||
}).assertSuccessful();
|
||||
}
|
||||
|
||||
@ -497,14 +538,14 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
|
||||
}
|
||||
|
||||
@Test
|
||||
public void relatedPerson_canNotDeleteTheirRelatedDebitor() {
|
||||
public void debitorAgent_canViewButNotDeleteTheirRelatedDebitor() {
|
||||
// given
|
||||
context("superuser-alex@hostsharing.net", null);
|
||||
final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "eleventh", "Fourth", "ele");
|
||||
|
||||
// when
|
||||
final var result = jpaAttempt.transacted(() -> {
|
||||
context("person-FourtheG@example.com");
|
||||
context("superuser-alex@hostsharing.net", "hs_office_relation#FourtheG-with-DEBITOR-FourtheG.admin");
|
||||
assertThat(debitorRepo.findByUuid(givenDebitor.getUuid())).isPresent();
|
||||
|
||||
debitorRepo.deleteByUuid(givenDebitor.getUuid());
|
||||
@ -561,20 +602,24 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
|
||||
}
|
||||
|
||||
private HsOfficeDebitorEntity givenSomeTemporaryDebitor(
|
||||
final String partner,
|
||||
final String contact,
|
||||
final String bankAccount,
|
||||
final String partnerName,
|
||||
final String contactLabel,
|
||||
final String bankAccountHolder,
|
||||
final String defaultPrefix) {
|
||||
return jpaAttempt.transacted(() -> {
|
||||
context("superuser-alex@hostsharing.net");
|
||||
final var givenPartner = partnerRepo.findPartnerByOptionalNameLike(partner).get(0);
|
||||
final var givenContact = contactRepo.findContactByOptionalLabelLike(contact).get(0);
|
||||
final var givenPartnerPerson = one(personRepo.findPersonByOptionalNameLike(partnerName));
|
||||
final var givenContact = one(contactRepo.findContactByOptionalLabelLike(contactLabel));
|
||||
final var givenBankAccount =
|
||||
bankAccount != null ? bankAccountRepo.findByOptionalHolderLike(bankAccount).get(0) : null;
|
||||
bankAccountHolder != null ? one(bankAccountRepo.findByOptionalHolderLike(bankAccountHolder)) : null;
|
||||
final var newDebitor = HsOfficeDebitorEntity.builder()
|
||||
.debitorNumberSuffix((byte)20)
|
||||
.partner(givenPartner)
|
||||
.billingContact(givenContact)
|
||||
.debitorRel(HsOfficeRelationEntity.builder()
|
||||
.type(HsOfficeRelationType.DEBITOR)
|
||||
.anchor(givenPartnerPerson)
|
||||
.holder(givenPartnerPerson)
|
||||
.contact(givenContact)
|
||||
.build())
|
||||
.refundBankAccount(givenBankAccount)
|
||||
.defaultPrefix(defaultPrefix)
|
||||
.billable(true)
|
||||
|
@ -1,7 +1,8 @@
|
||||
package net.hostsharing.hsadminng.hs.office.debitor;
|
||||
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
|
||||
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
|
||||
|
||||
import static net.hostsharing.hsadminng.hs.office.contact.TestHsOfficeContact.TEST_CONTACT;
|
||||
import static net.hostsharing.hsadminng.hs.office.partner.TestHsOfficePartner.TEST_PARTNER;
|
||||
@ -13,7 +14,11 @@ public class TestHsOfficeDebitor {
|
||||
|
||||
public static final HsOfficeDebitorEntity TEST_DEBITOR = HsOfficeDebitorEntity.builder()
|
||||
.debitorNumberSuffix(DEFAULT_DEBITOR_SUFFIX)
|
||||
.debitorRel(HsOfficeRelationEntity.builder()
|
||||
.holder(HsOfficePersonEntity.builder().build())
|
||||
.anchor(HsOfficePersonEntity.builder().build())
|
||||
.contact(TEST_CONTACT)
|
||||
.build())
|
||||
.partner(TEST_PARTNER)
|
||||
.billingContact(TEST_CONTACT)
|
||||
.build();
|
||||
}
|
||||
|
@ -5,7 +5,6 @@ import io.restassured.RestAssured;
|
||||
import io.restassured.http.ContentType;
|
||||
import net.hostsharing.hsadminng.HsadminNgApplication;
|
||||
import net.hostsharing.hsadminng.context.Context;
|
||||
import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorRepository;
|
||||
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerRepository;
|
||||
import net.hostsharing.hsadminng.hs.office.test.ContextBasedTestWithCleanup;
|
||||
import net.hostsharing.test.Accepts;
|
||||
@ -24,6 +23,7 @@ import jakarta.persistence.PersistenceContext;
|
||||
import java.time.LocalDate;
|
||||
import java.util.UUID;
|
||||
|
||||
import static net.hostsharing.hsadminng.hs.office.membership.HsOfficeReasonForTermination.CANCELLATION;
|
||||
import static net.hostsharing.hsadminng.hs.office.membership.HsOfficeReasonForTermination.NONE;
|
||||
import static net.hostsharing.test.IsValidUuidMatcher.isUuidValid;
|
||||
import static net.hostsharing.test.JsonMatcher.lenientlyEquals;
|
||||
@ -51,9 +51,6 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
|
||||
@Autowired
|
||||
HsOfficeMembershipRepository membershipRepo;
|
||||
|
||||
@Autowired
|
||||
HsOfficeDebitorRepository debitorRepo;
|
||||
|
||||
@Autowired
|
||||
HsOfficePartnerRepository partnerRepo;
|
||||
|
||||
@ -82,8 +79,7 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
|
||||
.body("", lenientlyEquals("""
|
||||
[
|
||||
{
|
||||
"partner": { "person": { "tradeName": "First GmbH" } },
|
||||
"mainDebitor": { "debitorNumber": 1000111 },
|
||||
"partner": { "partnerNumber": 10001 },
|
||||
"memberNumber": 1000101,
|
||||
"memberNumberSuffix": "01",
|
||||
"validFrom": "2022-10-01",
|
||||
@ -91,8 +87,7 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
|
||||
"reasonForTermination": "NONE"
|
||||
},
|
||||
{
|
||||
"partner": { "person": { "tradeName": "Second e.K." } },
|
||||
"mainDebitor": { "debitorNumber": 1000212 },
|
||||
"partner": { "partnerNumber": 10002 },
|
||||
"memberNumber": 1000202,
|
||||
"memberNumberSuffix": "02",
|
||||
"validFrom": "2022-10-01",
|
||||
@ -100,8 +95,7 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
|
||||
"reasonForTermination": "NONE"
|
||||
},
|
||||
{
|
||||
"partner": { "person": { "tradeName": "Third OHG" } },
|
||||
"mainDebitor": { "debitorNumber": 1000313 },
|
||||
"partner": { "partnerNumber": 10003 },
|
||||
"memberNumber": 1000303,
|
||||
"memberNumberSuffix": "03",
|
||||
"validFrom": "2022-10-01",
|
||||
@ -132,8 +126,7 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
|
||||
.body("", lenientlyEquals("""
|
||||
[
|
||||
{
|
||||
"partner": { "person": { "tradeName": "First GmbH" } },
|
||||
"mainDebitor": { "debitorNumber": 1000111 },
|
||||
"partner": { "partnerNumber": 10001 },
|
||||
"memberNumber": 1000101,
|
||||
"memberNumberSuffix": "01",
|
||||
"validFrom": "2022-10-01",
|
||||
@ -161,8 +154,7 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
|
||||
.body("", lenientlyEquals("""
|
||||
[
|
||||
{
|
||||
"partner": { "person": { "tradeName": "Second e.K." } },
|
||||
"mainDebitor": { "debitorNumber": 1000212 },
|
||||
"partner": { "partnerNumber": 10002 },
|
||||
"memberNumber": 1000202,
|
||||
"memberNumberSuffix": "02",
|
||||
"validFrom": "2022-10-01",
|
||||
@ -184,7 +176,6 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
|
||||
|
||||
context.define("superuser-alex@hostsharing.net");
|
||||
final var givenPartner = partnerRepo.findPartnerByOptionalNameLike("Third").get(0);
|
||||
final var givenDebitor = debitorRepo.findDebitorByOptionalNameLike("Third").get(0);
|
||||
final var givenMemberSuffix = TEMP_MEMBER_NUMBER_SUFFIX;
|
||||
final var expectedMemberNumber = Integer.parseInt(givenPartner.getPartnerNumber() + TEMP_MEMBER_NUMBER_SUFFIX);
|
||||
|
||||
@ -195,12 +186,11 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
|
||||
.body("""
|
||||
{
|
||||
"partnerUuid": "%s",
|
||||
"mainDebitorUuid": "%s",
|
||||
"memberNumberSuffix": "%s",
|
||||
"validFrom": "2022-10-13",
|
||||
"membershipFeeBillable": "true"
|
||||
}
|
||||
""".formatted(givenPartner.getUuid(), givenDebitor.getUuid(), givenMemberSuffix))
|
||||
""".formatted(givenPartner.getUuid(), givenMemberSuffix))
|
||||
.port(port)
|
||||
.when()
|
||||
.post("http://localhost/api/hs/office/memberships")
|
||||
@ -208,9 +198,7 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
|
||||
.statusCode(201)
|
||||
.contentType(ContentType.JSON)
|
||||
.body("uuid", isUuidValid())
|
||||
.body("mainDebitor.debitorNumber", is(givenDebitor.getDebitorNumber()))
|
||||
.body("mainDebitor.debitorNumberSuffix", is((int) givenDebitor.getDebitorNumberSuffix()))
|
||||
.body("partner.person.tradeName", is("Third OHG"))
|
||||
.body("partner.partnerNumber", is(10003))
|
||||
.body("memberNumber", is(expectedMemberNumber))
|
||||
.body("memberNumberSuffix", is(givenMemberSuffix))
|
||||
.body("validFrom", is("2022-10-13"))
|
||||
@ -246,8 +234,7 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
|
||||
.contentType("application/json")
|
||||
.body("", lenientlyEquals("""
|
||||
{
|
||||
"partner": { "person": { "tradeName": "First GmbH" } },
|
||||
"mainDebitor": { "debitorNumber": 1000111 },
|
||||
"partner": { "partnerNumber": 10001 },
|
||||
"memberNumber": 1000101,
|
||||
"memberNumberSuffix": "01",
|
||||
"validFrom": "2022-10-01",
|
||||
@ -275,14 +262,14 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
|
||||
|
||||
@Test
|
||||
@Accepts({ "Membership:X(Access Control)" })
|
||||
void debitorAgentUser_canGetRelatedMembership() {
|
||||
void parnerRelAgent_canGetRelatedMembership() {
|
||||
context.define("superuser-alex@hostsharing.net");
|
||||
final var givenMembershipUuid = membershipRepo.findMembershipByMemberNumber(1000303).getUuid();
|
||||
|
||||
RestAssured // @formatter:off
|
||||
.given()
|
||||
.header("current-user", "superuser-alex@hostsharing.net")
|
||||
.header("assumed-roles", "hs_office_debitor#1000313:ThirdOHG-thirdcontact.agent")
|
||||
.header("assumed-roles", "hs_office_relation#HostsharingeG-with-PARTNER-ThirdOHG.agent")
|
||||
.port(port)
|
||||
.when()
|
||||
.get("http://localhost/api/hs/office/memberships/" + givenMembershipUuid)
|
||||
@ -291,11 +278,7 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
|
||||
.contentType("application/json")
|
||||
.body("", lenientlyEquals("""
|
||||
{
|
||||
"partner": { "person": { "tradeName": "Third OHG" } },
|
||||
"mainDebitor": {
|
||||
"debitorNumber": 1000313,
|
||||
"billingContact": { "label": "third contact" }
|
||||
},
|
||||
"partner": { "partnerNumber": 10003 },
|
||||
"memberNumber": 1000303,
|
||||
"memberNumberSuffix": "03",
|
||||
"validFrom": "2022-10-01",
|
||||
@ -314,7 +297,7 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
|
||||
void globalAdmin_canPatchValidToOfArbitraryMembership() {
|
||||
|
||||
context.define("superuser-alex@hostsharing.net");
|
||||
final var givenMembership = givenSomeTemporaryMembershipBessler();
|
||||
final var givenMembership = givenSomeTemporaryMembershipBessler("First");
|
||||
|
||||
final var location = RestAssured // @formatter:off
|
||||
.given()
|
||||
@ -333,10 +316,7 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
|
||||
.statusCode(200)
|
||||
.contentType(ContentType.JSON)
|
||||
.body("uuid", isUuidValid())
|
||||
.body("partner.person.tradeName", is(givenMembership.getPartner().getPerson().getTradeName()))
|
||||
.body("mainDebitor.debitorNumber", is(givenMembership.getMainDebitor().getDebitorNumber()))
|
||||
.body("mainDebitor.debitorNumberSuffix", is((int) givenMembership.getMainDebitor().getDebitorNumberSuffix()))
|
||||
.body("mainDebitor.debitorNumberSuffix", is((int) givenMembership.getMainDebitor().getDebitorNumberSuffix()))
|
||||
.body("partner.partnerNumber", is(givenMembership.getPartner().getPartnerNumber()))
|
||||
.body("memberNumberSuffix", is(givenMembership.getMemberNumberSuffix()))
|
||||
.body("validFrom", is("2022-11-01"))
|
||||
.body("validTo", is("2023-12-31"))
|
||||
@ -346,72 +326,31 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
|
||||
// finally, the Membership is actually updated
|
||||
assertThat(membershipRepo.findByUuid(givenMembership.getUuid())).isPresent().get()
|
||||
.matches(mandate -> {
|
||||
assertThat(mandate.getPartner().toShortString()).isEqualTo("LP First GmbH");
|
||||
assertThat(mandate.getMainDebitor().toString()).isEqualTo(givenMembership.getMainDebitor().toString());
|
||||
assertThat(mandate.getPartner().toShortString()).isEqualTo("P-10001");
|
||||
assertThat(mandate.getMemberNumberSuffix()).isEqualTo(givenMembership.getMemberNumberSuffix());
|
||||
assertThat(mandate.getValidity().asString()).isEqualTo("[2022-11-01,2024-01-01)");
|
||||
assertThat(mandate.getReasonForTermination()).isEqualTo(HsOfficeReasonForTermination.CANCELLATION);
|
||||
assertThat(mandate.getReasonForTermination()).isEqualTo(CANCELLATION);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void globalAdmin_canPatchMainDebitorOfArbitraryMembership() {
|
||||
void partnerRelAgent_canPatchValidityOfRelatedMembership() {
|
||||
|
||||
context.define("superuser-alex@hostsharing.net");
|
||||
final var givenMembership = givenSomeTemporaryMembershipBessler();
|
||||
final var givenNewMainDebitor = debitorRepo.findDebitorByDebitorNumber(1000313).get(0);
|
||||
// given
|
||||
final var givenPartnerAgent = "hs_office_relation#HostsharingeG-with-PARTNER-FirstGmbH.agent";
|
||||
context.define("superuser-alex@hostsharing.net", givenPartnerAgent);
|
||||
final var givenMembership = givenSomeTemporaryMembershipBessler("First");
|
||||
|
||||
// when
|
||||
RestAssured // @formatter:off
|
||||
.given()
|
||||
.header("current-user", "superuser-alex@hostsharing.net")
|
||||
.header("assumed-roles", givenPartnerAgent)
|
||||
.contentType(ContentType.JSON)
|
||||
.body("""
|
||||
{
|
||||
"mainDebitorUuid": "%s"
|
||||
}
|
||||
""".formatted(givenNewMainDebitor.getUuid()))
|
||||
.port(port)
|
||||
.when()
|
||||
.patch("http://localhost/api/hs/office/memberships/" + givenMembership.getUuid())
|
||||
.then().log().all().assertThat()
|
||||
.statusCode(200)
|
||||
.contentType(ContentType.JSON)
|
||||
.body("uuid", isUuidValid())
|
||||
.body("partner.person.tradeName", is(givenMembership.getPartner().getPerson().getTradeName()))
|
||||
.body("mainDebitor.debitorNumber", is(1000313))
|
||||
.body("memberNumberSuffix", is(givenMembership.getMemberNumberSuffix()))
|
||||
.body("validFrom", is("2022-11-01"))
|
||||
.body("validTo", nullValue())
|
||||
.body("reasonForTermination", is("NONE"));
|
||||
// @formatter:on
|
||||
|
||||
// finally, the Membership is actually updated
|
||||
assertThat(membershipRepo.findByUuid(givenMembership.getUuid())).isPresent().get()
|
||||
.matches(mandate -> {
|
||||
assertThat(mandate.getPartner().toShortString()).isEqualTo("LP First GmbH");
|
||||
assertThat(mandate.getMainDebitor().toString()).isEqualTo(givenMembership.getMainDebitor().toString());
|
||||
assertThat(mandate.getMemberNumberSuffix()).isEqualTo(givenMembership.getMemberNumberSuffix());
|
||||
assertThat(mandate.getValidity().asString()).isEqualTo("[2022-11-01,)");
|
||||
assertThat(mandate.getReasonForTermination()).isEqualTo(NONE);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void partnerAgent_canViewButNotPatchValidityOfRelatedMembership() {
|
||||
|
||||
context.define("superuser-alex@hostsharing.net", "hs_office_partner#10001:FirstGmbH-firstcontact.agent");
|
||||
final var givenMembership = givenSomeTemporaryMembershipBessler();
|
||||
|
||||
final var location = RestAssured // @formatter:off
|
||||
.given()
|
||||
.header("current-user", "superuser-alex@hostsharing.net")
|
||||
.header("assumed-roles", "hs_office_partner#10001:FirstGmbH-firstcontact.agent")
|
||||
.contentType(ContentType.JSON)
|
||||
.body("""
|
||||
{
|
||||
"validTo": "2023-12-31",
|
||||
"validTo": "2024-01-01",
|
||||
"reasonForTermination": "CANCELLATION"
|
||||
}
|
||||
""")
|
||||
@ -419,13 +358,13 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
|
||||
.when()
|
||||
.patch("http://localhost/api/hs/office/memberships/" + givenMembership.getUuid())
|
||||
.then().assertThat()
|
||||
.statusCode(403); // @formatter:on
|
||||
.statusCode(200); // @formatter:on
|
||||
|
||||
// finally, the Membership is actually updated
|
||||
assertThat(membershipRepo.findByUuid(givenMembership.getUuid())).isPresent().get()
|
||||
.matches(mandate -> {
|
||||
assertThat(mandate.getValidity().asString()).isEqualTo("[2022-11-01,)");
|
||||
assertThat(mandate.getReasonForTermination()).isEqualTo(NONE);
|
||||
assertThat(mandate.getValidity().asString()).isEqualTo("[2022-11-01,2024-01-02)");
|
||||
assertThat(mandate.getReasonForTermination()).isEqualTo(CANCELLATION);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
@ -438,7 +377,7 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
|
||||
@Test
|
||||
void globalAdmin_canDeleteArbitraryMembership() {
|
||||
context.define("superuser-alex@hostsharing.net");
|
||||
final var givenMembership = givenSomeTemporaryMembershipBessler();
|
||||
final var givenMembership = givenSomeTemporaryMembershipBessler("First");
|
||||
|
||||
RestAssured // @formatter:off
|
||||
.given()
|
||||
@ -457,12 +396,12 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
|
||||
@Accepts({ "Membership:X(Access Control)" })
|
||||
void partnerAgentUser_canNotDeleteRelatedMembership() {
|
||||
context.define("superuser-alex@hostsharing.net");
|
||||
final var givenMembership = givenSomeTemporaryMembershipBessler();
|
||||
final var givenMembership = givenSomeTemporaryMembershipBessler("First");
|
||||
|
||||
RestAssured // @formatter:off
|
||||
.given()
|
||||
.header("current-user", "superuser-alex@hostsharing.net")
|
||||
.header("assumed-roles", "hs_office_partner#10001:FirstGmbH-firstcontact.admin")
|
||||
.header("assumed-roles", "hs_office_relation#HostsharingeG-with-PARTNER-FirstGmbH.agent")
|
||||
.port(port)
|
||||
.when()
|
||||
.delete("http://localhost/api/hs/office/memberships/" + givenMembership.getUuid())
|
||||
@ -477,7 +416,7 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
|
||||
@Accepts({ "Membership:X(Access Control)" })
|
||||
void normalUser_canNotDeleteUnrelatedMembership() {
|
||||
context.define("superuser-alex@hostsharing.net");
|
||||
final var givenMembership = givenSomeTemporaryMembershipBessler();
|
||||
final var givenMembership = givenSomeTemporaryMembershipBessler("First");
|
||||
|
||||
RestAssured // @formatter:off
|
||||
.given()
|
||||
@ -493,15 +432,13 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
|
||||
}
|
||||
}
|
||||
|
||||
private HsOfficeMembershipEntity givenSomeTemporaryMembershipBessler() {
|
||||
private HsOfficeMembershipEntity givenSomeTemporaryMembershipBessler(final String partnerName) {
|
||||
return jpaAttempt.transacted(() -> {
|
||||
context.define("superuser-alex@hostsharing.net");
|
||||
final var givenDebitor = debitorRepo.findDebitorByOptionalNameLike("First").get(0);
|
||||
final var givenPartner = partnerRepo.findPartnerByOptionalNameLike("First").get(0);
|
||||
final var givenPartner = partnerRepo.findPartnerByOptionalNameLike(partnerName).get(0);
|
||||
final var newMembership = HsOfficeMembershipEntity.builder()
|
||||
.uuid(UUID.randomUUID())
|
||||
.partner(givenPartner)
|
||||
.mainDebitor(givenDebitor)
|
||||
.memberNumberSuffix(TEMP_MEMBER_NUMBER_SUFFIX)
|
||||
.validity(Range.closedInfinite(LocalDate.parse("2022-11-01")))
|
||||
.reasonForTermination(NONE)
|
||||
|
@ -2,7 +2,6 @@ package net.hostsharing.hsadminng.hs.office.membership;
|
||||
|
||||
import net.hostsharing.hsadminng.context.Context;
|
||||
import net.hostsharing.hsadminng.hs.office.coopassets.HsOfficeCoopAssetsTransactionRepository;
|
||||
import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity;
|
||||
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity;
|
||||
import net.hostsharing.hsadminng.mapper.Mapper;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
@ -28,7 +27,6 @@ import java.util.UUID;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
@ -76,7 +74,6 @@ public class HsOfficeMembershipControllerRestTest {
|
||||
.content("""
|
||||
{
|
||||
"partnerUuid": null,
|
||||
"mainDebitorUuid": "%s",
|
||||
"memberNumberSuffix": "01",
|
||||
"validFrom": "2022-10-13",
|
||||
"membershipFeeBillable": "true"
|
||||
@ -91,40 +88,12 @@ public class HsOfficeMembershipControllerRestTest {
|
||||
.andExpect(jsonPath("message", is("[partnerUuid must not be null but is \"null\"]")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void respondBadRequest_ifDebitorUuidIsMissing() throws Exception {
|
||||
|
||||
// when
|
||||
mockMvc.perform(MockMvcRequestBuilders
|
||||
.post("/api/hs/office/memberships")
|
||||
.header("current-user", "superuser-alex@hostsharing.net")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("""
|
||||
{
|
||||
"partnerUuid": "%s",
|
||||
"mainDebitorUuid": null,
|
||||
"memberNumberSuffix": "01",
|
||||
"validFrom": "2022-10-13",
|
||||
"membershipFeeBillable": "true"
|
||||
}
|
||||
""".formatted(UUID.randomUUID()))
|
||||
.accept(MediaType.APPLICATION_JSON))
|
||||
|
||||
// then
|
||||
.andExpect(status().is4xxClientError())
|
||||
.andExpect(jsonPath("statusCode", is(400)))
|
||||
.andExpect(jsonPath("statusPhrase", is("Bad Request")))
|
||||
.andExpect(jsonPath("message", is("[mainDebitorUuid must not be null but is \"null\"]")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void respondBadRequest_ifAnyGivenPartnerUuidCannotBeFound() throws Exception {
|
||||
|
||||
// given
|
||||
final var givenPartnerUuid = UUID.randomUUID();
|
||||
final var givenMainDebitorUuid = UUID.randomUUID();
|
||||
when(em.find(HsOfficePartnerEntity.class, givenPartnerUuid)).thenReturn(null);
|
||||
when(em.find(HsOfficeDebitorEntity.class, givenMainDebitorUuid)).thenReturn(mock(HsOfficeDebitorEntity.class));
|
||||
|
||||
// when
|
||||
mockMvc.perform(MockMvcRequestBuilders
|
||||
@ -134,12 +103,11 @@ public class HsOfficeMembershipControllerRestTest {
|
||||
.content("""
|
||||
{
|
||||
"partnerUuid": "%s",
|
||||
"mainDebitorUuid": "%s",
|
||||
"memberNumberSuffix": "01",
|
||||
"validFrom": "2022-10-13",
|
||||
"membershipFeeBillable": "true"
|
||||
}
|
||||
""".formatted(givenPartnerUuid, givenMainDebitorUuid))
|
||||
""".formatted(givenPartnerUuid))
|
||||
.accept(MediaType.APPLICATION_JSON))
|
||||
|
||||
// then
|
||||
@ -149,38 +117,6 @@ public class HsOfficeMembershipControllerRestTest {
|
||||
.andExpect(jsonPath("message", is("Unable to find Partner with uuid " + givenPartnerUuid)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void respondBadRequest_ifAnyGivenDebitorUuidCannotBeFound() throws Exception {
|
||||
|
||||
// given
|
||||
final var givenPartnerUuid = UUID.randomUUID();
|
||||
final var givenMainDebitorUuid = UUID.randomUUID();
|
||||
when(em.find(HsOfficePartnerEntity.class, givenPartnerUuid)).thenReturn(mock(HsOfficePartnerEntity.class));
|
||||
when(em.find(HsOfficeDebitorEntity.class, givenMainDebitorUuid)).thenReturn(null);
|
||||
|
||||
// when
|
||||
mockMvc.perform(MockMvcRequestBuilders
|
||||
.post("/api/hs/office/memberships")
|
||||
.header("current-user", "superuser-alex@hostsharing.net")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("""
|
||||
{
|
||||
"partnerUuid": "%s",
|
||||
"mainDebitorUuid": "%s",
|
||||
"memberNumberSuffix": "01",
|
||||
"validFrom": "2022-10-13",
|
||||
"membershipFeeBillable": "true"
|
||||
}
|
||||
""".formatted(givenPartnerUuid, givenMainDebitorUuid))
|
||||
.accept(MediaType.APPLICATION_JSON))
|
||||
|
||||
// then
|
||||
.andExpect(status().is4xxClientError())
|
||||
.andExpect(jsonPath("statusCode", is(400)))
|
||||
.andExpect(jsonPath("statusPhrase", is("Bad Request")))
|
||||
.andExpect(jsonPath("message", is("Unable to find Debitor with uuid " + givenMainDebitorUuid)));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@EnumSource(InvalidMemberSuffixVariants.class)
|
||||
void respondBadRequest_ifMemberNumberSuffixIsInvalid(final InvalidMemberSuffixVariants testCase) throws Exception {
|
||||
@ -193,12 +129,11 @@ public class HsOfficeMembershipControllerRestTest {
|
||||
.content("""
|
||||
{
|
||||
"partnerUuid": "%s",
|
||||
"mainDebitorUuid": "%s",
|
||||
%s
|
||||
"validFrom": "2022-10-13",
|
||||
"membershipFeeBillable": "true"
|
||||
}
|
||||
""".formatted(UUID.randomUUID(), UUID.randomUUID(), testCase.memberNumberSuffixEntry))
|
||||
""".formatted(UUID.randomUUID(), testCase.memberNumberSuffixEntry))
|
||||
.accept(MediaType.APPLICATION_JSON))
|
||||
|
||||
// then
|
||||
|
@ -17,7 +17,6 @@ import java.time.LocalDate;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static net.hostsharing.hsadminng.hs.office.debitor.TestHsOfficeDebitor.TEST_DEBITOR;
|
||||
import static net.hostsharing.hsadminng.hs.office.partner.TestHsOfficePartner.TEST_PARTNER;
|
||||
import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
@ -32,7 +31,6 @@ class HsOfficeMembershipEntityPatcherUnitTest extends PatchUnitTestBase<
|
||||
> {
|
||||
|
||||
private static final UUID INITIAL_MEMBERSHIP_UUID = UUID.randomUUID();
|
||||
private static final UUID PATCHED_MAIN_DEBITOR_UUID = UUID.randomUUID();
|
||||
private static final LocalDate GIVEN_VALID_FROM = LocalDate.parse("2020-04-15");
|
||||
private static final LocalDate PATCHED_VALID_TO = LocalDate.parse("2022-12-31");
|
||||
|
||||
@ -56,7 +54,6 @@ class HsOfficeMembershipEntityPatcherUnitTest extends PatchUnitTestBase<
|
||||
protected HsOfficeMembershipEntity newInitialEntity() {
|
||||
final var entity = new HsOfficeMembershipEntity();
|
||||
entity.setUuid(INITIAL_MEMBERSHIP_UUID);
|
||||
entity.setMainDebitor(TEST_DEBITOR);
|
||||
entity.setPartner(TEST_PARTNER);
|
||||
entity.setValidity(Range.closedInfinite(GIVEN_VALID_FROM));
|
||||
entity.setMembershipFeeBillable(GIVEN_MEMBERSHIP_FEE_BILLABLE);
|
||||
@ -70,19 +67,12 @@ class HsOfficeMembershipEntityPatcherUnitTest extends PatchUnitTestBase<
|
||||
|
||||
@Override
|
||||
protected HsOfficeMembershipEntityPatcher createPatcher(final HsOfficeMembershipEntity membership) {
|
||||
return new HsOfficeMembershipEntityPatcher(em, mapper, membership);
|
||||
return new HsOfficeMembershipEntityPatcher(mapper, membership);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Stream<Property> propertyTestDescriptors() {
|
||||
return Stream.of(
|
||||
new JsonNullableProperty<>(
|
||||
"debitor",
|
||||
HsOfficeMembershipPatchResource::setMainDebitorUuid,
|
||||
PATCHED_MAIN_DEBITOR_UUID,
|
||||
HsOfficeMembershipEntity::setMainDebitor,
|
||||
newDebitor(PATCHED_MAIN_DEBITOR_UUID))
|
||||
.notNullable(),
|
||||
new JsonNullableProperty<>(
|
||||
"valid",
|
||||
HsOfficeMembershipPatchResource::setValidTo,
|
||||
@ -102,10 +92,4 @@ class HsOfficeMembershipEntityPatcherUnitTest extends PatchUnitTestBase<
|
||||
HsOfficeMembershipEntity::setMembershipFeeBillable)
|
||||
);
|
||||
}
|
||||
|
||||
private static HsOfficeDebitorEntity newDebitor(final UUID uuid) {
|
||||
final var newDebitor = new HsOfficeDebitorEntity();
|
||||
newDebitor.setUuid(uuid);
|
||||
return newDebitor;
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user
besser jeder