hs-office-partner-details
5 files added
21 files modified
| | |
| | | JOIN HsOfficePersonEntity person ON person.uuid = partner.person |
| | | JOIN HsOfficeContactEntity contact ON contact.uuid = debitor.billingContact |
| | | WHERE :name is null |
| | | OR partner.birthName like concat(:name, '%') |
| | | OR partner.details.birthName like concat(:name, '%') |
| | | OR person.tradeName like concat(:name, '%') |
| | | OR person.familyName like concat(:name, '%') |
| | | OR person.givenName like concat(:name, '%') |
| | |
| | | entityToSave.setPerson(personRepo.findByUuid(body.getPersonUuid()).orElseThrow( |
| | | () -> new NoSuchElementException("cannot find person uuid " + body.getPersonUuid()) |
| | | )); |
| | | entityToSave.setDetails(map(body.getDetails(), HsOfficePartnerDetailsEntity.class)); |
| | | entityToSave.getDetails().setUuid(UUID.randomUUID()); |
| | | |
| | | final var saved = partnerRepo.save(entityToSave); |
| | | |
| | |
| | | |
| | | final var current = partnerRepo.findByUuid(partnerUuid).orElseThrow(); |
| | | |
| | | new HsOfficePartnerEntityPatcher(em, current, contactRepo::findByUuid, personRepo::findByUuid).apply(body); |
| | | new HsOfficePartnerEntityPatcher(em, current).apply(body); |
| | | |
| | | final var saved = partnerRepo.save(current); |
| | | final var mapped = map(saved, HsOfficePartnerResource.class); |
| | | return ResponseEntity.ok(mapped); |
| | | } |
| | | |
| | | |
| | | final BiConsumer<HsOfficePartnerEntity, HsOfficePartnerResource> PARTNER_ENTITY_TO_RESOURCE_POSTMAPPER = (entity, resource) -> { |
| | | resource.setPerson(map(entity.getPerson(), HsOfficePersonResource.class)); |
| | |
| | | // TODO.impl: user postmapper + getReference |
| | | private HsOfficePartnerEntity mapToHsOfficePartnerEntity(final HsOfficePartnerInsertResource resource) { |
| | | final var entity = new HsOfficePartnerEntity(); |
| | | entity.setBirthday(resource.getBirthday()); |
| | | entity.setBirthName(resource.getBirthName()); |
| | | entity.setDateOfDeath(resource.getDateOfDeath()); |
| | | entity.setRegistrationNumber(resource.getRegistrationNumber()); |
| | | entity.setRegistrationOffice(resource.getRegistrationOffice()); |
| | | // entity.setBirthday(resource.getBirthday()); |
| | | // entity.setBirthName(resource.getBirthName()); |
| | | // entity.setDateOfDeath(resource.getDateOfDeath()); |
| | | // entity.setRegistrationNumber(resource.getRegistrationNumber()); |
| | | // entity.setRegistrationOffice(resource.getRegistrationOffice()); |
| | | return entity; |
| | | } |
| | | } |
New file |
| | |
| | | package net.hostsharing.hsadminng.hs.office.partner; |
| | | |
| | | import lombok.*; |
| | | import net.hostsharing.hsadminng.Stringify; |
| | | import net.hostsharing.hsadminng.Stringifyable; |
| | | import net.hostsharing.hsadminng.errors.DisplayName; |
| | | |
| | | import javax.persistence.Column; |
| | | import javax.persistence.Entity; |
| | | import javax.persistence.Id; |
| | | import javax.persistence.Table; |
| | | import java.time.LocalDate; |
| | | import java.util.UUID; |
| | | |
| | | import static net.hostsharing.hsadminng.Stringify.stringify; |
| | | |
| | | @Entity |
| | | @Table(name = "hs_office_partner_details_rv") |
| | | @Getter |
| | | @Setter |
| | | @Builder |
| | | @NoArgsConstructor |
| | | @AllArgsConstructor |
| | | @DisplayName("PartnerDetails") |
| | | public class HsOfficePartnerDetailsEntity implements Stringifyable { |
| | | |
| | | private static Stringify<HsOfficePartnerDetailsEntity> stringify = stringify( |
| | | HsOfficePartnerDetailsEntity.class, |
| | | "partnerDetails") |
| | | .withProp(HsOfficePartnerDetailsEntity::getRegistrationOffice) |
| | | .withProp(HsOfficePartnerDetailsEntity::getRegistrationNumber) |
| | | .withProp(HsOfficePartnerDetailsEntity::getBirthday) |
| | | .withProp(HsOfficePartnerDetailsEntity::getBirthday) |
| | | .withProp(HsOfficePartnerDetailsEntity::getDateOfDeath) |
| | | .withSeparator(", ") |
| | | .quotedValues(false); |
| | | |
| | | private @Id UUID uuid; |
| | | |
| | | private @Column(name = "registrationoffice") String registrationOffice; |
| | | private @Column(name = "registrationnumber") String registrationNumber; |
| | | private @Column(name = "birthname") String birthName; |
| | | private @Column(name = "birthday") LocalDate birthday; |
| | | private @Column(name = "dateofdeath") LocalDate dateOfDeath; |
| | | |
| | | @Override |
| | | public String toString() { |
| | | return stringify.apply(this); |
| | | } |
| | | |
| | | @Override |
| | | public String toShortString() { |
| | | return registrationNumber != null ? registrationNumber |
| | | : birthName != null ? birthName |
| | | : birthday != null ? birthday.toString() |
| | | : dateOfDeath != null ? dateOfDeath.toString() : "<empty details>"; |
| | | } |
| | | } |
New file |
| | |
| | | package net.hostsharing.hsadminng.hs.office.partner; |
| | | |
| | | import net.hostsharing.hsadminng.EntityPatcher; |
| | | import net.hostsharing.hsadminng.OptionalFromJson; |
| | | import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficePartnerDetailsPatchResource; |
| | | |
| | | import javax.persistence.EntityManager; |
| | | import java.util.UUID; |
| | | |
| | | class HsOfficePartnerDetailsEntityPatcher implements EntityPatcher<HsOfficePartnerDetailsPatchResource> { |
| | | |
| | | private final EntityManager em; |
| | | private final HsOfficePartnerDetailsEntity entity; |
| | | |
| | | HsOfficePartnerDetailsEntityPatcher( |
| | | final EntityManager em, |
| | | final HsOfficePartnerDetailsEntity entity) { |
| | | this.em = em; |
| | | this.entity = entity; |
| | | } |
| | | |
| | | @Override |
| | | public void apply(final HsOfficePartnerDetailsPatchResource resource) { |
| | | if (resource != null) { |
| | | OptionalFromJson.of(resource.getRegistrationOffice()).ifPresent(entity::setRegistrationOffice); |
| | | OptionalFromJson.of(resource.getRegistrationNumber()).ifPresent(entity::setRegistrationNumber); |
| | | OptionalFromJson.of(resource.getBirthday()).ifPresent(entity::setBirthday); |
| | | OptionalFromJson.of(resource.getBirthName()).ifPresent(entity::setBirthName); |
| | | OptionalFromJson.of(resource.getDateOfDeath()).ifPresent(entity::setDateOfDeath); |
| | | } |
| | | } |
| | | } |
| | |
| | | import net.hostsharing.hsadminng.Stringifyable; |
| | | import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity; |
| | | import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity; |
| | | import org.hibernate.annotations.NotFound; |
| | | import org.hibernate.annotations.NotFoundAction; |
| | | |
| | | import javax.persistence.*; |
| | | import java.time.LocalDate; |
| | |
| | | private @Id UUID uuid; |
| | | |
| | | @ManyToOne |
| | | @JoinColumn(name = "personuuid") |
| | | @JoinColumn(name = "personuuid", nullable = false) |
| | | private HsOfficePersonEntity person; |
| | | |
| | | @ManyToOne |
| | | @JoinColumn(name = "contactuuid") |
| | | @JoinColumn(name = "contactuuid", nullable = false) |
| | | private HsOfficeContactEntity contact; |
| | | |
| | | private @Column(name = "registrationoffice") String registrationOffice; |
| | | private @Column(name = "registrationnumber") String registrationNumber; |
| | | private @Column(name = "birthname") String birthName; |
| | | private @Column(name = "birthday") LocalDate birthday; |
| | | private @Column(name = "dateofdeath") LocalDate dateOfDeath; |
| | | @ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.DETACH}, optional = true) |
| | | @JoinColumn(name = "detailsuuid", nullable = true) |
| | | @NotFound(action= NotFoundAction.IGNORE) |
| | | private HsOfficePartnerDetailsEntity details; |
| | | |
| | | @Override |
| | | public String toString() { |
| | |
| | | class HsOfficePartnerEntityPatcher implements EntityPatcher<HsOfficePartnerPatchResource> { |
| | | private final EntityManager em; |
| | | private final HsOfficePartnerEntity entity; |
| | | private final Function<UUID, Optional<HsOfficeContactEntity>> fetchContact; |
| | | private final Function<UUID, Optional<HsOfficePersonEntity>> fetchPerson; |
| | | |
| | | HsOfficePartnerEntityPatcher( |
| | | final EntityManager em, |
| | | final HsOfficePartnerEntity entity, |
| | | final Function<UUID, Optional<HsOfficeContactEntity>> fetchContact, |
| | | final Function<UUID, Optional<HsOfficePersonEntity>> fetchPerson) { |
| | | final HsOfficePartnerEntity entity) { |
| | | this.em = em; |
| | | this.entity = entity; |
| | | this.fetchContact = fetchContact; |
| | | this.fetchPerson = fetchPerson; |
| | | } |
| | | |
| | | @Override |
| | |
| | | verifyNotNull(newValue, "person"); |
| | | entity.setPerson(em.getReference(HsOfficePersonEntity.class, newValue)); |
| | | }); |
| | | OptionalFromJson.of(resource.getRegistrationOffice()).ifPresent(entity::setRegistrationOffice); |
| | | OptionalFromJson.of(resource.getRegistrationNumber()).ifPresent(entity::setRegistrationNumber); |
| | | OptionalFromJson.of(resource.getBirthday()).ifPresent(entity::setBirthday); |
| | | OptionalFromJson.of(resource.getBirthName()).ifPresent(entity::setBirthName); |
| | | OptionalFromJson.of(resource.getDateOfDeath()).ifPresent(entity::setDateOfDeath); |
| | | |
| | | new HsOfficePartnerDetailsEntityPatcher(em, entity.getDetails()).apply(resource.getDetails()); |
| | | } |
| | | |
| | | private void verifyNotNull(final UUID newValue, final String propertyName) { |
| | |
| | | JOIN HsOfficeContactEntity contact ON contact.uuid = partner.contact |
| | | JOIN HsOfficePersonEntity person ON person.uuid = partner.person |
| | | WHERE :name is null |
| | | OR partner.birthName like concat(:name, '%') |
| | | OR partner.details.birthName like concat(:name, '%') |
| | | OR contact.label like concat(:name, '%') |
| | | OR person.tradeName like concat(:name, '%') |
| | | OR person.givenName like concat(:name, '%') |
| | |
| | | paths: |
| | | /api/hs/office/partners/{partnerUUID}: |
| | | null: org.openapitools.jackson.nullable.JsonNullable |
| | | /api/hs/office/partners/{partnerUUID}/details: |
| | | null: org.openapitools.jackson.nullable.JsonNullable |
| | | /api/hs/office/contacts/{contactUUID}: |
| | | null: org.openapitools.jackson.nullable.JsonNullable |
| | | /api/hs/office/persons/{personUUID}: |
| | |
| | | $ref: './hs-office-person-schemas.yaml#/components/schemas/HsOfficePerson' |
| | | contact: |
| | | $ref: './hs-office-contact-schemas.yaml#/components/schemas/HsOfficeContact' |
| | | details: |
| | | $ref: '#/components/schemas/HsOfficePartnerDetails' |
| | | |
| | | HsOfficePartnerDetails: |
| | | type: object |
| | | properties: |
| | | uuid: |
| | | type: string |
| | | format: uuid |
| | | registrationOffice: |
| | | type: string |
| | | nullable: true |
| | | type: string |
| | | nullable: true |
| | | registrationNumber: |
| | | type: string |
| | | nullable: true |
| | | type: string |
| | | nullable: true |
| | | birthName: |
| | | type: string |
| | | nullable: true |
| | | type: string |
| | | nullable: true |
| | | birthday: |
| | | type: string |
| | | format: date |
| | | nullable: true |
| | | type: string |
| | | format: date |
| | | nullable: true |
| | | dateOfDeath: |
| | | type: string |
| | | format: date |
| | | nullable: true |
| | | type: string |
| | | format: date |
| | | nullable: true |
| | | |
| | | HsOfficePartnerPatch: |
| | | type: object |
| | |
| | | type: string |
| | | format: uuid |
| | | nullable: true |
| | | details: |
| | | $ref: '#/components/schemas/HsOfficePartnerDetailsPatch' |
| | | |
| | | HsOfficePartnerDetailsPatch: |
| | | type: object |
| | | nullable: true |
| | | properties: |
| | | registrationOffice: |
| | | type: string |
| | | nullable: true |
| | |
| | | contactUuid: |
| | | type: string |
| | | format: uuid |
| | | details: |
| | | $ref: '#/components/schemas/HsOfficePartnerDetailsInsert' |
| | | required: |
| | | - personUuid |
| | | - contactUuid |
| | | |
| | | HsOfficePartnerDetailsInsert: |
| | | type: object |
| | | properties: |
| | | registrationOffice: |
| | | type: string |
| | | nullable: true |
| | |
| | | type: string |
| | | format: date |
| | | nullable: true |
| | | required: |
| | | - personUuid |
| | | - contactUuid |
| | |
| | | or VALUE = 'view' |
| | | or VALUE = 'assume' |
| | | or VALUE ~ '^add-[a-z]+$' |
| | | or VALUE ~ '^new-[a-z]+$' |
| | | or VALUE ~ '^new-[a-z-]+$' |
| | | ); |
| | | |
| | | create table RbacPermission |
| | |
| | | and r.roleType = roleTypeToAssume |
| | | into roleUuidToAssume; |
| | | if roleUuidToAssume is null then |
| | | raise exception '[403] role % not accessible for user %', roleName, currentUser(); |
| | | raise exception '[403] role % not accessible for user %', roleName, currentSubjects(); |
| | | end if; |
| | | if not isGranted(currentUserUuid, roleUuidToAssume) then |
| | | raise exception '[403] user % has no permission to assume role %', currentUser(), roleName; |
| | |
| | | --liquibase formatted sql |
| | | |
| | | |
| | | -- ============================================================================ |
| | | --changeset hs-office-partner-MAIN-TABLE:1 endDelimiter:--// |
| | | --changeset hs-office-partner-DETAILS-TABLE:1 endDelimiter:--// |
| | | -- ---------------------------------------------------------------------------- |
| | | |
| | | create table if not exists hs_office_partner |
| | | create table hs_office_partner_details |
| | | ( |
| | | uuid uuid unique references RbacObject (uuid) initially deferred, |
| | | personUuid uuid not null references hs_office_person(uuid), |
| | | contactUuid uuid not null references hs_office_contact(uuid), |
| | | registrationOffice varchar(96), |
| | | registrationNumber varchar(96), |
| | | birthName varchar(96), |
| | |
| | | |
| | | |
| | | -- ============================================================================ |
| | | --changeset hs-office-partner-DETAILS-TABLE-JOURNAL:1 endDelimiter:--// |
| | | -- ---------------------------------------------------------------------------- |
| | | |
| | | call create_journal('hs_office_partner_details'); |
| | | --// |
| | | |
| | | -- ============================================================================ |
| | | --changeset hs-office-partner-MAIN-TABLE:1 endDelimiter:--// |
| | | -- ---------------------------------------------------------------------------- |
| | | |
| | | create table hs_office_partner |
| | | ( |
| | | uuid uuid unique references RbacObject (uuid) initially deferred, |
| | | personUuid uuid not null references hs_office_person(uuid), |
| | | contactUuid uuid not null references hs_office_contact(uuid), |
| | | detailsUuid uuid not null references hs_office_partner_details(uuid) on delete cascade |
| | | ); |
| | | --// |
| | | |
| | | |
| | | -- ============================================================================ |
| | | --changeset hs-office-partner-MAIN-TABLE-JOURNAL:1 endDelimiter:--// |
| | | -- ---------------------------------------------------------------------------- |
| | | |
| | |
| | | --> role:hsOfficePerson.guest[person.guest] |
| | | end |
| | | |
| | | subgraph hsOfficePartnerDetails |
| | | direction TB |
| | | |
| | | perm:hsOfficePartnerDetails.*{{partner.*}} |
| | | perm:hsOfficePartnerDetails.edit{{partner.edit}} |
| | | perm:hsOfficePartnerDetails.view{{partner.view}} |
| | | end |
| | | |
| | | subgraph hsOfficePartner |
| | | |
| | | 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 |
| | | |
| | | 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: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 |
| | |
| | | |
| | | if TG_OP = 'INSERT' then |
| | | |
| | | -- === ATTENTION: code generated from related Mermaid flowchart: === |
| | | |
| | | perform createRoleWithGrants( |
| | | hsOfficePartnerOwner(NEW), |
| | | permissions => array['*'], |
| | |
| | | hsOfficeContactGuest(newContact)] |
| | | ); |
| | | |
| | | |
| | | perform createRoleWithGrants( |
| | | hsOfficePartnerGuest(NEW), |
| | | permissions => array['view'], |
| | | incomingSuperRoles => array[ |
| | | hsOfficePartnerTenant(NEW)] |
| | | 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), 'fail'), |
| | | createPermissions(NEW.detailsUuid, array ['*']) |
| | | ); |
| | | |
| | | call grantPermissionsToRole( |
| | | getRoleId(hsOfficePartnerAdmin(NEW), 'fail'), |
| | | createPermissions(NEW.detailsUuid, array ['edit']) |
| | | ); |
| | | |
| | | 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), 'fail'), |
| | | createPermissions(NEW.detailsUuid, array ['view']) |
| | | ); |
| | | |
| | | |
| | | elsif TG_OP = 'UPDATE' then |
| | | |
| | |
| | | |
| | | 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; |
| | |
| | | '(select idName from hs_office_person_iv p where p.uuid = target.personUuid)', |
| | | $updates$ |
| | | personUuid = new.personUuid, |
| | | contactUuid = new.contactUuid, |
| | | registrationOffice = new.registrationOffice, |
| | | registrationNumber = new.registrationNumber, |
| | | birthday = new.birthday, |
| | | birthName = new.birthName, |
| | | dateOfDeath = new.dateOfDeath |
| | | contactUuid = new.contactUuid |
| | | $updates$); |
| | | --// |
| | | |
New file |
| | |
| | | --liquibase formatted sql |
| | | |
| | | -- ============================================================================ |
| | | --changeset hs-office-partner-details-rbac-OBJECT:1 endDelimiter:--// |
| | | -- ---------------------------------------------------------------------------- |
| | | call generateRelatedRbacObject('hs_office_partner_details'); |
| | | --// |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | -- ============================================================================ |
| | | --changeset hs-office-partner-details-rbac-IDENTITY-VIEW:1 endDelimiter:--// |
| | | -- ---------------------------------------------------------------------------- |
| | | call generateRbacIdentityView('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$); |
| | | --// |
| | | |
| | | |
| | | -- ============================================================================ |
| | | --changeset hs-office-partner-details-rbac-RESTRICTED-VIEW:1 endDelimiter:--// |
| | | -- ---------------------------------------------------------------------------- |
| | | call generateRbacRestrictedView('hs_office_partner_details', |
| | | 'target.uuid', -- no specific order required |
| | | $updates$ |
| | | registrationOffice = new.registrationOffice, |
| | | registrationNumber = new.registrationNumber, |
| | | birthName = new.birthName, |
| | | birthday = new.birthday, |
| | | dateOfDeath = new.dateOfDeath |
| | | $updates$); |
| | | --// |
| | | |
| | | |
| | | -- ============================================================================ |
| | | --changeset hs-office-partner-details-rbac-NEW-CONTACT: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(); |
| | | --// |
| | | |
| | |
| | | create or replace procedure createHsOfficePartnerTestData( personTradeOrFamilyName varchar, contactLabel varchar ) |
| | | language plpgsql as $$ |
| | | declare |
| | | currentTask varchar; |
| | | idName varchar; |
| | | relatedPerson hs_office_person; |
| | | relatedContact hs_office_contact; |
| | | birthday date; |
| | | currentTask varchar; |
| | | idName varchar; |
| | | relatedPerson hs_office_person; |
| | | relatedContact hs_office_contact; |
| | | relatedDetailsUuid uuid; |
| | | birthday date; |
| | | begin |
| | | idName := cleanIdentifier( personTradeOrFamilyName|| '-' || contactLabel); |
| | | currentTask := 'creating partner test-data ' || idName; |
| | |
| | | raise notice 'creating test partner: %', idName; |
| | | raise notice '- using person (%): %', relatedPerson.uuid, relatedPerson; |
| | | raise notice '- using contact (%): %', relatedContact.uuid, relatedContact; |
| | | |
| | | if relatedPerson.persontype = 'NATURAL' then |
| | | insert |
| | | into hs_office_partner_details (uuid, birthName, birthday) |
| | | values (uuid_generate_v4(), 'Meyer', '1987-10-31') |
| | | returning uuid into relatedDetailsUuid; |
| | | else |
| | | insert |
| | | into hs_office_partner_details (uuid, registrationOffice, registrationNumber) |
| | | values (uuid_generate_v4(), 'Hamburg', '12345') |
| | | returning uuid into relatedDetailsUuid; |
| | | end if; |
| | | |
| | | insert |
| | | into hs_office_partner (uuid, personuuid, contactuuid, birthday) |
| | | values (uuid_generate_v4(), relatedPerson.uuid, relatedContact.uuid, birthDay); |
| | | into hs_office_partner (uuid, personuuid, contactuuid, detailsUuid) |
| | | values (uuid_generate_v4(), relatedPerson.uuid, relatedContact.uuid, relatedDetailsUuid); |
| | | end; $$; |
| | | --// |
| | | |
| | | /* |
| | | Creates a range of test partner for mass data generation. |
| | | */ |
| | | create or replace procedure createHsOfficePartnerTestData( |
| | | startCount integer, -- count of auto generated rows before the run |
| | | endCount integer -- count of auto generated rows after the run |
| | | ) |
| | | language plpgsql as $$ |
| | | declare |
| | | person hs_office_person; |
| | | contact hs_office_contact; |
| | | begin |
| | | for t in startCount..endCount |
| | | loop |
| | | select p.* from hs_office_person p where tradeName = intToVarChar(t, 4) into person; |
| | | select c.* from hs_office_contact c where c.label = intToVarChar(t, 4) || '#' || t into contact; |
| | | |
| | | call createHsOfficePartnerTestData(person.uuid, contact.uuid); |
| | | commit; |
| | | end loop; |
| | | end; $$; |
| | | --// |
| | | |
| | | |
| | | -- ============================================================================ |
| | |
| | | - include: |
| | | file: db/changelog/223-hs-office-partner-rbac.sql |
| | | - include: |
| | | file: db/changelog/224-hs-office-partner-details-rbac.sql |
| | | - include: |
| | | file: db/changelog/228-hs-office-partner-test-data.sql |
| | | - include: |
| | | file: db/changelog/230-hs-office-relationship.sql |
| | |
| | | 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 org.junit.jupiter.api.Test; |
| | |
| | | .person(HsOfficePersonEntity.builder() |
| | | .tradeName("some trade name") |
| | | .build()) |
| | | .birthName("some birth name") |
| | | .details(HsOfficePartnerDetailsEntity.builder().birthName("some birth name").build()) |
| | | |
| | | .build()) |
| | | .billingContact(HsOfficeContactEntity.builder().label("some label").build()) |
| | | .build(); |
| | |
| | | class CreateDebitor { |
| | | |
| | | @Test |
| | | public void testHostsharingAdmin_withoutAssumedRole_canCreateNewDebitor() { |
| | | public void globalAdmin_canCreateNewDebitor() { |
| | | // given |
| | | context("superuser-alex@hostsharing.net"); |
| | | final var count = debitorRepo.count(); |
| | |
| | | class FindByOptionalName { |
| | | |
| | | @Test |
| | | public void globalAdmin_withoutAssumedRole_canViewAllDebitors() { |
| | | public void globalAdmin_canViewAllDebitors() { |
| | | // given |
| | | context("superuser-alex@hostsharing.net"); |
| | | |
| | |
| | | class FindByDebitorNumberLike { |
| | | |
| | | @Test |
| | | public void globalAdmin_withoutAssumedRole_canViewAllDebitors() { |
| | | public void globalAdmin_canViewAllDebitors() { |
| | | // given |
| | | context("superuser-alex@hostsharing.net"); |
| | | |
| | |
| | | class FindByNameLike { |
| | | |
| | | @Test |
| | | public void globalAdmin_withoutAssumedRole_canViewAllDebitors() { |
| | | public void globalAdmin_canViewAllDebitors() { |
| | | // given |
| | | context("superuser-alex@hostsharing.net"); |
| | | |
| | |
| | | class UpdateDebitor { |
| | | |
| | | @Test |
| | | public void hostsharingAdmin_withoutAssumedRole_canUpdateArbitraryDebitor() { |
| | | public void globalAdmin_canUpdateArbitraryDebitor() { |
| | | // given |
| | | context("superuser-alex@hostsharing.net"); |
| | | final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "fifth contact"); |
| | |
| | | |
| | | private void assertThatDebitorActuallyInDatabase(final HsOfficeDebitorEntity saved) { |
| | | final var found = debitorRepo.findByUuid(saved.getUuid()); |
| | | assertThat(found).isNotEmpty().get().isNotSameAs(saved).usingRecursiveComparison().isEqualTo(saved); |
| | | assertThat(found).isNotEmpty().get().isNotSameAs(saved) |
| | | .extracting(Object::toString).isEqualTo(saved.toString()); |
| | | } |
| | | |
| | | private void assertThatDebitorIsVisibleForUserWithRole( |
| | |
| | | class DeleteByUuid { |
| | | |
| | | @Test |
| | | public void globalAdmin_withoutAssumedRole_canDeleteAnyDebitor() { |
| | | public void globalAdmin_canDeleteAnyDebitor() { |
| | | // given |
| | | context("superuser-alex@hostsharing.net", null); |
| | | final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "tenth"); |
| | |
| | | |
| | | private void assertThatMembershipExistsAndIsAccessibleToCurrentContext(final HsOfficeMembershipEntity saved) { |
| | | final var found = membershipRepo.findByUuid(saved.getUuid()); |
| | | assertThat(found).isNotEmpty().get().isNotSameAs(saved).usingRecursiveComparison().isEqualTo(saved); |
| | | assertThat(found).isNotEmpty().get().isNotSameAs(saved) |
| | | .extracting(Object::toString).isEqualTo(saved.toString()); |
| | | } |
| | | |
| | | private void assertThatMembershipIsVisibleForUserWithRole( |
| | |
| | | { |
| | | "person": { "familyName": "Smith" }, |
| | | "contact": { "label": "fifth contact" }, |
| | | "birthday": "1987-10-31" |
| | | "details": { "birthday": "1987-10-31" } |
| | | }, |
| | | { |
| | | "person": { "tradeName": "First GmbH" }, |
| | | "contact": { "label": "first contact" } |
| | | "contact": { "label": "first contact" }, |
| | | "details": { "registrationOffice": "Hamburg" } |
| | | }, |
| | | { |
| | | "person": { "tradeName": "Third OHG" }, |
| | | "contact": { "label": "third contact" } |
| | | "contact": { "label": "third contact" }, |
| | | "details": { "registrationOffice": "Hamburg" } |
| | | }, |
| | | { |
| | | "person": { "tradeName": "Second e.K." }, |
| | | "contact": { "label": "second contact" } |
| | | "contact": { "label": "second contact" }, |
| | | "details": { "registrationOffice": "Hamburg" } |
| | | }, |
| | | { |
| | | "person": { "personType": "SOLE_REPRESENTATION" }, |
| | | "contact": { "label": "forth contact" } |
| | | "contact": { "label": "forth contact" }, |
| | | "details": { "registrationOffice": "Hamburg" } |
| | | } |
| | | ] |
| | | """)); |
| | |
| | | { |
| | | "contactUuid": "%s", |
| | | "personUuid": "%s", |
| | | "registrationOffice": "Registergericht Hamburg", |
| | | "registrationNumber": "123456" |
| | | "details": { |
| | | "registrationOffice": "Registergericht Aurich", |
| | | "registrationNumber": "123456" |
| | | } |
| | | } |
| | | """.formatted(givenContact.getUuid(), givenPerson.getUuid())) |
| | | .port(port) |
| | |
| | | .statusCode(201) |
| | | .contentType(ContentType.JSON) |
| | | .body("uuid", isUuidValid()) |
| | | .body("registrationNumber", is("123456")) |
| | | .body("details.registrationOffice", is("Registergericht Aurich")) |
| | | .body("details.registrationNumber", is("123456")) |
| | | .body("contact.label", is(givenContact.getLabel())) |
| | | .body("person.tradeName", is(givenPerson.getTradeName())) |
| | | .header("Location", startsWith("http://localhost")) |
| | |
| | | { |
| | | "contactUuid": "%s", |
| | | "personUuid": "%s", |
| | | "registrationOffice": "Registergericht Hamburg", |
| | | "registrationNumber": "222222", |
| | | "birthName": "Maja Schmidt", |
| | | "birthday": "1938-04-08", |
| | | "dateOfDeath": "2022-01-12" |
| | | "details": { |
| | | "registrationOffice": "Registergericht Hamburg", |
| | | "registrationNumber": "222222", |
| | | "birthName": "Maja Schmidt", |
| | | "birthday": "1938-04-08", |
| | | "dateOfDeath": "2022-01-12" |
| | | } |
| | | } |
| | | """.formatted(givenContact.getUuid(), givenPerson.getUuid())) |
| | | .port(port) |
| | |
| | | .statusCode(200) |
| | | .contentType(ContentType.JSON) |
| | | .body("uuid", isUuidValid()) |
| | | .body("registrationNumber", is("222222")) |
| | | .body("details.registrationNumber", is("222222")) |
| | | .body("contact.label", is(givenContact.getLabel())) |
| | | .body("person.tradeName", is(givenPerson.getTradeName())); |
| | | // @formatter:on |
| | |
| | | .matches(person -> { |
| | | assertThat(person.getPerson().getTradeName()).isEqualTo("Third OHG"); |
| | | assertThat(person.getContact().getLabel()).isEqualTo("forth contact"); |
| | | assertThat(person.getRegistrationOffice()).isEqualTo("Registergericht Hamburg"); |
| | | assertThat(person.getRegistrationNumber()).isEqualTo("222222"); |
| | | assertThat(person.getBirthName()).isEqualTo("Maja Schmidt"); |
| | | assertThat(person.getBirthday()).isEqualTo("1938-04-08"); |
| | | assertThat(person.getDateOfDeath()).isEqualTo("2022-01-12"); |
| | | assertThat(person.getDetails().getRegistrationOffice()).isEqualTo("Registergericht Hamburg"); |
| | | assertThat(person.getDetails().getRegistrationNumber()).isEqualTo("222222"); |
| | | assertThat(person.getDetails().getBirthName()).isEqualTo("Maja Schmidt"); |
| | | assertThat(person.getDetails().getBirthday()).isEqualTo("1938-04-08"); |
| | | assertThat(person.getDetails().getDateOfDeath()).isEqualTo("2022-01-12"); |
| | | return true; |
| | | }); |
| | | } |
| | |
| | | .contentType(ContentType.JSON) |
| | | .body(""" |
| | | { |
| | | "birthName": "Maja Schmidt", |
| | | "birthday": "1938-04-08", |
| | | "dateOfDeath": "2022-01-12" |
| | | "details": { |
| | | "birthName": "Maja Schmidt", |
| | | "birthday": "1938-04-08", |
| | | "dateOfDeath": "2022-01-12" |
| | | } |
| | | } |
| | | """) |
| | | .port(port) |
| | |
| | | .statusCode(200) |
| | | .contentType(ContentType.JSON) |
| | | .body("uuid", isUuidValid()) |
| | | .body("birthName", is("Maja Schmidt")) |
| | | .body("details.birthName", is("Maja Schmidt")) |
| | | .body("contact.label", is(givenPartner.getContact().getLabel())) |
| | | .body("person.tradeName", is(givenPartner.getPerson().getTradeName())); |
| | | // @formatter:on |
| | |
| | | .matches(person -> { |
| | | assertThat(person.getPerson().getTradeName()).isEqualTo(givenPartner.getPerson().getTradeName()); |
| | | assertThat(person.getContact().getLabel()).isEqualTo(givenPartner.getContact().getLabel()); |
| | | assertThat(person.getRegistrationOffice()).isEqualTo(null); |
| | | assertThat(person.getRegistrationNumber()).isEqualTo(null); |
| | | assertThat(person.getBirthName()).isEqualTo("Maja Schmidt"); |
| | | assertThat(person.getBirthday()).isEqualTo("1938-04-08"); |
| | | assertThat(person.getDateOfDeath()).isEqualTo("2022-01-12"); |
| | | assertThat(person.getDetails().getRegistrationOffice()).isEqualTo(null); |
| | | assertThat(person.getDetails().getRegistrationNumber()).isEqualTo(null); |
| | | assertThat(person.getDetails().getBirthName()).isEqualTo("Maja Schmidt"); |
| | | assertThat(person.getDetails().getBirthday()).isEqualTo("1938-04-08"); |
| | | assertThat(person.getDetails().getDateOfDeath()).isEqualTo("2022-01-12"); |
| | | return true; |
| | | }); |
| | | } |
| | |
| | | .uuid(UUID.randomUUID()) |
| | | .person(givenPerson) |
| | | .contact(givenContact) |
| | | .details(HsOfficePartnerDetailsEntity.builder() |
| | | .uuid((UUID.randomUUID())) |
| | | .build()) |
| | | .build(); |
| | | |
| | | toCleanup(newPartner.getUuid()); |
New file |
| | |
| | | package net.hostsharing.hsadminng.hs.office.partner; |
| | | |
| | | import net.hostsharing.hsadminng.PatchUnitTestBase; |
| | | import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity; |
| | | import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficePartnerDetailsPatchResource; |
| | | import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity; |
| | | import org.junit.jupiter.api.BeforeEach; |
| | | import org.junit.jupiter.api.TestInstance; |
| | | import org.junit.jupiter.api.extension.ExtendWith; |
| | | import org.mockito.Mock; |
| | | import org.mockito.junit.jupiter.MockitoExtension; |
| | | |
| | | import javax.persistence.EntityManager; |
| | | import java.time.LocalDate; |
| | | import java.util.UUID; |
| | | import java.util.stream.Stream; |
| | | |
| | | import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS; |
| | | import static org.mockito.ArgumentMatchers.any; |
| | | import static org.mockito.ArgumentMatchers.eq; |
| | | import static org.mockito.Mockito.lenient; |
| | | |
| | | @TestInstance(PER_CLASS) |
| | | @ExtendWith(MockitoExtension.class) |
| | | class HsOfficePartnerDetailsEntityPatcherUnitTest extends PatchUnitTestBase< |
| | | HsOfficePartnerDetailsPatchResource, |
| | | HsOfficePartnerDetailsEntity |
| | | > { |
| | | |
| | | private static final UUID INITIAL_PARTNER_UUID = UUID.randomUUID(); |
| | | private static final UUID INITIAL_CONTACT_UUID = UUID.randomUUID(); |
| | | private static final UUID INITIAL_PERSON_UUID = UUID.randomUUID(); |
| | | |
| | | private static final LocalDate INITIAL_BIRTHDAY = LocalDate.parse("1900-01-01"); |
| | | private static final LocalDate PATCHED_BIRTHDAY = LocalDate.parse("1990-12-31"); |
| | | |
| | | private static final LocalDate INITIAL_DAY_OF_DEATH = LocalDate.parse("2000-01-01"); |
| | | private static final LocalDate PATCHED_DATE_OF_DEATH = LocalDate.parse("2022-08-31"); |
| | | |
| | | @Mock |
| | | private EntityManager em; |
| | | |
| | | @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(HsOfficePersonEntity.class), any())).thenAnswer(invocation -> |
| | | HsOfficePersonEntity.builder().uuid(invocation.getArgument(1)).build()); |
| | | } |
| | | |
| | | @Override |
| | | protected HsOfficePartnerDetailsEntity newInitialEntity() { |
| | | final var entity = new HsOfficePartnerDetailsEntity(); |
| | | entity.setUuid(INITIAL_PARTNER_UUID); |
| | | entity.setRegistrationOffice("initial Reg-Office"); |
| | | entity.setRegistrationNumber("initial Reg-Number"); |
| | | entity.setBirthday(INITIAL_BIRTHDAY); |
| | | entity.setBirthName("initial birth name"); |
| | | entity.setDateOfDeath(INITIAL_DAY_OF_DEATH); |
| | | return entity; |
| | | } |
| | | |
| | | @Override |
| | | protected HsOfficePartnerDetailsPatchResource newPatchResource() { |
| | | return new HsOfficePartnerDetailsPatchResource(); |
| | | } |
| | | |
| | | @Override |
| | | protected HsOfficePartnerDetailsEntityPatcher createPatcher(final HsOfficePartnerDetailsEntity details) { |
| | | return new HsOfficePartnerDetailsEntityPatcher(em, details); |
| | | } |
| | | |
| | | @Override |
| | | protected Stream<Property> propertyTestDescriptors() { |
| | | return Stream.of( |
| | | new JsonNullableProperty<>( |
| | | "registrationOffice", |
| | | HsOfficePartnerDetailsPatchResource::setRegistrationOffice, |
| | | "patched Reg-Office", |
| | | HsOfficePartnerDetailsEntity::setRegistrationOffice), |
| | | new JsonNullableProperty<>( |
| | | "birthday", |
| | | HsOfficePartnerDetailsPatchResource::setBirthday, |
| | | PATCHED_BIRTHDAY, |
| | | HsOfficePartnerDetailsEntity::setBirthday), |
| | | new JsonNullableProperty<>( |
| | | "dayOfDeath", |
| | | HsOfficePartnerDetailsPatchResource::setDateOfDeath, |
| | | PATCHED_DATE_OF_DEATH, |
| | | HsOfficePartnerDetailsEntity::setDateOfDeath) |
| | | ); |
| | | } |
| | | } |
New file |
| | |
| | | package net.hostsharing.hsadminng.hs.office.partner; |
| | | |
| | | import org.junit.jupiter.api.Test; |
| | | |
| | | import java.time.LocalDate; |
| | | |
| | | import static org.assertj.core.api.Assertions.assertThat; |
| | | |
| | | class HsOfficePartnerDetailsEntityTest { |
| | | |
| | | final HsOfficePartnerDetailsEntity given = HsOfficePartnerDetailsEntity.builder() |
| | | .registrationOffice("Hamburg") |
| | | .registrationNumber("12345") |
| | | .birthday(LocalDate.parse("2002-01-15")) |
| | | .birthName("Melly Miller") |
| | | .dateOfDeath(LocalDate.parse("2081-12-21")) |
| | | .build(); |
| | | |
| | | @Test |
| | | void toStringContainsAllProperties() { |
| | | |
| | | final var result = given.toString(); |
| | | |
| | | assertThat(result).isEqualTo("partnerDetails(Hamburg, 12345, 2002-01-15, 2002-01-15, 2081-12-21)"); |
| | | } |
| | | |
| | | @Test |
| | | void toShortStringContainsFirstNonNullValue() { |
| | | |
| | | assertThat(given.toShortString()).isEqualTo("12345"); |
| | | |
| | | assertThat(HsOfficePartnerDetailsEntity.builder() |
| | | .birthName("Melly Miller") |
| | | .birthday(LocalDate.parse("2002-01-15")) |
| | | .dateOfDeath(LocalDate.parse("2081-12-21")) |
| | | .build().toShortString()).isEqualTo("Melly Miller"); |
| | | |
| | | assertThat(HsOfficePartnerDetailsEntity.builder() |
| | | .birthday(LocalDate.parse("2002-01-15")) |
| | | .dateOfDeath(LocalDate.parse("2081-12-21")) |
| | | .build().toShortString()).isEqualTo("2002-01-15"); |
| | | |
| | | assertThat(HsOfficePartnerDetailsEntity.builder() |
| | | .dateOfDeath(LocalDate.parse("2081-12-21")) |
| | | .build().toShortString()).isEqualTo("2081-12-21"); |
| | | |
| | | |
| | | assertThat(HsOfficePartnerDetailsEntity.builder() |
| | | .build().toShortString()).isEqualTo("<empty details>"); |
| | | } |
| | | } |
| | |
| | | package net.hostsharing.hsadminng.hs.office.partner; |
| | | |
| | | import net.hostsharing.hsadminng.PatchUnitTestBase; |
| | | 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.PatchUnitTestBase; |
| | | import org.junit.jupiter.api.BeforeEach; |
| | | import org.junit.jupiter.api.TestInstance; |
| | | import org.junit.jupiter.api.extension.ExtendWith; |
| | |
| | | import org.mockito.junit.jupiter.MockitoExtension; |
| | | |
| | | import javax.persistence.EntityManager; |
| | | import java.time.LocalDate; |
| | | import java.util.Optional; |
| | | import java.util.UUID; |
| | | import java.util.stream.Stream; |
| | | |
| | | import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS; |
| | | import static org.mockito.ArgumentMatchers.any; |
| | | import static org.mockito.ArgumentMatchers.eq; |
| | | import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS; |
| | | import static org.mockito.Mockito.lenient; |
| | | |
| | | @TestInstance(PER_CLASS) |
| | |
| | | private static final UUID INITIAL_PARTNER_UUID = UUID.randomUUID(); |
| | | private static final UUID INITIAL_CONTACT_UUID = UUID.randomUUID(); |
| | | private static final UUID INITIAL_PERSON_UUID = UUID.randomUUID(); |
| | | private static final UUID INITIAL_DETAILS_UUID = UUID.randomUUID(); |
| | | private static final UUID PATCHED_CONTACT_UUID = UUID.randomUUID(); |
| | | private static final UUID PATCHED_PERSON_UUID = UUID.randomUUID(); |
| | | |
| | | private static final LocalDate INITIAL_BIRTHDAY = LocalDate.parse("1900-01-01"); |
| | | private static final LocalDate PATCHED_BIRTHDAY = LocalDate.parse("1990-12-31"); |
| | | |
| | | private static final LocalDate INITIAL_DAY_OF_DEATH = LocalDate.parse("2000-01-01"); |
| | | private static final LocalDate PATCHED_DATE_OF_DEATH = LocalDate.parse("2022-08-31"); |
| | | |
| | | private final HsOfficePersonEntity givenInitialPerson = HsOfficePersonEntity.builder() |
| | | .uuid(INITIAL_PERSON_UUID) |
| | |
| | | private final HsOfficeContactEntity givenInitialContact = HsOfficeContactEntity.builder() |
| | | .uuid(INITIAL_CONTACT_UUID) |
| | | .build(); |
| | | |
| | | private final HsOfficePartnerDetailsEntity givenInitialDetails = HsOfficePartnerDetailsEntity.builder() |
| | | .uuid(INITIAL_DETAILS_UUID) |
| | | .build(); |
| | | @Mock |
| | | private EntityManager em; |
| | | |
| | | @BeforeEach |
| | | void initMocks() { |
| | | lenient().when(em.getReference(eq(HsOfficeContactEntity.class), any())).thenAnswer(invocation -> |
| | | HsOfficeContactEntity.builder().uuid(invocation.getArgument(1)).build()); |
| | | HsOfficeContactEntity.builder().uuid(invocation.getArgument(1)).build()); |
| | | lenient().when(em.getReference(eq(HsOfficePersonEntity.class), any())).thenAnswer(invocation -> |
| | | HsOfficePersonEntity.builder().uuid(invocation.getArgument(1)).build()); |
| | | } |
| | |
| | | entity.setUuid(INITIAL_PARTNER_UUID); |
| | | entity.setPerson(givenInitialPerson); |
| | | entity.setContact(givenInitialContact); |
| | | entity.setRegistrationOffice("initial Reg-Office"); |
| | | entity.setRegistrationNumber("initial Reg-Number"); |
| | | entity.setBirthday(INITIAL_BIRTHDAY); |
| | | entity.setBirthName("initial birth name"); |
| | | entity.setDateOfDeath(INITIAL_DAY_OF_DEATH); |
| | | entity.setDetails(givenInitialDetails); |
| | | return entity; |
| | | } |
| | | |
| | |
| | | |
| | | @Override |
| | | protected HsOfficePartnerEntityPatcher createPatcher(final HsOfficePartnerEntity partner) { |
| | | return new HsOfficePartnerEntityPatcher( |
| | | em, |
| | | partner, |
| | | uuid -> uuid == PATCHED_CONTACT_UUID |
| | | ? Optional.of(newContact(uuid)) |
| | | : Optional.empty(), |
| | | uuid -> uuid == PATCHED_PERSON_UUID |
| | | ? Optional.of(newPerson(uuid)) |
| | | : Optional.empty()); |
| | | return new HsOfficePartnerEntityPatcher(em, partner); |
| | | } |
| | | |
| | | @Override |
| | |
| | | PATCHED_PERSON_UUID, |
| | | HsOfficePartnerEntity::setPerson, |
| | | newPerson(PATCHED_PERSON_UUID)) |
| | | .notNullable(), |
| | | new JsonNullableProperty<>( |
| | | "registrationOffice", |
| | | HsOfficePartnerPatchResource::setRegistrationOffice, |
| | | "patched Reg-Office", |
| | | HsOfficePartnerEntity::setRegistrationOffice), |
| | | new JsonNullableProperty<>( |
| | | "birthday", |
| | | HsOfficePartnerPatchResource::setBirthday, |
| | | PATCHED_BIRTHDAY, |
| | | HsOfficePartnerEntity::setBirthday), |
| | | new JsonNullableProperty<>( |
| | | "dayOfDeath", |
| | | HsOfficePartnerPatchResource::setDateOfDeath, |
| | | PATCHED_DATE_OF_DEATH, |
| | | HsOfficePartnerEntity::setDateOfDeath) |
| | | .notNullable() |
| | | ); |
| | | } |
| | | |
| | |
| | | |
| | | import javax.persistence.EntityManager; |
| | | import javax.servlet.http.HttpServletRequest; |
| | | import java.time.LocalDate; |
| | | import java.util.*; |
| | | |
| | | import static net.hostsharing.hsadminng.rbac.rbacgrant.RawRbacGrantEntity.grantDisplaysOf; |
| | |
| | | .uuid(UUID.randomUUID()) |
| | | .person(givenPerson) |
| | | .contact(givenContact) |
| | | .details(HsOfficePartnerDetailsEntity.builder() |
| | | .uuid(UUID.randomUUID()) |
| | | .build()) |
| | | .build()); |
| | | return partnerRepo.save(newPartner); |
| | | }); |
| | |
| | | .uuid(UUID.randomUUID()) |
| | | .person(givenPerson) |
| | | .contact(givenContact) |
| | | .details(HsOfficePartnerDetailsEntity.builder().uuid(UUID.randomUUID()).build()) |
| | | .build()); |
| | | return partnerRepo.save(newPartner); |
| | | }); |
| | |
| | | .containsExactlyInAnyOrder(Array.fromFormatted( |
| | | initialGrantNames, |
| | | // owner |
| | | "{ grant perm * on partner#EBess-4th to role partner#EBess-4th.owner by system and assume }", |
| | | "{ grant role partner#EBess-4th.owner to role global#global.admin by system and assume }", |
| | | "{ grant perm * on partner#EBess-4th to role partner#EBess-4th.owner by system and assume }", |
| | | "{ grant perm * on partner_details#EBess-4th-details to role partner#EBess-4th.owner by system and assume }", |
| | | "{ grant role partner#EBess-4th.owner to role global#global.admin by system and assume }", |
| | | |
| | | // admin |
| | | "{ grant perm edit on partner#EBess-4th to role partner#EBess-4th.admin by system and assume }", |
| | | "{ grant role partner#EBess-4th.admin to role partner#EBess-4th.owner by system and assume }", |
| | | "{ grant role person#EBess.tenant to role partner#EBess-4th.admin by system and assume }", |
| | | "{ grant role contact#4th.tenant to role partner#EBess-4th.admin by system and assume }", |
| | | "{ grant perm edit on partner#EBess-4th to role partner#EBess-4th.admin by system and assume }", |
| | | "{ grant perm edit on partner_details#EBess-4th-details to role partner#EBess-4th.admin by system and assume }", |
| | | "{ grant role partner#EBess-4th.admin to role partner#EBess-4th.owner by system and assume }", |
| | | "{ grant role person#EBess.tenant to role partner#EBess-4th.admin by system and assume }", |
| | | "{ grant role contact#4th.tenant to role partner#EBess-4th.admin by system and assume }", |
| | | |
| | | // agent |
| | | "{ grant role partner#EBess-4th.agent to role partner#EBess-4th.admin by system and assume }", |
| | | "{ grant role partner#EBess-4th.agent to role person#EBess.admin by system and assume }", |
| | | "{ grant role partner#EBess-4th.agent to role contact#4th.admin by system and assume }", |
| | | "{ grant perm view on partner_details#EBess-4th-details to role partner#EBess-4th.agent by system and assume }", |
| | | "{ grant role partner#EBess-4th.agent to role partner#EBess-4th.admin by system and assume }", |
| | | "{ grant role partner#EBess-4th.agent to role person#EBess.admin by system and assume }", |
| | | "{ grant role partner#EBess-4th.agent to role contact#4th.admin by system and assume }", |
| | | |
| | | // tenant |
| | | "{ grant role partner#EBess-4th.tenant to role partner#EBess-4th.agent by system and assume }", |
| | | "{ grant role person#EBess.guest to role partner#EBess-4th.tenant by system and assume }", |
| | | "{ grant role contact#4th.guest to role partner#EBess-4th.tenant by system and assume }", |
| | | "{ grant role partner#EBess-4th.tenant to role partner#EBess-4th.agent by system and assume }", |
| | | "{ grant role person#EBess.guest to role partner#EBess-4th.tenant by system and assume }", |
| | | "{ grant role contact#4th.guest to role partner#EBess-4th.tenant by system and assume }", |
| | | |
| | | // guest |
| | | "{ grant perm view on partner#EBess-4th to role partner#EBess-4th.guest by system and assume }", |
| | | "{ grant role partner#EBess-4th.guest to role partner#EBess-4th.tenant by system and assume }", |
| | | "{ grant perm view on partner#EBess-4th to role partner#EBess-4th.guest by system and assume }", |
| | | "{ grant role partner#EBess-4th.guest to role partner#EBess-4th.tenant by system and assume }", |
| | | |
| | | null)); |
| | | } |
| | | |
| | |
| | | context("superuser-alex@hostsharing.net"); |
| | | givenPartner.setContact(givenNewContact); |
| | | givenPartner.setPerson(givenNewPerson); |
| | | givenPartner.setDateOfDeath(LocalDate.parse("2022-09-15")); |
| | | return toCleanup(partnerRepo.save(givenPartner)); |
| | | }); |
| | | |
| | |
| | | } |
| | | |
| | | @Test |
| | | public void personAdmin_canNotUpdateRelatedPartner() { |
| | | // given |
| | | context("superuser-alex@hostsharing.net"); |
| | | final var givenPartner = givenSomeTemporaryPartnerBessler("eighth"); |
| | | assertThatPartnerIsVisibleForUserWithRole( |
| | | givenPartner, |
| | | "hs_office_person#ErbenBesslerMelBessler.admin"); |
| | | assertThatPartnerActuallyInDatabase(givenPartner); |
| | | |
| | | // when |
| | | final var result = jpaAttempt.transacted(() -> { |
| | | context("superuser-alex@hostsharing.net", "hs_office_person#ErbenBesslerMelBessler.admin"); |
| | | givenPartner.setDateOfDeath(LocalDate.parse("2022-09-15")); |
| | | return partnerRepo.save(givenPartner); |
| | | }); |
| | | |
| | | // then |
| | | result.assertExceptionWithRootCauseMessage(JpaSystemException.class, |
| | | "[403] Subject ", " is not allowed to update hs_office_partner uuid"); |
| | | } |
| | | |
| | | @Test |
| | | public void contactAdmin_canNotUpdateRelatedPartner() { |
| | | public void partnerAgent_canNotUpdateRelatedPartner() { |
| | | // given |
| | | context("superuser-alex@hostsharing.net"); |
| | | final var givenPartner = givenSomeTemporaryPartnerBessler("ninth"); |
| | | assertThatPartnerIsVisibleForUserWithRole( |
| | | givenPartner, |
| | | "hs_office_contact#ninthcontact.admin"); |
| | | "hs_office_partner#ErbenBesslerMelBessler-ninthcontact.agent"); |
| | | assertThatPartnerActuallyInDatabase(givenPartner); |
| | | final var givenNewContact = contactRepo.findContactByOptionalLabelLike("tenth").get(0); |
| | | |
| | | // when |
| | | final var result = jpaAttempt.transacted(() -> { |
| | | context("superuser-alex@hostsharing.net", "hs_office_contact#ninthcontact.admin"); |
| | | givenPartner.setDateOfDeath(LocalDate.parse("2022-09-15")); |
| | | context("superuser-alex@hostsharing.net", "hs_office_partner#ErbenBesslerMelBessler-ninthcontact.agent"); |
| | | givenPartner.getDetails().setBirthName("new birthname"); |
| | | return partnerRepo.save(givenPartner); |
| | | }); |
| | | |
| | | // then |
| | | result.assertExceptionWithRootCauseMessage(JpaSystemException.class, |
| | | "[403] Subject ", " is not allowed to update hs_office_partner uuid"); |
| | | "[403] Subject ", " is not allowed to update hs_office_partner_details uuid"); |
| | | } |
| | | |
| | | private void assertThatPartnerActuallyInDatabase(final HsOfficePartnerEntity saved) { |
| | |
| | | .uuid(UUID.randomUUID()) |
| | | .person(givenPerson) |
| | | .contact(givenContact) |
| | | .details(HsOfficePartnerDetailsEntity.builder() |
| | | .uuid(UUID.randomUUID()) |
| | | .build()) |
| | | .build(); |
| | | |
| | | toCleanup(newPartner); |
| | |
| | | |
| | | private void assertThatSepaMandateActuallyInDatabase(final HsOfficeSepaMandateEntity saved) { |
| | | final var found = sepaMandateRepo.findByUuid(saved.getUuid()); |
| | | assertThat(found).isNotEmpty().get().isNotSameAs(saved).usingRecursiveComparison().isEqualTo(saved); |
| | | assertThat(found).isNotEmpty().get().isNotSameAs(saved) |
| | | .extracting(Object::toString).isEqualTo(saved.toString()); |
| | | } |
| | | |
| | | private void assertThatSepaMandateIsVisibleForUserWithRole( |