introduce-partner-business-role #16

Merged
hsh-michaelhoennig merged 33 commits from introduce-partner-business-role into master 2024-02-01 14:48:16 +01:00
6 changed files with 91 additions and 15 deletions
Showing only changes of commit 10cd4bd0e8 - Show all commits

View File

@ -0,0 +1,14 @@
# Sketch UI for mailinglist subscriptions
```PlantUML
@startsalt
{{^==Mailinglist-Subscriptions
Members-Announce: | [<color:#9a9a9a>X</color>] | ^frank.meiler@example.org^ | [Create New]
Members-Discussion: | [X] | ^frank.meiler@example.org^ | [Create New]
Customers-Announce: | [<color:#9a9a9a>X</color>] | ^frank.meiler@example.org^ | [Create New]
Operations-Announce: | [<color:#9a9a9a>X</color>] | ^Default Contact Data^ | [Create New]
Operations-Discussion: | [ ] | ^ ^ | [Create New]
}}
@endsalt
```

View File

@ -33,7 +33,7 @@ create table hs_office_partner
( (
uuid uuid unique references RbacObject (uuid) initially deferred, uuid uuid unique references RbacObject (uuid) initially deferred,
partnerNumber numeric(5), partnerNumber numeric(5),
partnerRoleUuid uuid not null references hs_office_relationship(uuid), partnerRoleUuid uuid not null references hs_office_relationship(uuid) on delete cascade,
personUuid uuid not null references hs_office_person(uuid), -- TODO: remove, replaced by partnerRoleUuid personUuid uuid not null references hs_office_person(uuid), -- TODO: remove, replaced by partnerRoleUuid
contactUuid uuid not null references hs_office_contact(uuid), -- TODO: remove, replaced by partnerRoleUuid contactUuid uuid not null references hs_office_contact(uuid), -- TODO: remove, replaced by partnerRoleUuid
detailsUuid uuid not null references hs_office_partner_details(uuid) on delete cascade detailsUuid uuid not null references hs_office_partner_details(uuid) on delete cascade

View File

@ -27,12 +27,17 @@ create or replace function hsOfficePartnerRbacRolesTrigger()
language plpgsql language plpgsql
strict as $$ strict as $$
declare declare
oldPartnerRole hs_office_relationship;
newPartnerRole hs_office_relationship;
oldPerson hs_office_person; oldPerson hs_office_person;
newPerson hs_office_person; newPerson hs_office_person;
oldContact hs_office_contact; oldContact hs_office_contact;
newContact hs_office_contact; newContact hs_office_contact;
begin begin
select * from hs_office_relationship as r where r.uuid = NEW.partnerroleuuid into newPartnerRole;
select * from hs_office_person as p where p.uuid = NEW.personUuid into newPerson; 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_contact as c where c.uuid = NEW.contactUuid into newContact;
@ -52,6 +57,7 @@ begin
incomingSuperRoles => array[ incomingSuperRoles => array[
hsOfficePartnerOwner(NEW)], hsOfficePartnerOwner(NEW)],
outgoingSubRoles => array[ outgoingSubRoles => array[
hsOfficeRelationshipTenant(newPartnerRole),
hsOfficePersonTenant(newPerson), hsOfficePersonTenant(newPerson),
hsOfficeContactTenant(newContact)] hsOfficeContactTenant(newContact)]
); );
@ -60,6 +66,7 @@ begin
hsOfficePartnerAgent(NEW), hsOfficePartnerAgent(NEW),
incomingSuperRoles => array[ incomingSuperRoles => array[
hsOfficePartnerAdmin(NEW), hsOfficePartnerAdmin(NEW),
hsOfficeRelationshipAdmin(newPartnerRole),
hsOfficePersonAdmin(newPerson), hsOfficePersonAdmin(newPerson),
hsOfficeContactAdmin(newContact)] hsOfficeContactAdmin(newContact)]
); );
@ -69,6 +76,7 @@ begin
incomingSuperRoles => array[ incomingSuperRoles => array[
hsOfficePartnerAgent(NEW)], hsOfficePartnerAgent(NEW)],
outgoingSubRoles => array[ outgoingSubRoles => array[
hsOfficeRelationshipTenant(newPartnerRole),
hsOfficePersonGuest(newPerson), hsOfficePersonGuest(newPerson),
hsOfficeContactGuest(newContact)] hsOfficeContactGuest(newContact)]
); );
@ -109,6 +117,19 @@ begin
elsif TG_OP = 'UPDATE' then elsif TG_OP = 'UPDATE' then
if OLD.partnerRoleUuid <> NEW.partnerRoleUuid then
select * from hs_office_relationship as r where r.uuid = OLD.partnerRoleUuid into oldPartnerRole;
call revokeRoleFromRole(hsOfficeRelationshipTenant(oldPerson), hsOfficePartnerAdmin(OLD));
call grantRoleToRole(hsOfficeRelationshipTenant(newPerson), hsOfficePartnerAdmin(NEW));
call revokeRoleFromRole(hsOfficePartnerAgent(OLD), hsOfficeRelationshipAdmin(oldPerson));
call grantRoleToRole(hsOfficePartnerAgent(NEW), hsOfficeRelationshipAdmin(newPerson));
call revokeRoleFromRole(hsOfficeRelationshipGuest(oldPerson), hsOfficePartnerTenant(OLD));
call grantRoleToRole(hsOfficeRelationshipGuest(newPerson), hsOfficePartnerTenant(NEW));
end if;
if OLD.personUuid <> NEW.personUuid then if OLD.personUuid <> NEW.personUuid then
select * from hs_office_person as p where p.uuid = OLD.personUuid into oldPerson; select * from hs_office_person as p where p.uuid = OLD.personUuid into oldPerson;
@ -179,7 +200,7 @@ call generateRbacIdentityView('hs_office_partner', $idName$
call generateRbacRestrictedView('hs_office_partner', call generateRbacRestrictedView('hs_office_partner',
'(select idName from hs_office_person_iv p where p.uuid = target.personUuid)', '(select idName from hs_office_person_iv p where p.uuid = target.personUuid)',
$updates$ $updates$
partherRoleUuid = new.partnerRoleUuid, partnerRoleUuid = new.partnerRoleUuid,
personUuid = new.personUuid, personUuid = new.personUuid,
contactUuid = new.contactUuid contactUuid = new.contactUuid
$updates$); $updates$);
@ -191,7 +212,7 @@ call generateRbacRestrictedView('hs_office_partner',
--changeset hs-office-partner-rbac-NEW-PARTNER:1 endDelimiter:--// --changeset hs-office-partner-rbac-NEW-PARTNER:1 endDelimiter:--//
-- ---------------------------------------------------------------------------- -- ----------------------------------------------------------------------------
/* /*
Creates a global permission for new-partner and assigns it to the hostsharing admins role. Creates a global permission for new-partner and assigns it to the Hostsharing admins role.
*/ */
do language plpgsql $$ do language plpgsql $$
declare declare

View File

@ -763,7 +763,8 @@ public class ImportOfficeData extends ContextBasedTest {
if (containsRole(rec, "vip-contact")) { if (containsRole(rec, "vip-contact")) {
addRelationship(partnerPerson, contactPerson, contact, HsOfficeRelationshipType.VIP_CONTACT); addRelationship(partnerPerson, contactPerson, contact, HsOfficeRelationshipType.VIP_CONTACT);
} }
verifyContainsOnly(rec.getString("roles"), "partner", "vip-contact", "ex-partner", "billing", "contractual", "operation"); verifyContainsOnly(rec.getString("roles"),
"partner", "vip-contact", "ex-partner", "billing", "contractual", "operation");
}); });
optionallyAddMissingContractualRelationships(); optionallyAddMissingContractualRelationships();

View File

@ -122,7 +122,7 @@ class HsOfficePartnerControllerAcceptanceTest {
context.define("superuser-alex@hostsharing.net"); context.define("superuser-alex@hostsharing.net");
final var givenPerson = personRepo.findPersonByOptionalNameLike("Third").get(0); final var givenPerson = personRepo.findPersonByOptionalNameLike("Third").get(0);
final var givenContact = contactRepo.findContactByOptionalLabelLike("forth").get(0); final var givenContact = contactRepo.findContactByOptionalLabelLike("fourth").get(0);
final var location = RestAssured // @formatter:off final var location = RestAssured // @formatter:off
.given() .given()
@ -191,8 +191,9 @@ class HsOfficePartnerControllerAcceptanceTest {
void globalAdmin_canNotAddPartner_ifPersonDoesNotExist() { void globalAdmin_canNotAddPartner_ifPersonDoesNotExist() {
context.define("superuser-alex@hostsharing.net"); context.define("superuser-alex@hostsharing.net");
final var mandantPerson = personRepo.findPersonByOptionalNameLike("Hostsharing eG").get(0);
final var givenPersonUuid = UUID.fromString("3fa85f64-5717-4562-b3fc-2c963f66afa6"); final var givenPersonUuid = UUID.fromString("3fa85f64-5717-4562-b3fc-2c963f66afa6");
final var givenContact = contactRepo.findContactByOptionalLabelLike("forth").get(0); final var givenContact = contactRepo.findContactByOptionalLabelLike("fourth").get(0);
final var location = RestAssured // @formatter:off final var location = RestAssured // @formatter:off
.given() .given()
@ -201,11 +202,22 @@ class HsOfficePartnerControllerAcceptanceTest {
.body(""" .body("""
{ {
"partnerNumber": "12345", "partnerNumber": "12345",
"contactUuid": "%s", "partnerRole": {
"relAnchorUuid": "%s",
"relType": "PARTNER",
"relHolder": "%s",
"contact": "%s",
},
"personUuid": "%s", "personUuid": "%s",
"contactUuid": "%s",
"details": {} "details": {}
} }
""".formatted(givenContact.getUuid(), givenPersonUuid)) """.formatted(
mandantPerson.getUuid(),
givenPersonUuid,
givenContact.getUuid(),
givenPersonUuid,
givenContact.getUuid()))
.port(port) .port(port)
.when() .when()
.post("http://localhost/api/hs/office/partners") .post("http://localhost/api/hs/office/partners")

View File

@ -74,13 +74,23 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest {
// given // given
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
final var count = partnerRepo.count(); final var count = partnerRepo.count();
final var givenPerson = personRepo.findPersonByOptionalNameLike("First GmbH").get(0); final var givenMandantorPerson = personRepo.findPersonByOptionalNameLike("Hostsharing eG").get(0);
final var givenPartnerPerson = personRepo.findPersonByOptionalNameLike("First GmbH").get(0);
final var givenContact = contactRepo.findContactByOptionalLabelLike("first contact").get(0); final var givenContact = contactRepo.findContactByOptionalLabelLike("first contact").get(0);
final var partnerRole = HsOfficeRelationshipEntity.builder()
.relHolder(givenPartnerPerson)
.relType(HsOfficeRelationshipType.PARTNER)
.relAnchor(givenMandantorPerson)
.contact(givenContact)
.build();
relationshipRepo.save(partnerRole);
// when // when
final var result = attempt(em, () -> { final var result = attempt(em, () -> {
final var newPartner = toCleanup(HsOfficePartnerEntity.builder() final var newPartner = toCleanup(HsOfficePartnerEntity.builder()
.person(givenPerson) .partnerRole(partnerRole)
.person(givenPartnerPerson)
.contact(givenContact) .contact(givenContact)
.details(HsOfficePartnerDetailsEntity.builder() .details(HsOfficePartnerDetailsEntity.builder()
.build()) .build())
@ -148,11 +158,12 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest {
.containsExactlyInAnyOrder(Array.fromFormatted( .containsExactlyInAnyOrder(Array.fromFormatted(
initialGrantNames, initialGrantNames,
// relationship - TODO: check and cleanup // relationship - TODO: check and cleanup
"{ grant role relationship#HostsharingeG-with-PARTNER-EBess.tenant to role partner#22222:EBess-4th.admin by system and assume }",
"{ grant role relationship#HostsharingeG-with-PARTNER-EBess.tenant to role partner#22222:EBess-4th.tenant by system and assume }",
"{ grant role partner#22222:EBess-4th.agent to role relationship#HostsharingeG-with-PARTNER-EBess.admin by system and assume }",
"{ grant role relationship#HostsharingeG-with-PARTNER-EBess.owner to role global#global.admin by system and assume }", "{ grant role relationship#HostsharingeG-with-PARTNER-EBess.owner to role global#global.admin by system and assume }",
"{ grant role relationship#HostsharingeG-with-PARTNER-EBess.tenant to role contact#4th.admin by system and assume }", "{ grant role relationship#HostsharingeG-with-PARTNER-EBess.tenant to role contact#4th.admin by system and assume }",
"{ grant role person#HostsharingeG.tenant to role person#EBess.admin by system and assume }",
"{ grant role relationship#HostsharingeG-with-PARTNER-EBess.tenant to role person#EBess.admin by system and assume }", "{ grant role relationship#HostsharingeG-with-PARTNER-EBess.tenant to role person#EBess.admin by system and assume }",
"{ grant role person#EBess.tenant to role person#HostsharingeG.admin by system and assume }",
"{ grant role relationship#HostsharingeG-with-PARTNER-EBess.owner to role person#HostsharingeG.admin by system and assume }", "{ grant role relationship#HostsharingeG-with-PARTNER-EBess.owner to role person#HostsharingeG.admin by system and assume }",
"{ grant role relationship#HostsharingeG-with-PARTNER-EBess.tenant to role person#HostsharingeG.admin by system and assume }", "{ grant role relationship#HostsharingeG-with-PARTNER-EBess.tenant to role person#HostsharingeG.admin by system and assume }",
"{ grant perm edit on relationship#HostsharingeG-with-PARTNER-EBess to role relationship#HostsharingeG-with-PARTNER-EBess.admin by system and assume }", "{ grant perm edit on relationship#HostsharingeG-with-PARTNER-EBess to role relationship#HostsharingeG-with-PARTNER-EBess.admin by system and assume }",
@ -311,6 +322,10 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest {
// given // given
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
final var givenPartner = givenSomeTemporaryPartnerBessler(22222, "Erben Bessler", "ninth"); final var givenPartner = givenSomeTemporaryPartnerBessler(22222, "Erben Bessler", "ninth");
final var newPartnerRole = em.createNativeQuery(
"select uuid from hs_office_relationship where uuid=:partnerRoleUuid")
.setParameter("partnerRoleUuid", givenPartner.getPartnerRole().getUuid())
.getSingleResult();
assertThatPartnerIsVisibleForUserWithRole( assertThatPartnerIsVisibleForUserWithRole(
givenPartner, givenPartner,
"hs_office_partner#22222:ErbenBesslerMelBessler-ninthcontact.agent"); "hs_office_partner#22222:ErbenBesslerMelBessler-ninthcontact.agent");
@ -412,7 +427,8 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest {
// when // when
final var result = jpaAttempt.transacted(() -> { final var result = jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
return partnerRepo.deleteByUuid(givenPartner.getUuid()); return relationshipRepo.deleteByUuid(givenPartner.getPartnerRole().getUuid());
// return partnerRepo.deleteByUuid(givenPartner.getUuid());
}); });
// then // then
@ -455,11 +471,23 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest {
final Integer partnerNumber, final String person, final String contact) { final Integer partnerNumber, final String person, final String contact) {
return jpaAttempt.transacted(() -> { return jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
final var givenPerson = personRepo.findPersonByOptionalNameLike(person).get(0); final var givenMandantorPerson = personRepo.findPersonByOptionalNameLike("Hostsharing eG").get(0);
final var givenPartnerPerson = personRepo.findPersonByOptionalNameLike(person).get(0);
final var givenContact = contactRepo.findContactByOptionalLabelLike(contact).get(0); final var givenContact = contactRepo.findContactByOptionalLabelLike(contact).get(0);
final var partnerRole = HsOfficeRelationshipEntity.builder()
.relHolder(givenPartnerPerson)
.relType(HsOfficeRelationshipType.PARTNER)
.relAnchor(givenMandantorPerson)
.contact(givenContact)
.build();
relationshipRepo.save(partnerRole);
em.flush(); // TODO: why is that necessary?
final var newPartner = HsOfficePartnerEntity.builder() final var newPartner = HsOfficePartnerEntity.builder()
.partnerNumber(partnerNumber) .partnerNumber(partnerNumber)
.person(givenPerson) .partnerRole(partnerRole)
.person(givenPartnerPerson)
.contact(givenContact) .contact(givenContact)
.details(HsOfficePartnerDetailsEntity.builder().build()) .details(HsOfficePartnerDetailsEntity.builder().build())
.build(); .build();